diff --git a/.gitignore b/.gitignore index 1b078ed632..a001e38de8 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ /build_vc2015-32 /build_vc2015-64 /build +/llvm +/src/r_drawersasm.obj +/src/r_drawersasm.o diff --git a/CMakeLists.txt b/CMakeLists.txt index eae0702f88..e96249ff31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,20 +174,24 @@ if( MSVC ) # Disable run-time type information set( ALL_C_FLAGS "/GF /Gy /GR-" ) - if( CMAKE_SIZEOF_VOID_P MATCHES "4") - # SSE2 option (to allow x87 in 32 bit and disallow extended feature sets which have not yet been checked for precision) - option (ZDOOM_USE_SSE2 "Use SSE2 instruction set") - if (ZDOOM_USE_SSE2) - set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") - else () - if (MSVC_VERSION GREATER 1699) - # On Visual C++ 2012 and later SSE2 is the default, so we need to switch it off explicitly - set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:IA32") - endif () - endif () - else() - set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") - endif() + # Use SSE 2 as minimum always as the true color drawers needs it for __vectorcall + #set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") # This is already the default + + +# if( CMAKE_SIZEOF_VOID_P MATCHES "4") +# # SSE2 option (to allow x87 in 32 bit and disallow extended feature sets which have not yet been checked for precision) +# option (ZDOOM_USE_SSE2 "Use SSE2 instruction set") +# if (ZDOOM_USE_SSE2) +# set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") +# else () +# if (MSVC_VERSION GREATER 1699) +# # On Visual C++ 2012 and later SSE2 is the default, so we need to switch it off explicitly +# set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:IA32") +# endif () +# endif () +# else() +# set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") +# endif() # Avoid CRT DLL dependancies in release builds, optionally generate assembly output for checking crash locations. option( ZDOOM_GENERATE_ASM "Generate assembly output." OFF ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37746a89ef..1d8680a8b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -601,7 +601,9 @@ endif() # Start defining source files for ZDoom set( PLAT_WIN32_SOURCES + sound/mididevices/music_win_mididevice.cpp win32/eaxedit.cpp + win32/critsec.cpp win32/fb_d3d9.cpp win32/fb_d3d9_wipe.cpp win32/fb_ddraw.cpp @@ -626,6 +628,7 @@ set( PLAT_POSIX_SOURCES posix/i_steam.cpp ) set( PLAT_SDL_SOURCES posix/sdl/crashcatcher.c + posix/sdl/critsec.cpp posix/sdl/hardware.cpp posix/sdl/i_gui.cpp posix/sdl/i_input.cpp @@ -640,6 +643,7 @@ set( PLAT_UNIX_SOURCES posix/unix/i_specialpaths.cpp posix/unix/iwadpicker_gtk.cpp ) set( PLAT_OSX_SOURCES + sound/mididevices/music_audiotoolbox_mididevice.cpp posix/osx/iwadpicker_cocoa.mm posix/osx/i_specialpaths.mm posix/osx/zdoom.icns ) @@ -702,6 +706,7 @@ if( HAVE_MMX ) PROPERTIES COMPILE_FLAGS "-mmmx" ) endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) endif( HAVE_MMX ) + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.c ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.h COMMAND lemon -C${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y DEPENDS lemon ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y ) @@ -749,8 +754,8 @@ file( GLOB HEADER_FILES g_inventory/*.h intermission/*.h menu/*.h - oplsynth/*.h - oplsynth/dosbox/*.h + sound/oplsynth/*.h + sound/oplsynth/dosbox/*.h posix/*.h posix/cocoa/*.h posix/sdl/*.h @@ -765,9 +770,22 @@ file( GLOB HEADER_FILES scripting/decorate/*.h scripting/zscript/*.h scripting/vm/*.h - timidity/*.h - wildmidi/*.h + sound/timidity/*.h + sound/wildmidi/*.h xlat/*.h + swrenderer/*.h + swrenderer/drawers/*.h + swrenderer/scene/*.h + swrenderer/segments/*.h + swrenderer/line/*.h + swrenderer/plane/*.h + swrenderer/things/*.h + swrenderer/viewport/*.h + polyrenderer/*.h + polyrenderer/math/*.h + polyrenderer/drawers/*.h + polyrenderer/drawers/*.php + polyrenderer/scene/*.h gl/*.h gl/api/*.h gl/data/*.h @@ -786,10 +804,77 @@ file( GLOB HEADER_FILES *.h ) +set ( SWRENDER_SOURCES + swrenderer/r_swcanvas.cpp + swrenderer/r_swrenderer.cpp + swrenderer/r_memory.cpp + swrenderer/r_renderthread.cpp + swrenderer/drawers/r_draw_pal.cpp + swrenderer/drawers/r_draw_rgba.cpp + swrenderer/drawers/r_thread.cpp + swrenderer/scene/r_3dfloors.cpp + swrenderer/scene/r_light.cpp + swrenderer/scene/r_opaque_pass.cpp + swrenderer/scene/r_portal.cpp + swrenderer/scene/r_scene.cpp + swrenderer/scene/r_translucent_pass.cpp + swrenderer/viewport/r_drawerargs.cpp + swrenderer/viewport/r_skydrawer.cpp + swrenderer/viewport/r_spandrawer.cpp + swrenderer/viewport/r_spritedrawer.cpp + swrenderer/viewport/r_viewport.cpp + swrenderer/viewport/r_walldrawer.cpp + swrenderer/line/r_line.cpp + swrenderer/line/r_walldraw.cpp + swrenderer/line/r_wallsetup.cpp + swrenderer/line/r_fogboundary.cpp + swrenderer/line/r_renderdrawsegment.cpp + swrenderer/segments/r_clipsegment.cpp + swrenderer/segments/r_drawsegment.cpp + swrenderer/segments/r_portalsegment.cpp + swrenderer/things/r_visiblesprite.cpp + swrenderer/things/r_visiblespritelist.cpp + swrenderer/things/r_voxel.cpp + swrenderer/things/r_particle.cpp + swrenderer/things/r_playersprite.cpp + swrenderer/things/r_sprite.cpp + swrenderer/things/r_wallsprite.cpp + swrenderer/things/r_decal.cpp + swrenderer/plane/r_visibleplane.cpp + swrenderer/plane/r_visibleplanelist.cpp + swrenderer/plane/r_skyplane.cpp + swrenderer/plane/r_planerenderer.cpp + swrenderer/plane/r_flatplane.cpp + swrenderer/plane/r_slopeplane.cpp +) + +set( POLYRENDER_SOURCES + polyrenderer/poly_renderer.cpp + polyrenderer/scene/poly_scene.cpp + polyrenderer/scene/poly_portal.cpp + polyrenderer/scene/poly_cull.cpp + polyrenderer/scene/poly_decal.cpp + polyrenderer/scene/poly_particle.cpp + polyrenderer/scene/poly_plane.cpp + polyrenderer/scene/poly_playersprite.cpp + polyrenderer/scene/poly_wall.cpp + polyrenderer/scene/poly_wallsprite.cpp + polyrenderer/scene/poly_sprite.cpp + polyrenderer/scene/poly_sky.cpp + polyrenderer/drawers/poly_buffer.cpp + polyrenderer/drawers/poly_triangle.cpp + polyrenderer/drawers/poly_draw_args.cpp + polyrenderer/drawers/screen_triangle.cpp + polyrenderer/math/tri_matrix.cpp + polyrenderer/math/poly_intersection.cpp +) + # These files will be flagged as "headers" so that they appear in project files # without being compiled. set( NOT_COMPILED_SOURCE_FILES ${OTHER_SYSTEM_SOURCES} + ${SWRENDER_SOURCES} + ${POLYRENDER_SOURCES} sc_man_scanner.h sc_man_scanner.re g_statusbar/sbarinfo_commands.cpp @@ -801,128 +886,30 @@ set( NOT_COMPILED_SOURCE_FILES zcc-parse.h ) -set( FASTMATH_PCH_SOURCES - r_swrenderer.cpp - r_3dfloors.cpp - r_bsp.cpp - r_draw.cpp - r_draw_pal.cpp - r_drawt_pal.cpp - r_thread.cpp - r_main.cpp - r_plane.cpp - r_segs.cpp - r_sky.cpp - r_things.cpp - r_walldraw.cpp - s_advsound.cpp - s_environment.cpp - s_playlist.cpp - s_sndseq.cpp - s_sound.cpp - GuillotineBinPack.cpp - SkylineBinPack.cpp - intermission/intermission.cpp - intermission/intermission_parse.cpp - menu/joystickmenu.cpp - menu/loadsavemenu.cpp - menu/menu.cpp - menu/menudef.cpp - menu/messagebox.cpp - menu/optionmenu.cpp - menu/playermenu.cpp - menu/videomenu.cpp - oplsynth/fmopl.cpp - oplsynth/mlopl.cpp - oplsynth/mlopl_io.cpp - oplsynth/dosbox/opl.cpp - oplsynth/OPL3.cpp - oplsynth/nukedopl3.cpp - timidity/common.cpp - timidity/instrum.cpp - timidity/instrum_dls.cpp - timidity/instrum_font.cpp - timidity/instrum_sf2.cpp - timidity/mix.cpp - timidity/playmidi.cpp - timidity/resample.cpp - timidity/timidity.cpp - wildmidi/file_io.cpp - wildmidi/gus_pat.cpp - wildmidi/reverb.cpp - wildmidi/wm_error.cpp - r_data/colormaps.cpp - r_data/r_translate.cpp -) # This is disabled for now because I cannot find a way to give the .pch file a different name. # Visual C++ 2015 seems hell-bent of only allowing one .pch file with the same name as the executable. #enable_precompiled_headers( g_pch2.h FASTMATH_PCH_SOURCES ) # Enable fast math for some sources set( FASTMATH_SOURCES - ${FASTMATH_PCH_SOURCES} - oplsynth/music_opldumper_mididevice.cpp - oplsynth/music_opl_mididevice.cpp - oplsynth/opl_mus_player.cpp + swrenderer/r_all.cpp + polyrenderer/poly_all.cpp + sound/oplsynth/opl_mus_player.cpp sound/fmodsound.cpp - sound/i_music.cpp - sound/i_sound.cpp sound/mpg123_decoder.cpp - sound/music_cd.cpp - sound/music_dumb.cpp - sound/music_gme.cpp - sound/music_mus_midiout.cpp - sound/music_smf_midiout.cpp - sound/music_hmi_midiout.cpp - sound/music_xmi_midiout.cpp - sound/music_midistream.cpp sound/music_midi_base.cpp - sound/music_midi_timidity.cpp - sound/music_mus_opl.cpp - sound/music_stream.cpp - sound/music_fluidsynth_mididevice.cpp - sound/music_softsynth_mididevice.cpp - sound/music_timidity_mididevice.cpp - sound/music_wildmidi_mididevice.cpp - sound/music_win_mididevice.cpp - sound/music_audiotoolbox_mididevice.cpp sound/oalsound.cpp sound/sndfile_decoder.cpp - sound/music_pseudo_mididevice.cpp - wildmidi/wildmidi_lib.cpp - gl/compatibility/gl_20.cpp - gl/data/gl_data.cpp - gl/data/gl_portaldata.cpp - gl/data/gl_setup.cpp + sound/mididevices/music_timiditypp_mididevice.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_2ddrawer.cpp - gl/renderer/gl_quaddrawer.cpp - gl/renderer/gl_renderer.cpp - gl/renderer/gl_renderstate.cpp - gl/renderer/gl_renderbuffers.cpp - gl/renderer/gl_lightdata.cpp - gl/renderer/gl_postprocess.cpp - gl/renderer/gl_postprocessstate.cpp gl/hqnx/init.cpp gl/hqnx/hq2x.cpp gl/hqnx/hq3x.cpp gl/hqnx/hq4x.cpp gl/xbr/xbrz.cpp gl/xbr/xbrz_old.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 @@ -940,40 +927,9 @@ set( FASTMATH_SOURCES 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/stereo3d/gl_quadstereo.cpp - gl/stereo3d/gl_sidebyside3d.cpp - gl/stereo3d/gl_interleaved3d.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/shaders/gl_shaderprogram.cpp - gl/shaders/gl_presentshader.cpp - gl/shaders/gl_present3dRowshader.cpp - gl/shaders/gl_bloomshader.cpp - gl/shaders/gl_ambientshader.cpp - gl/shaders/gl_blurshader.cpp - gl/shaders/gl_colormapshader.cpp - gl/shaders/gl_tonemapshader.cpp - gl/shaders/gl_lensshader.cpp - gl/shaders/gl_fxaashader.cpp - gl/system/gl_interface.cpp - gl/system/gl_framebuffer.cpp - gl/system/gl_debug.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 ) set (PCH_SOURCES @@ -998,6 +954,7 @@ set (PCH_SOURCES compatibility.cpp configfile.cpp ct_chat.cpp + cycler.cpp d_dehacked.cpp d_iwad.cpp d_main.cpp @@ -1096,6 +1053,12 @@ set (PCH_SOURCES po_man.cpp portal.cpp r_utility.cpp + r_sky.cpp + s_advsound.cpp + s_environment.cpp + s_playlist.cpp + s_sndseq.cpp + s_sound.cpp serializer.cpp sc_man.cpp st_stuff.cpp @@ -1120,6 +1083,8 @@ set (PCH_SOURCES g_inventory/a_weapons.cpp g_shared/a_action.cpp g_shared/a_decals.cpp + g_shared/a_dynlight.cpp + g_shared/a_dynlightdata.cpp g_shared/a_flashfader.cpp g_shared/a_lightning.cpp g_shared/a_morph.cpp @@ -1131,6 +1096,70 @@ set (PCH_SOURCES g_statusbar/sbar_mugshot.cpp g_statusbar/shared_sbar.cpp g_statusbar/strife_sbar.cpp + gl/compatibility/gl_20.cpp + gl/data/gl_data.cpp + gl/data/gl_portaldata.cpp + gl/data/gl_setup.cpp + gl/data/gl_vertexbuffer.cpp + gl/dynlights/gl_glow.cpp + gl/dynlights/gl_lightbuffer.cpp + gl/dynlights/gl_aabbtree.cpp + gl/dynlights/gl_shadowmap.cpp + gl/models/gl_models_md3.cpp + gl/models/gl_models_md2.cpp + gl/models/gl_voxels.cpp + gl/renderer/gl_quaddrawer.cpp + gl/renderer/gl_renderer.cpp + gl/renderer/gl_renderstate.cpp + gl/renderer/gl_renderbuffers.cpp + gl/renderer/gl_lightdata.cpp + gl/renderer/gl_postprocess.cpp + gl/renderer/gl_postprocessstate.cpp + gl/shaders/gl_shader.cpp + gl/shaders/gl_texshader.cpp + gl/shaders/gl_shaderprogram.cpp + gl/shaders/gl_shadowmapshader.cpp + gl/shaders/gl_presentshader.cpp + gl/shaders/gl_present3dRowshader.cpp + gl/shaders/gl_bloomshader.cpp + gl/shaders/gl_ambientshader.cpp + gl/shaders/gl_blurshader.cpp + gl/shaders/gl_colormapshader.cpp + gl/shaders/gl_tonemapshader.cpp + gl/shaders/gl_lensshader.cpp + gl/shaders/gl_fxaashader.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/stereo3d/gl_quadstereo.cpp + gl/stereo3d/gl_sidebyside3d.cpp + gl/stereo3d/gl_interleaved3d.cpp + gl/system/gl_interface.cpp + gl/system/gl_framebuffer.cpp + gl/system/gl_swframebuffer.cpp + gl/system/gl_swwipe.cpp + gl/system/gl_debug.cpp + gl/system/gl_menu.cpp + gl/system/gl_wipe.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 + menu/joystickmenu.cpp + menu/loadsavemenu.cpp + menu/menu.cpp + menu/menudef.cpp + menu/messagebox.cpp + menu/optionmenu.cpp + menu/playermenu.cpp + menu/videomenu.cpp resourcefiles/ancientzip.cpp resourcefiles/file_7z.cpp resourcefiles/file_grp.cpp @@ -1172,6 +1201,10 @@ set (PCH_SOURCES fragglescript/t_spec.cpp fragglescript/t_variable.cpp fragglescript/t_cmd.cpp + intermission/intermission.cpp + intermission/intermission_parse.cpp + r_data/colormaps.cpp + r_data/r_translate.cpp r_data/sprites.cpp r_data/voxels.cpp r_data/renderstyle.cpp @@ -1195,8 +1228,50 @@ set (PCH_SOURCES scripting/zscript/zcc_compile.cpp scripting/zscript/zcc_parser.cpp sfmt/SFMT.cpp + sound/i_music.cpp + sound/i_sound.cpp + sound/mididevices/music_opldumper_mididevice.cpp + sound/mididevices/music_opl_mididevice.cpp + sound/mididevices/music_pseudo_mididevice.cpp + sound/mididevices/music_fluidsynth_mididevice.cpp + sound/mididevices/music_softsynth_mididevice.cpp + sound/mididevices/music_timidity_mididevice.cpp + sound/mididevices/music_wildmidi_mididevice.cpp + sound/musicformats/music_cd.cpp + sound/musicformats/music_dumb.cpp + sound/musicformats/music_gme.cpp + sound/musicformats/music_mus_midiout.cpp + sound/musicformats/music_smf_midiout.cpp + sound/musicformats/music_hmi_midiout.cpp + sound/musicformats/music_xmi_midiout.cpp + sound/musicformats/music_midistream.cpp + sound/musicformats/music_opl.cpp + sound/musicformats/music_stream.cpp + sound/oplsynth/fmopl.cpp + sound/oplsynth/mlopl.cpp + sound/oplsynth/mlopl_io.cpp + sound/oplsynth/dosbox/opl.cpp + sound/oplsynth/OPL3.cpp + sound/oplsynth/nukedopl3.cpp + sound/timidity/common.cpp + sound/timidity/instrum.cpp + sound/timidity/instrum_dls.cpp + sound/timidity/instrum_font.cpp + sound/timidity/instrum_sf2.cpp + sound/timidity/mix.cpp + sound/timidity/playmidi.cpp + sound/timidity/resample.cpp + sound/timidity/timidity.cpp + sound/wildmidi/file_io.cpp + sound/wildmidi/gus_pat.cpp + sound/wildmidi/reverb.cpp + sound/wildmidi/wildmidi_lib.cpp + sound/wildmidi/wm_error.cpp events.cpp + GuillotineBinPack.cpp + SkylineBinPack.cpp ) + enable_precompiled_headers( g_pch.h PCH_SOURCES ) add_executable( zdoom WIN32 MACOSX_BUNDLE @@ -1246,15 +1321,16 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") endif() target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma ) + include_directories( . g_statusbar g_shared g_inventory - oplsynth sound textures - timidity - wildmidi + sound/oplsynth + sound/timidity + sound/wildmidi xlat scripting scripting/vm @@ -1366,12 +1442,12 @@ install(TARGETS zdoom COMPONENT "Game executable") source_group("Audio Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/.+") -source_group("Audio Files\\OPL Synth" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/oplsynth/.+") -source_group("Audio Files\\OPL Synth\\DOSBox" FILES oplsynth/dosbox/opl.cpp oplsynth/dosbox/opl.h) -source_group("Audio Files\\Timidity\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.h$") -source_group("Audio Files\\Timidity\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.cpp$") -source_group("Audio Files\\WildMidi\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/wildmidi/.+\\.h$") -source_group("Audio Files\\WildMidi\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/wildmidi/.+\\.cpp$") +source_group("Audio Files\\OPL Synth" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/oplsynth/.+") +source_group("Audio Files\\OPL Synth\\DOSBox" FILES sound/oplsynth/dosbox/opl.cpp sound/oplsynth/dosbox/opl.h) +source_group("Audio Files\\Timidity" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/timidity/.+") +source_group("Audio Files\\WildMidi" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/wildmidi/.+") +source_group("Audio Files\\MIDI Devices" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/mididevices/.+") +source_group("Audio Files\\Music formats" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/musicformats/.+") source_group("External\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/math/.+") source_group("External\\RapidJSON" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/rapidjson/.+") source_group("External\\SFMT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sfmt/.+") @@ -1393,8 +1469,18 @@ source_group("OpenGL Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOU 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("Software Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/.+") +source_group("Software Renderer\\Drawers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/drawers/.+") +source_group("Software Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/scene/.+") +source_group("Software Renderer\\Segments" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/segments/.+") +source_group("Software Renderer\\Line" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/line/.+") +source_group("Software Renderer\\Plane" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/plane/.+") +source_group("Software Renderer\\Things" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/things/.+") +source_group("Software Renderer\\Viewport" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/viewport/.+") +source_group("Poly Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/polyrenderer/.+") +source_group("Poly Renderer\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/polyrenderer/math/.+") +source_group("Poly Renderer\\Drawers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/polyrenderer/drawers/.+") +source_group("Poly Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/polyrenderer/scene/.+") source_group("Render Data\\Resource Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_data/.+\\.h$") source_group("Render Data\\Resource Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_data/.+\\.cpp$") source_group("Render Data\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/textures/.+") diff --git a/src/actor.h b/src/actor.h index b5f4472b94..b3a63b0a87 100644 --- a/src/actor.h +++ b/src/actor.h @@ -47,6 +47,7 @@ struct subsector_t; struct FBlockNode; struct FPortalGroupArray; struct visstyle_t; +class FLightDefaults; // // NOTES: AActor // @@ -974,6 +975,10 @@ public: void ClearRenderSectorList(); void ClearRenderLineList(); + void AttachLight(unsigned int count, const FLightDefaults *lightdef); + void SetDynamicLights(); + + // info for drawing // NOTE: The first member variable *must* be snext. AActor *snext, **sprev; // links in sector (if needed) @@ -1191,6 +1196,7 @@ public: DVector3 Prev; DRotator PrevAngles; int PrevPortalGroup; + TArray > AttachedLights; // ThingIDs static void ClearTIDHashes (); @@ -1432,12 +1438,10 @@ public: int ApplyDamageFactor(FName damagetype, int damage) const; int GetModifiedDamage(FName damagetype, int damage, bool passive); + static void DeleteAllAttachedLights(); + static void RecreateAllAttachedLights(); - // begin of GZDoom specific additions - TArray > dynamiclights; - void * lightassociations; bool hasmodel; - // end of GZDoom specific additions size_t PropagateMark(); }; @@ -1469,6 +1473,7 @@ public: { base = nullptr; } + private: AActor *base; int id; diff --git a/src/actorinlines.h b/src/actorinlines.h new file mode 100644 index 0000000000..d55ca1b55c --- /dev/null +++ b/src/actorinlines.h @@ -0,0 +1,55 @@ +#pragma once + +#include "actor.h" +#include "r_defs.h" +// These depend on both actor.h and r_defs.h so they cannot be in either file without creating a cross dependency. + +inline DVector3 AActor::PosRelative(int portalgroup) const +{ + return Pos() + Displacements.getOffset(Sector->PortalGroup, portalgroup); +} + +inline DVector3 AActor::PosRelative(const AActor *other) const +{ + return Pos() + Displacements.getOffset(Sector->PortalGroup, other->Sector->PortalGroup); +} + +inline DVector3 AActor::PosRelative(sector_t *sec) const +{ + return Pos() + Displacements.getOffset(Sector->PortalGroup, sec->PortalGroup); +} + +inline DVector3 AActor::PosRelative(line_t *line) const +{ + return Pos() + Displacements.getOffset(Sector->PortalGroup, line->frontsector->PortalGroup); +} + +inline DVector3 PosRelative(const DVector3 &pos, line_t *line, sector_t *refsec = NULL) +{ + return pos + Displacements.getOffset(refsec->PortalGroup, line->frontsector->PortalGroup); +} + + +inline void AActor::ClearInterpolation() +{ + Prev = Pos(); + PrevAngles = Angles; + if (Sector) PrevPortalGroup = Sector->PortalGroup; + else PrevPortalGroup = 0; +} + +inline double secplane_t::ZatPoint(const AActor *ac) const +{ + return (D + normal.X*ac->X() + normal.Y*ac->Y()) * negiC; +} + +inline double sector_t::HighestCeilingAt(AActor *a, sector_t **resultsec) +{ + return HighestCeilingAt(a->Pos(), resultsec); +} + +inline double sector_t::LowestFloorAt(AActor *a, sector_t **resultsec) +{ + return LowestFloorAt(a->Pos(), resultsec); +} + diff --git a/src/am_map.cpp b/src/am_map.cpp index 5c2a80e9d1..57b6dd2d77 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -70,6 +70,7 @@ #include "a_keys.h" #include "r_data/colormaps.h" #include "g_levellocals.h" +#include "actorinlines.h" //============================================================================= @@ -153,12 +154,12 @@ CVAR (Color, am_ovportalcolor, 0x004022, CVAR_ARCHIVE); struct AMColor { int Index; - uint32 RGB; + uint32_t RGB; void FromCVar(FColorCVar & cv) { Index = cv.GetIndex(); - RGB = uint32(cv) | MAKEARGB(255, 0, 0, 0); + RGB = uint32_t(cv) | MAKEARGB(255, 0, 0, 0); } void FromRGB(int r,int g, int b) @@ -1932,7 +1933,7 @@ void AM_drawSubsectors() points[j].Y = float(f_y + (f_h - (pt.y - m_y) * scale)); } // For lighting and texture determination - sector_t *sec = Renderer->FakeFlat(subsectors[i].render_sector, &tempsec, &floorlight, &ceilinglight, false); + sector_t *sec = Renderer->FakeFlat(subsectors[i].render_sector, &tempsec, &floorlight, &ceilinglight); // Find texture origin. originpt.x = -sec->GetXOffset(sector_t::floor); originpt.y = sec->GetYOffset(sector_t::floor); @@ -1958,13 +1959,13 @@ void AM_drawSubsectors() double secx; double secy; double seczb, seczt; - double cmpz = ViewPos.Z; + double cmpz = r_viewpoint.Pos.Z; if (players[consoleplayer].camera && sec == players[consoleplayer].camera->Sector) { // For the actual camera sector use the current viewpoint as reference. - secx = ViewPos.X; - secy = ViewPos.Y; + secx = r_viewpoint.Pos.X; + secy = r_viewpoint.Pos.Y; } else { @@ -2142,7 +2143,6 @@ void AM_showSS() { AM_drawSeg(sub->firstline + i, yellow); } - PO_LinkToSubsectors(); for (int i = 0; i -typedef int8_t SBYTE; -typedef uint8_t BYTE; -typedef int16_t SWORD; -typedef uint16_t WORD; -typedef uint32_t uint32; -typedef uint64_t QWORD; - -// windef.h, included by windows.h, has its own incompatible definition -// of DWORD as a long. In files that mix Doom and Windows code, you -// must define USE_WINDOWS_DWORD before including doomtype.h so that -// you are aware that those files have a different DWORD than the rest -// of the source. - -#ifndef USE_WINDOWS_DWORD -typedef uint32 DWORD; -#endif -typedef uint32 BITFIELD; +typedef uint32_t BITFIELD; typedef int INTBOOL; #if !defined(GUID_DEFINED) #define GUID_DEFINED typedef struct _GUID { - DWORD Data1; + uint32_t Data1; uint16_t Data2; uint16_t Data3; uint8_t Data4[8]; @@ -35,7 +19,7 @@ typedef struct _GUID union QWORD_UNION { - QWORD AsOne; + uint64_t AsOne; struct { #ifdef __BIG_ENDIAN__ @@ -53,13 +37,13 @@ union QWORD_UNION #define FRACUNIT (1< #include diff --git a/src/c_console.cpp b/src/c_console.cpp index f33b2ed370..18e99af5f8 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -48,7 +48,7 @@ #include "hu_stuff.h" #include "i_system.h" #include "i_video.h" -#include "i_input.h" +#include "g_input.h" #include "m_swap.h" #include "v_palette.h" #include "v_video.h" @@ -162,10 +162,14 @@ int active_con_scale() int scale = con_scale; if (scale <= 0) { - scale = CleanXfac - 1; - if (scale <= 0) + scale = uiscale; + if (scale == 0) { - scale = 1; + scale = CleanXfac - 1; + if (scale <= 0) + { + scale = 1; + } } } return scale; diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index fa4b8d1484..5b33cc2630 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -1677,7 +1677,7 @@ void C_SetCVarsToDefaults (void) } } -void C_ArchiveCVars (FConfigFile *f, uint32 filter) +void C_ArchiveCVars (FConfigFile *f, uint32_t filter) { FBaseCVar *cvar = CVars; diff --git a/src/c_cvars.h b/src/c_cvars.h index 5a918bc637..02e9d67a2d 100644 --- a/src/c_cvars.h +++ b/src/c_cvars.h @@ -96,13 +96,13 @@ class FxCVar; class FBaseCVar { public: - FBaseCVar (const char *name, uint32 flags, void (*callback)(FBaseCVar &)); + FBaseCVar (const char *name, uint32_t flags, void (*callback)(FBaseCVar &)); virtual ~FBaseCVar (); inline void Callback () { if (m_Callback) m_Callback (*this); } inline const char *GetName () const { return Name; } - inline uint32 GetFlags () const { return Flags; } + inline uint32_t GetFlags () const { return Flags; } inline FBaseCVar *GetNext() const { return m_Next; } void CmdSet (const char *newval); @@ -147,11 +147,11 @@ protected: static UCVarValue FromGUID (const GUID &value, ECVarType type); char *Name; - uint32 Flags; + uint32_t Flags; private: FBaseCVar (const FBaseCVar &var); - FBaseCVar (const char *name, uint32 flags); + FBaseCVar (const char *name, uint32_t flags); void (*m_Callback)(FBaseCVar &); FBaseCVar *m_Next; @@ -159,26 +159,26 @@ private: static bool m_UseCallback; static bool m_DoNoSet; - friend FString C_GetMassCVarString (uint32 filter, bool compact); + friend FString C_GetMassCVarString (uint32_t filter, bool compact); friend void C_ReadCVars (uint8_t **demo_p); friend void C_BackupCVars (void); friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev); friend FBaseCVar *FindCVarSub (const char *var_name, int namelen); friend void UnlatchCVars (void); friend void DestroyCVarsFlagged (uint32_t flags); - friend void C_ArchiveCVars (FConfigFile *f, uint32 filter); + friend void C_ArchiveCVars (FConfigFile *f, uint32_t filter); friend void C_SetCVarsToDefaults (void); - friend void FilterCompactCVars (TArray &cvars, uint32 filter); + friend void FilterCompactCVars (TArray &cvars, uint32_t filter); friend void C_DeinitConsole(); }; // Returns a string with all cvars whose flags match filter. In compact mode, // the cvar names are omitted to save space. -FString C_GetMassCVarString (uint32 filter, bool compact=false); +FString C_GetMassCVarString (uint32_t filter, bool compact=false); // Writes all cvars that could effect demo sync to *demo_p. These are // cvars that have either CVAR_SERVERINFO or CVAR_DEMOSAVE set. -void C_WriteCVars (uint8_t **demo_p, uint32 filter, bool compact=false); +void C_WriteCVars (uint8_t **demo_p, uint32_t filter, bool compact=false); // Read all cvars from *demo_p and set them appropriately. void C_ReadCVars (uint8_t **demo_p); @@ -205,12 +205,12 @@ void UnlatchCVars (void); void DestroyCVarsFlagged (uint32_t flags); // archive cvars to FILE f -void C_ArchiveCVars (FConfigFile *f, uint32 filter); +void C_ArchiveCVars (FConfigFile *f, uint32_t filter); // initialize cvars to default values after they are created void C_SetCVarsToDefaults (void); -void FilterCompactCVars (TArray &cvars, uint32 filter); +void FilterCompactCVars (TArray &cvars, uint32_t filter); void C_DeinitConsole(); @@ -218,7 +218,7 @@ class FBoolCVar : public FBaseCVar { friend class FxCVar; public: - FBoolCVar (const char *name, bool def, uint32 flags, void (*callback)(FBoolCVar &)=NULL); + FBoolCVar (const char *name, bool def, uint32_t flags, void (*callback)(FBoolCVar &)=NULL); virtual ECVarType GetRealType () const; @@ -244,7 +244,7 @@ class FIntCVar : public FBaseCVar { friend class FxCVar; public: - FIntCVar (const char *name, int def, uint32 flags, void (*callback)(FIntCVar &)=NULL); + FIntCVar (const char *name, int def, uint32_t flags, void (*callback)(FIntCVar &)=NULL); virtual ECVarType GetRealType () const; @@ -272,7 +272,7 @@ class FFloatCVar : public FBaseCVar { friend class FxCVar; public: - FFloatCVar (const char *name, float def, uint32 flags, void (*callback)(FFloatCVar &)=NULL); + FFloatCVar (const char *name, float def, uint32_t flags, void (*callback)(FFloatCVar &)=NULL); virtual ECVarType GetRealType () const; @@ -299,7 +299,7 @@ class FStringCVar : public FBaseCVar { friend class FxCVar; public: - FStringCVar (const char *name, const char *def, uint32 flags, void (*callback)(FStringCVar &)=NULL); + FStringCVar (const char *name, const char *def, uint32_t flags, void (*callback)(FStringCVar &)=NULL); ~FStringCVar (); virtual ECVarType GetRealType () const; @@ -326,7 +326,7 @@ class FColorCVar : public FIntCVar { friend class FxCVar; public: - FColorCVar (const char *name, int def, uint32 flags, void (*callback)(FColorCVar &)=NULL); + FColorCVar (const char *name, int def, uint32_t flags, void (*callback)(FColorCVar &)=NULL); virtual ECVarType GetRealType () const; @@ -334,8 +334,8 @@ public: virtual UCVarValue GetGenericRepDefault (ECVarType type) const; virtual void SetGenericRepDefault (UCVarValue value, ECVarType type); - inline operator uint32 () const { return Value; } - inline uint32 operator *() const { return Value; } + inline operator uint32_t () const { return Value; } + inline uint32_t operator *() const { return Value; } inline int GetIndex () const { return Index; } protected: @@ -351,7 +351,7 @@ class FFlagCVar : public FBaseCVar { friend class FxCVar; public: - FFlagCVar (const char *name, FIntCVar &realvar, uint32 bitval); + FFlagCVar (const char *name, FIntCVar &realvar, uint32_t bitval); virtual ECVarType GetRealType () const; @@ -372,7 +372,7 @@ protected: virtual void DoSet (UCVarValue value, ECVarType type); FIntCVar &ValueVar; - uint32 BitVal; + uint32_t BitVal; int BitNum; }; @@ -380,7 +380,7 @@ class FMaskCVar : public FBaseCVar { friend class FxCVar; public: - FMaskCVar (const char *name, FIntCVar &realvar, uint32 bitval); + FMaskCVar (const char *name, FIntCVar &realvar, uint32_t bitval); virtual ECVarType GetRealType () const; @@ -397,14 +397,14 @@ protected: virtual void DoSet (UCVarValue value, ECVarType type); FIntCVar &ValueVar; - uint32 BitVal; + uint32_t BitVal; int BitNum; }; class FGUIDCVar : public FBaseCVar { public: - FGUIDCVar (const char *name, const GUID *defguid, uint32 flags, void (*callback)(FGUIDCVar &)=NULL); + FGUIDCVar (const char *name, const GUID *defguid, uint32_t flags, void (*callback)(FGUIDCVar &)=NULL); virtual ECVarType GetRealType () const; diff --git a/src/c_dispatch.h b/src/c_dispatch.h index 68d6233514..24a7c42ecf 100644 --- a/src/c_dispatch.h +++ b/src/c_dispatch.h @@ -153,10 +153,10 @@ struct FButtonStatus enum { MAX_KEYS = 6 }; // Maximum number of keys that can press this button uint16_t Keys[MAX_KEYS]; - BYTE bDown; // Button is down right now - BYTE bWentDown; // Button went down this tic - BYTE bWentUp; // Button went up this tic - BYTE padTo16Bytes; + uint8_t bDown; // Button is down right now + uint8_t bWentDown; // Button went down this tic + uint8_t bWentUp; // Button went up this tic + uint8_t padTo16Bytes; bool PressKey (int keynum); // Returns true if this key caused the button to be pressed. bool ReleaseKey (int keynum); // Returns true if this key is no longer pressed. diff --git a/src/cmdlib.cpp b/src/cmdlib.cpp index 471c02cc8a..6fc253ff38 100644 --- a/src/cmdlib.cpp +++ b/src/cmdlib.cpp @@ -412,7 +412,7 @@ bool CheckWildcards (const char *pattern, const char *text) void FormatGUID (char *buffer, size_t buffsize, const GUID &guid) { mysnprintf (buffer, buffsize, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", - (uint32)guid.Data1, guid.Data2, guid.Data3, + (uint32_t)guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], diff --git a/src/colormatcher.cpp b/src/colormatcher.cpp index bc0a921282..a93192fe23 100644 --- a/src/colormatcher.cpp +++ b/src/colormatcher.cpp @@ -71,10 +71,10 @@ void FColorMatcher::SetPalette (const uint32_t *palette) Pal = (const PalEntry *)palette; } -BYTE FColorMatcher::Pick (int r, int g, int b) +uint8_t FColorMatcher::Pick (int r, int g, int b) { if (Pal == NULL) return 1; - return (BYTE)BestColor ((uint32 *)Pal, r, g, b); + return (uint8_t)BestColor ((uint32_t *)Pal, r, g, b); } diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 7698a3f8b8..f47ea9a965 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -114,6 +114,7 @@ static FCompatOption Options[] = { "linkfrozenprops", BCOMPATF_LINKFROZENPROPS, SLOT_BCOMPAT }, { "floatbob", BCOMPATF_FLOATBOB, SLOT_BCOMPAT }, { "noslopeid", BCOMPATF_NOSLOPEID, SLOT_BCOMPAT }, + { "clipmidtex", BCOMPATF_CLIPMIDTEX, SLOT_BCOMPAT }, // list copied from g_mapinfo.cpp { "shorttex", COMPATF_SHORTTEX, SLOT_COMPAT }, @@ -152,7 +153,6 @@ static FCompatOption Options[] = { "multiexit", COMPATF2_MULTIEXIT, SLOT_COMPAT2 }, { "teleport", COMPATF2_TELEPORT, SLOT_COMPAT2 }, { "disablepushwindowcheck", COMPATF2_PUSHWINDOW, SLOT_COMPAT2 }, - { NULL, 0, 0 } }; diff --git a/src/compatibility.h b/src/compatibility.h index 2c33e962eb..125f83f34c 100644 --- a/src/compatibility.h +++ b/src/compatibility.h @@ -7,7 +7,7 @@ union FMD5Holder { - BYTE Bytes[16]; + uint8_t Bytes[16]; uint32_t DWords[4]; hash_t Hash; }; diff --git a/src/configfile.cpp b/src/configfile.cpp index 146fefdfce..2319f28226 100644 --- a/src/configfile.cpp +++ b/src/configfile.cpp @@ -863,7 +863,7 @@ const char *FConfigFile::GenerateEndTag(const char *value) // isn't in the value. We create the sequences by generating two // 64-bit random numbers and Base64 encoding the first 15 bytes // from them. - union { QWORD rand_num[2]; BYTE rand_bytes[16]; }; + union { uint64_t rand_num[2]; uint8_t rand_bytes[16]; }; do { rand_num[0] = pr_endtag.GenRand64(); diff --git a/src/critsec.h b/src/critsec.h new file mode 100644 index 0000000000..ad510383b8 --- /dev/null +++ b/src/critsec.h @@ -0,0 +1,37 @@ +#pragma once + +// System independent critical sections without polluting the namespace with the operating system headers. +class FInternalCriticalSection; +FInternalCriticalSection *CreateCriticalSection(); +void DeleteCriticalSection(FInternalCriticalSection *c); +void EnterCriticalSection(FInternalCriticalSection *c); +void LeaveCriticalSection(FInternalCriticalSection *c); + +// This is just a convenience wrapper around the function interface. +class FCriticalSection +{ +public: + FCriticalSection() + { + c = CreateCriticalSection(); + } + + ~FCriticalSection() + { + DeleteCriticalSection(c); + } + + void Enter() + { + EnterCriticalSection(c); + } + + void Leave() + { + LeaveCriticalSection(c); + } + +private: + FInternalCriticalSection *c; + +}; diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index 98e95cdde4..c49e95f578 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -27,7 +27,7 @@ #include "v_video.h" #include "gi.h" #include "d_gui.h" -#include "i_input.h" +#include "g_input.h" #include "templates.h" #include "d_net.h" #include "d_event.h" diff --git a/src/gl/utility/gl_cycler.cpp b/src/cycler.cpp similarity index 88% rename from src/gl/utility/gl_cycler.cpp rename to src/cycler.cpp index d79770bf37..dab4bab505 100644 --- a/src/gl/utility/gl_cycler.cpp +++ b/src/cycler.cpp @@ -35,7 +35,7 @@ #include #include "serializer.h" -#include "gl/utility/gl_cycler.h" +#include "cycler.h" //========================================================================== // @@ -68,11 +68,11 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FCycler &c, FCycler *d FCycler::FCycler() { - m_cycle = 0.f; + m_cycle = 0.; m_cycleType = CYCLE_Linear; m_shouldCycle = false; - m_start = m_current = 0.f; - m_end = 0.f; + m_start = m_current = 0.; + m_end = 0.; m_increment = true; } @@ -83,19 +83,19 @@ FCycler::FCycler() // //========================================================================== -void FCycler::SetParams(float start, float end, float cycle, bool update) +void FCycler::SetParams(double start, double end, double cycle, bool update) { if (!update || cycle != m_cycle) { m_cycle = cycle; - m_time = 0.f; + m_time = 0.; m_increment = true; m_current = start; } else { // When updating and keeping the same cycle, scale the current light size to the new dimensions. - float fact = (m_current - m_start) / (m_end - m_start); + double fact = (m_current - m_start) / (m_end - m_start); m_current = start + fact *(end - start); } m_start = start; @@ -109,10 +109,10 @@ void FCycler::SetParams(float start, float end, float cycle, bool update) // //========================================================================== -void FCycler::Update(float diff) +void FCycler::Update(double diff) { - float mult, angle; - float step = m_end - m_start; + double mult, angle; + double step = m_end - m_start; if (!m_shouldCycle) { @@ -140,15 +140,15 @@ void FCycler::Update(float diff) } break; case CYCLE_Sin: - angle = float(M_PI * 2.f * mult); - mult = sinf(angle); - mult = (mult + 1.f) / 2.f; + angle = double(M_PI * 2. * mult); + mult = g_sin(angle); + mult = (mult + 1.) / 2.; m_current = m_start + (step * mult); break; case CYCLE_Cos: - angle = float(M_PI * 2.f * mult); - mult = cosf(angle); - mult = (mult + 1.f) / 2.f; + angle = double(M_PI * 2. * mult); + mult = g_cos(angle); + mult = (mult + 1.) / 2.; m_current = m_start + (step * mult); break; case CYCLE_SawTooth: @@ -168,7 +168,7 @@ void FCycler::Update(float diff) if (m_time == m_cycle) { - m_time = 0.f; + m_time = 0.; m_increment = !m_increment; } } diff --git a/src/gl/utility/gl_cycler.h b/src/cycler.h similarity index 68% rename from src/gl/utility/gl_cycler.h rename to src/cycler.h index bcff7e9dea..680a976f7d 100644 --- a/src/gl/utility/gl_cycler.h +++ b/src/cycler.h @@ -21,17 +21,17 @@ class FCycler public: FCycler(); - void Update(float diff); - void SetParams(float start, float end, float cycle, bool update = false); + void Update(double diff); + void SetParams(double start, double end, double cycle, bool update = false); void ShouldCycle(bool sc) { m_shouldCycle = sc; } void SetCycleType(CycleType ct) { m_cycleType = ct; } - float GetVal() { return m_current; } + double GetVal() { return m_current; } - inline operator float () const { return m_current; } + inline operator double () const { return m_current; } protected: - float m_start, m_end, m_current; - float m_time, m_cycle; + double m_start, m_end, m_current; + double m_time, m_cycle; bool m_increment, m_shouldCycle; CycleType m_cycleType; diff --git a/src/d_main.cpp b/src/d_main.cpp index 7e268e11c3..3185f99662 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -111,6 +111,7 @@ #include "fragglescript/t_fs.h" #include "g_levellocals.h" #include "events.h" +#include "r_utility.h" EXTERN_CVAR(Bool, hud_althud) void DrawHUD(); @@ -124,7 +125,6 @@ void DrawHUD(); extern void ReadStatistics(); extern void M_RestoreMode (); extern void M_SetDefaultMode (); -extern void R_ExecuteSetViewSize (); extern void G_NewInit (); extern void SetupPlayerClasses (); extern void HUD_InitHud(); @@ -138,6 +138,7 @@ void G_BuildTiccmd (ticcmd_t* cmd); void D_DoAdvanceDemo (); void D_AddWildFile (TArray &wadfiles, const char *pattern); void D_LoadWadSettings (); +void ParseGLDefs(); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- @@ -670,7 +671,7 @@ void D_Display () if (viewactive) { - R_SetFOV (players[consoleplayer].camera && players[consoleplayer].camera->player ? + R_SetFOV (r_viewpoint, players[consoleplayer].camera && players[consoleplayer].camera->player ? players[consoleplayer].camera->player->FOV : 90.f); } @@ -697,12 +698,10 @@ void D_Display () } } - RenderTarget = screen; - // change the view size if needed if (setsizeneeded && StatusBar != NULL) { - R_ExecuteSetViewSize (); + R_ExecuteSetViewSize (r_viewpoint, r_viewwindow); } setmodeneeded = false; @@ -973,7 +972,6 @@ void D_ErrorCleanup () menuactive = MENU_Off; } insave = false; - Renderer->ErrorCleanup(); } //========================================================================== @@ -2503,6 +2501,8 @@ void D_DoomMain (void) StartScreen->Progress (); + ParseGLDefs(); + if (!batchrun) Printf ("R_Init: Init %s refresh subsystem.\n", gameinfo.ConfigName.GetChars()); StartScreen->LoadingStatus ("Loading graphics", 0x3f); R_Init (); diff --git a/src/d_net.h b/src/d_net.h index def8dee068..282f73e152 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -25,7 +25,7 @@ #include "doomtype.h" #include "doomdef.h" -#include "d_ticcmd.h" +#include "d_protocol.h" // diff --git a/src/d_netinf.h b/src/d_netinf.h index 80d64bbb67..c5c82f65b3 100644 --- a/src/d_netinf.h +++ b/src/d_netinf.h @@ -36,8 +36,6 @@ #include "c_cvars.h" -EXTERN_CVAR (Float, autoaim) - int D_GenderToInt (const char *gender); extern const char *GenderNames[3]; @@ -49,10 +47,10 @@ void D_UserInfoChanged (FBaseCVar *info); void D_SendServerInfoChange (const FBaseCVar *cvar, UCVarValue value, ECVarType type); void D_SendServerFlagChange (const FBaseCVar *cvar, int bitnum, bool set); -void D_DoServerInfoChange (BYTE **stream, bool singlebit); +void D_DoServerInfoChange (uint8_t **stream, bool singlebit); -void D_WriteUserInfoStrings (int player, BYTE **stream, bool compact=false); -void D_ReadUserInfoStrings (int player, BYTE **stream, bool update); +void D_WriteUserInfoStrings (int player, uint8_t **stream, bool compact=false); +void D_ReadUserInfoStrings (int player, uint8_t **stream, bool update); struct FPlayerColorSet; void D_GetPlayerColor (int player, float *h, float *s, float *v, FPlayerColorSet **colorset); diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 5c4b249788..a178ff9178 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -175,7 +175,7 @@ void D_GetPlayerColor (int player, float *h, float *s, float *v, FPlayerColorSet { userinfo_t *info = &players[player].userinfo; FPlayerColorSet *colorset = NULL; - uint32 color; + uint32_t color; int team; if (players[player].mo != NULL) @@ -485,7 +485,7 @@ int userinfo_t::ColorSetChanged(int setnum) return setnum; } -uint32 userinfo_t::ColorChanged(const char *colorname) +uint32_t userinfo_t::ColorChanged(const char *colorname) { FColorCVar *color = static_cast((*this)[NAME_Color]); assert(color != NULL); @@ -496,7 +496,7 @@ uint32 userinfo_t::ColorChanged(const char *colorname) return *color; } -uint32 userinfo_t::ColorChanged(uint32 colorval) +uint32_t userinfo_t::ColorChanged(uint32_t colorval) { FColorCVar *color = static_cast((*this)[NAME_Color]); assert(color != NULL); diff --git a/src/d_player.h b/src/d_player.h index 031083bbfe..4a718778bd 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -26,7 +26,7 @@ // Finally, for odd reasons, the player input // is buffered within the player data struct, // as commands per game tick. -#include "d_ticcmd.h" +#include "d_protocol.h" #include "doomstat.h" #include "a_weapons.h" @@ -308,7 +308,7 @@ struct userinfo_t : TMap { return *static_cast(*CheckKey(NAME_ColorSet)); } - uint32 GetColor() const + uint32_t GetColor() const { return *static_cast(*CheckKey(NAME_Color)); } @@ -356,8 +356,8 @@ struct userinfo_t : TMap int GenderChanged(const char *gendername); int PlayerClassChanged(const char *classname); int PlayerClassNumChanged(int classnum); - uint32 ColorChanged(const char *colorname); - uint32 ColorChanged(uint32 colorval); + uint32_t ColorChanged(const char *colorname); + uint32_t ColorChanged(uint32_t colorval); int ColorSetChanged(int setnum); }; diff --git a/src/d_protocol.cpp b/src/d_protocol.cpp index 36ec2cab2b..06caca46cd 100644 --- a/src/d_protocol.cpp +++ b/src/d_protocol.cpp @@ -33,7 +33,7 @@ */ #include "i_system.h" -#include "d_ticcmd.h" +#include "d_protocol.h" #include "d_net.h" #include "doomdef.h" #include "doomstat.h" diff --git a/src/d_protocol.h b/src/d_protocol.h index 7de26a179d..5ac1258b39 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -229,7 +229,15 @@ int UnpackUserCmd (usercmd_t *ucmd, const usercmd_t *basis, uint8_t **stream); int PackUserCmd (const usercmd_t *ucmd, const usercmd_t *basis, uint8_t **stream); int WriteUserCmdMessage (usercmd_t *ucmd, const usercmd_t *basis, uint8_t **stream); -struct ticcmd_t; +// The data sampled per tick (single player) +// and transmitted to other peers (multiplayer). +// Mainly movements/button commands per game tick, +// plus a checksum for internal state consistency. +struct ticcmd_t +{ + usercmd_t ucmd; + int16_t consistancy; // checks for net game +}; int SkipTicCmd (uint8_t **stream, int count); void ReadTicCmd (uint8_t **stream, int player, int tic); diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h deleted file mode 100644 index 66a629e081..0000000000 --- a/src/d_ticcmd.h +++ /dev/null @@ -1,38 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// System specific interface stuff. -// -//----------------------------------------------------------------------------- - - -#ifndef __D_TICCMD_H__ -#define __D_TICCMD_H__ - -#include "d_protocol.h" - -// The data sampled per tick (single player) -// and transmitted to other peers (multiplayer). -// Mainly movements/button commands per game tick, -// plus a checksum for internal state consistency. -struct ticcmd_t -{ - usercmd_t ucmd; - int16_t consistancy; // checks for net game -}; - -#endif // __D_TICCMD_H__ diff --git a/src/decallib.h b/src/decallib.h index 7c3369c4d0..75df4ae427 100644 --- a/src/decallib.h +++ b/src/decallib.h @@ -38,7 +38,6 @@ #include "doomtype.h" #include "r_data/renderstyle.h" -#include "textures/textures.h" class FScanner; class FDecalTemplate; diff --git a/src/dobject.h b/src/dobject.h index 31215b9814..f12bfc5014 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -185,236 +185,7 @@ protected: \ #define _X_VMEXPORT_true(cls) nullptr #define _X_VMEXPORT_false(cls) nullptr -enum EObjectFlags -{ - // GC flags - OF_White0 = 1 << 0, // Object is white (type 0) - OF_White1 = 1 << 1, // Object is white (type 1) - OF_Black = 1 << 2, // Object is black - OF_Fixed = 1 << 3, // Object is fixed (should not be collected) - OF_Rooted = 1 << 4, // Object is soft-rooted - OF_EuthanizeMe = 1 << 5, // Object wants to die - OF_Cleanup = 1 << 6, // Object is now being deleted by the collector - OF_YesReallyDelete = 1 << 7, // Object is being deleted outside the collector, and this is okay, so don't print a warning - - OF_WhiteBits = OF_White0 | OF_White1, - OF_MarkBits = OF_WhiteBits | OF_Black, - - // Other flags - OF_JustSpawned = 1 << 8, // Thinker was spawned this tic - OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls - OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list - OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk) - OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning) - OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function - OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function at all - OF_UI = 1 << 15, // Marks a class that defaults to VARF_UI for it's fields/methods - OF_Play = 1 << 16, // Marks a class that defaults to VARF_Play for it's fields/methods -}; - -template class TObjPtr; - -namespace GC -{ - enum EGCState - { - GCS_Pause, - GCS_Propagate, - GCS_Sweep, - GCS_Finalize - }; - - // Number of bytes currently allocated through M_Malloc/M_Realloc. - extern size_t AllocBytes; - - // Amount of memory to allocate before triggering a collection. - extern size_t Threshold; - - // List of gray objects. - extern DObject *Gray; - - // List of every object. - extern DObject *Root; - - // Current white value for potentially-live objects. - extern uint32 CurrentWhite; - - // Current collector state. - extern EGCState State; - - // Position of GC sweep in the list of objects. - extern DObject **SweepPos; - - // Size of GC pause. - extern int Pause; - - // Size of GC steps. - extern int StepMul; - - // Is this the final collection just before exit? - extern bool FinalGC; - - // Current white value for known-dead objects. - static inline uint32 OtherWhite() - { - return CurrentWhite ^ OF_WhiteBits; - } - - // Frees all objects, whether they're dead or not. - void FreeAll(); - - // Does one collection step. - void Step(); - - // Does a complete collection. - void FullGC(); - - // Handles the grunt work for a write barrier. - void Barrier(DObject *pointing, DObject *pointed); - - // Handles a write barrier. - static inline void WriteBarrier(DObject *pointing, DObject *pointed); - - // Handles a write barrier for a pointer that isn't inside an object. - static inline void WriteBarrier(DObject *pointed); - - // Handles a read barrier. - template inline T *ReadBarrier(T *&obj) - { - if (obj == NULL || !(obj->ObjectFlags & OF_EuthanizeMe)) - { - return obj; - } - return obj = NULL; - } - - // Check if it's time to collect, and do a collection step if it is. - static inline void CheckGC() - { - if (AllocBytes >= Threshold) - Step(); - } - - // Forces a collection to start now. - static inline void StartCollection() - { - Threshold = AllocBytes; - } - - // Marks a white object gray. If the object wants to die, the pointer - // is NULLed instead. - void Mark(DObject **obj); - - // Marks an array of objects. - void MarkArray(DObject **objs, size_t count); - - // For cleanup - void DelSoftRootHead(); - - // Soft-roots an object. - void AddSoftRoot(DObject *obj); - - // Unroots an object. - void DelSoftRoot(DObject *obj); - - template void Mark(T *&obj) - { - union - { - T *t; - DObject *o; - }; - o = obj; - Mark(&o); - obj = t; - } - template void Mark(TObjPtr &obj); - - template void MarkArray(T **obj, size_t count) - { - MarkArray((DObject **)(obj), count); - } - template void MarkArray(TArray &arr) - { - MarkArray(&arr[0], arr.Size()); - } -} - -// A template class to help with handling read barriers. It does not -// handle write barriers, because those can be handled more efficiently -// with knowledge of the object that holds the pointer. -template -class TObjPtr -{ - union - { - T pp; - DObject *o; - }; -public: - TObjPtr() throw() - { - } - TObjPtr(T q) throw() - : pp(q) - { - } - TObjPtr(const TObjPtr &q) throw() - : pp(q.pp) - { - } - T operator=(T q) throw() - { - return pp = q; - // The caller must now perform a write barrier. - } - operator T() throw() - { - return GC::ReadBarrier(pp); - } - T &operator*() - { - T q = GC::ReadBarrier(pp); - assert(q != NULL); - return *q; - } - T *operator&() throw() - { - // Does not perform a read barrier. The only real use for this is with - // the DECLARE_POINTER macro, where a read barrier would be a very bad - // thing. - return &pp; - } - T operator->() throw() - { - return GC::ReadBarrier(pp); - } - bool operator!=(T u) throw() - { - return GC::ReadBarrier(o) != u; - } - bool operator==(T u) throw() - { - return GC::ReadBarrier(o) == u; - } - - template friend inline void GC::Mark(TObjPtr &obj); - template friend FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr &value, TObjPtr *); - - friend class DObject; -}; - -// Use barrier_cast instead of static_cast when you need to cast -// the contents of a TObjPtr to a related type. -template inline T barrier_cast(TObjPtr &o) -{ - return static_cast(static_cast(o)); -} - -template inline void GC::Mark(TObjPtr &obj) -{ - GC::Mark(&obj.o); -} +#include "dobjgc.h" class DObject { @@ -434,7 +205,7 @@ private: public: DObject *ObjNext; // Keep track of all allocated objects DObject *GCNext; // Next object in this collection list - uint32 ObjectFlags; // Flags for this object + uint32_t ObjectFlags; // Flags for this object void *ScriptVar(FName field, PType *type); diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index 56e8c82d4d..35a30c1366 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -65,7 +65,6 @@ #include "sbar.h" #include "stats.h" #include "c_dispatch.h" -#include "p_acs.h" #include "s_sndseq.h" #include "r_data/r_interpolate.h" #include "doomstat.h" @@ -80,6 +79,8 @@ #include "g_levellocals.h" #include "events.h" +void MarkACSThinker(); + // MACROS ------------------------------------------------------------------ /* @@ -331,7 +332,7 @@ static void MarkRoot() Mark(DIntermissionController::CurrentIntermission); DThinker::MarkRoots(); FCanvasTextureInfo::Mark(); - Mark(DACSThinker::ActiveThinker); + MarkACSThinker(); Mark(E_FirstEventHandler); Mark(E_LastEventHandler); for (auto &s : level.sectorPortals) diff --git a/src/dobjgc.h b/src/dobjgc.h new file mode 100644 index 0000000000..cc5f210b3c --- /dev/null +++ b/src/dobjgc.h @@ -0,0 +1,235 @@ +#pragma once +#include +class DObject; +class FSerializer; + +enum EObjectFlags +{ + // GC flags + OF_White0 = 1 << 0, // Object is white (type 0) + OF_White1 = 1 << 1, // Object is white (type 1) + OF_Black = 1 << 2, // Object is black + OF_Fixed = 1 << 3, // Object is fixed (should not be collected) + OF_Rooted = 1 << 4, // Object is soft-rooted + OF_EuthanizeMe = 1 << 5, // Object wants to die + OF_Cleanup = 1 << 6, // Object is now being deleted by the collector + OF_YesReallyDelete = 1 << 7, // Object is being deleted outside the collector, and this is okay, so don't print a warning + + OF_WhiteBits = OF_White0 | OF_White1, + OF_MarkBits = OF_WhiteBits | OF_Black, + + // Other flags + OF_JustSpawned = 1 << 8, // Thinker was spawned this tic + OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls + OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list + OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk) + OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning) + OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function + OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function at all + OF_UI = 1 << 15, // Marks a class that defaults to VARF_UI for it's fields/methods + OF_Play = 1 << 16, // Marks a class that defaults to VARF_Play for it's fields/methods +}; + +template class TObjPtr; + +namespace GC +{ + enum EGCState + { + GCS_Pause, + GCS_Propagate, + GCS_Sweep, + GCS_Finalize + }; + + // Number of bytes currently allocated through M_Malloc/M_Realloc. + extern size_t AllocBytes; + + // Amount of memory to allocate before triggering a collection. + extern size_t Threshold; + + // List of gray objects. + extern DObject *Gray; + + // List of every object. + extern DObject *Root; + + // Current white value for potentially-live objects. + extern uint32_t CurrentWhite; + + // Current collector state. + extern EGCState State; + + // Position of GC sweep in the list of objects. + extern DObject **SweepPos; + + // Size of GC pause. + extern int Pause; + + // Size of GC steps. + extern int StepMul; + + // Is this the final collection just before exit? + extern bool FinalGC; + + // Current white value for known-dead objects. + static inline uint32_t OtherWhite() + { + return CurrentWhite ^ OF_WhiteBits; + } + + // Frees all objects, whether they're dead or not. + void FreeAll(); + + // Does one collection step. + void Step(); + + // Does a complete collection. + void FullGC(); + + // Handles the grunt work for a write barrier. + void Barrier(DObject *pointing, DObject *pointed); + + // Handles a write barrier. + static inline void WriteBarrier(DObject *pointing, DObject *pointed); + + // Handles a write barrier for a pointer that isn't inside an object. + static inline void WriteBarrier(DObject *pointed); + + // Handles a read barrier. + template inline T *ReadBarrier(T *&obj) + { + if (obj == NULL || !(obj->ObjectFlags & OF_EuthanizeMe)) + { + return obj; + } + return obj = NULL; + } + + // Check if it's time to collect, and do a collection step if it is. + static inline void CheckGC() + { + if (AllocBytes >= Threshold) + Step(); + } + + // Forces a collection to start now. + static inline void StartCollection() + { + Threshold = AllocBytes; + } + + // Marks a white object gray. If the object wants to die, the pointer + // is NULLed instead. + void Mark(DObject **obj); + + // Marks an array of objects. + void MarkArray(DObject **objs, size_t count); + + // For cleanup + void DelSoftRootHead(); + + // Soft-roots an object. + void AddSoftRoot(DObject *obj); + + // Unroots an object. + void DelSoftRoot(DObject *obj); + + template void Mark(T *&obj) + { + union + { + T *t; + DObject *o; + }; + o = obj; + Mark(&o); + obj = t; + } + template void Mark(TObjPtr &obj); + + template void MarkArray(T **obj, size_t count) + { + MarkArray((DObject **)(obj), count); + } + template void MarkArray(TArray &arr) + { + MarkArray(&arr[0], arr.Size()); + } +} + +// A template class to help with handling read barriers. It does not +// handle write barriers, because those can be handled more efficiently +// with knowledge of the object that holds the pointer. +template +class TObjPtr +{ + union + { + T pp; + DObject *o; + }; +public: + TObjPtr() throw() + { + } + TObjPtr(T q) throw() + : pp(q) + { + } + TObjPtr(const TObjPtr &q) throw() + : pp(q.pp) + { + } + T operator=(T q) throw() + { + return pp = q; + // The caller must now perform a write barrier. + } + operator T() throw() + { + return GC::ReadBarrier(pp); + } + T &operator*() + { + T q = GC::ReadBarrier(pp); + assert(q != NULL); + return *q; + } + T *operator&() throw() + { + // Does not perform a read barrier. The only real use for this is with + // the DECLARE_POINTER macro, where a read barrier would be a very bad + // thing. + return &pp; + } + T operator->() throw() + { + return GC::ReadBarrier(pp); + } + bool operator!=(T u) throw() + { + return GC::ReadBarrier(o) != u; + } + bool operator==(T u) throw() + { + return GC::ReadBarrier(o) == u; + } + + template friend inline void GC::Mark(TObjPtr &obj); + template friend FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr &value, TObjPtr *); + + friend class DObject; +}; + +// Use barrier_cast instead of static_cast when you need to cast +// the contents of a TObjPtr to a related type. +template inline T barrier_cast(TObjPtr &o) +{ + return static_cast(static_cast(o)); +} + +template inline void GC::Mark(TObjPtr &obj) +{ + GC::Mark(&obj.o); +} diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 668daafda8..818543e89f 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -645,7 +645,7 @@ void PInt::SetValue(void *addr, int val) } else if (Size == 8) { - *(QWORD *)addr = val; + *(uint64_t *)addr = val; } else { @@ -681,7 +681,7 @@ int PInt::GetValueInt(void *addr) const } else if (Size == 8) { // truncated output - return (int)*(QWORD *)addr; + return (int)*(uint64_t *)addr; } else { diff --git a/src/dobjtype.h b/src/dobjtype.h index 6b24610f83..c3ced5a638 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -8,6 +8,13 @@ typedef std::pair FTypeAndOffset; class PStruct; +// This is intentionally not in vm.h so that this file remains free of DObject pollution. +class VMException : public DObject +{ + DECLARE_CLASS(VMException, DObject); +}; + + #include "vm.h" // Variable/parameter/field flags ------------------------------------------- diff --git a/src/doomdef.h b/src/doomdef.h index d78bef6a9f..0b639db30b 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -359,6 +359,7 @@ enum BCOMPATF_LINKFROZENPROPS = 1 << 6, // Clearing PROP_TOTALLYFROZEN or PROP_FROZEN also clears the other BCOMPATF_FLOATBOB = 1 << 8, // Use Hexen's original method of preventing floatbobbing items from falling down BCOMPATF_NOSLOPEID = 1 << 9, // disable line IDs on slopes. + BCOMPATF_CLIPMIDTEX = 1 << 10, // Always Clip midtex's in the software renderer (required to run certain GZDoom maps) }; // phares 3/20/98: diff --git a/src/doomstat.h b/src/doomstat.h index a73f134478..3ebf07f92d 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -122,8 +122,8 @@ extern bool noblit; extern int viewwindowx; extern int viewwindowy; -extern "C" int viewheight; -extern "C" int viewwidth; +extern int viewheight; +extern int viewwidth; diff --git a/src/doomtype.h b/src/doomtype.h index 0d0b79068b..15717b65aa 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -118,9 +118,9 @@ enum struct PalEntry { PalEntry () {} - PalEntry (uint32 argb) { d = argb; } - operator uint32 () const { return d; } - PalEntry &operator= (uint32 other) { d = other; return *this; } + PalEntry (uint32_t argb) { d = argb; } + operator uint32_t () const { return d; } + PalEntry &operator= (uint32_t other) { d = other; return *this; } PalEntry InverseColor() const { PalEntry nc; nc.a = a; nc.r = 255 - r; nc.g = 255 - g; nc.b = 255 - b; return nc; } #ifdef __BIG_ENDIAN__ PalEntry (uint8_t ir, uint8_t ig, uint8_t ib) : a(0), r(ir), g(ig), b(ib) {} @@ -131,7 +131,7 @@ struct PalEntry { uint8_t a,r,g,b; }; - uint32 d; + uint32_t d; }; #else PalEntry (uint8_t ir, uint8_t ig, uint8_t ib) : b(ib), g(ig), r(ir), a(0) {} @@ -142,7 +142,7 @@ struct PalEntry { uint8_t b,g,r,a; }; - uint32 d; + uint32_t d; }; #endif }; diff --git a/src/dscript.h b/src/dscript.h deleted file mode 100644 index 725f9b77b8..0000000000 --- a/src/dscript.h +++ /dev/null @@ -1,26 +0,0 @@ -enum -{ - EX_Return = 0x00, - EX_EndFuncParms = 0x01, - EX_ByteConst = 0x02, - EX_WordConst = 0x03, - EX_DWordConst = 0x04, - EX_FixedConst = 0x05, // To become FloatConst whenever I move to floats - EX_StringConst = 0x06, - - EX_Extended1 = 0xF1, - EX_Extended2 = 0xF2, - EX_Extended3 = 0xF3, - EX_Extended4 = 0xF4, - EX_Extended5 = 0xF5, - EX_Extended6 = 0xF6, - EX_Extended7 = 0xF7, - EX_Extended8 = 0xF8, - EX_Extended9 = 0xF9, - EX_ExtendedA = 0xFA, - EX_ExtendedB = 0xFB, - EX_ExtendedC = 0xFC, - EX_ExtendedD = 0xFD, - EX_ExtendedE = 0xFE, - EX_ExtendedF = 0xFF -}; \ No newline at end of file diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 4b690efbc8..cbac457c65 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -183,7 +183,7 @@ void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) int size = arc.ArraySize(); for (int j = 0; j < size; j++) { - DThinker *thinker; + DThinker *thinker = nullptr; arc(nullptr, thinker); if (thinker != nullptr) { diff --git a/src/events.cpp b/src/events.cpp index bd2b512ce1..5a40a39835 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -776,12 +776,12 @@ void DStaticEventHandler::WorldTick() static FRenderEvent E_SetupRenderEvent() { FRenderEvent e; - e.ViewPos = ::ViewPos; - e.ViewAngle = ::ViewAngle; - e.ViewPitch = ::ViewPitch; - e.ViewRoll = ::ViewRoll; - e.FracTic = ::r_TicFracF; - e.Camera = ::camera; + e.ViewPos = r_viewpoint.Pos; + e.ViewAngle = r_viewpoint.Angles.Yaw; + e.ViewPitch = r_viewpoint.Angles.Pitch; + e.ViewRoll = r_viewpoint.Angles.Roll; + e.FracTic = r_viewpoint.TicFrac; + e.Camera = r_viewpoint.camera; return e; } diff --git a/src/f_wipe.cpp b/src/f_wipe.cpp index a3ceb8d508..95f170a85a 100644 --- a/src/f_wipe.cpp +++ b/src/f_wipe.cpp @@ -28,6 +28,9 @@ #include "f_wipe.h" #include "c_cvars.h" #include "templates.h" +#include "v_palette.h" + +EXTERN_CVAR(Bool, r_blendmethod) // // SCREEN WIPE PACKAGE @@ -42,7 +45,7 @@ static int *y; // [RH] Fire Wipe #define FIREWIDTH 64 #define FIREHEIGHT 64 -static BYTE *burnarray; +static uint8_t *burnarray; static int density; static int burntime; @@ -78,7 +81,7 @@ bool wipe_initMelt (int ticks) int i, r; // copy start screen to main screen - screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start); + screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_start); // makes this wipe faster (in theory) // to have stuff in column-major format @@ -162,21 +165,21 @@ bool wipe_exitMelt (int ticks) bool wipe_initBurn (int ticks) { - burnarray = new BYTE[FIREWIDTH * (FIREHEIGHT+5)]; + burnarray = new uint8_t[FIREWIDTH * (FIREHEIGHT+5)]; memset (burnarray, 0, FIREWIDTH * (FIREHEIGHT+5)); density = 4; burntime = 0; return 0; } -int wipe_CalcBurn (BYTE *burnarray, int width, int height, int density) +int wipe_CalcBurn (uint8_t *burnarray, int width, int height, int density) { // This is a modified version of the fire that was once used // on the player setup menu. static int voop; int a, b; - BYTE *from; + uint8_t *from; // generator from = &burnarray[width * height]; @@ -195,10 +198,10 @@ int wipe_CalcBurn (BYTE *burnarray, int width, int height, int density) from = burnarray; for (b = 0; b <= height; b += 2) { - BYTE *pixel = from; + uint8_t *pixel = from; // special case: first pixel on line - BYTE *p = pixel + (width << 1); + uint8_t *p = pixel + (width << 1); unsigned int top = *p + *(p + width - 1) + *(p + 1); unsigned int bottom = *(pixel + (width << 2)); unsigned int c1 = (top + bottom) >> 2; @@ -271,48 +274,89 @@ bool wipe_doBurn (int ticks) // Draw the screen int xstep, ystep, firex, firey; int x, y; - BYTE *to, *fromold, *fromnew; + uint8_t *to, *fromold, *fromnew; const int SHIFT = 16; xstep = (FIREWIDTH << SHIFT) / SCREENWIDTH; ystep = (FIREHEIGHT << SHIFT) / SCREENHEIGHT; to = screen->GetBuffer(); - fromold = (BYTE *)wipe_scr_start; - fromnew = (BYTE *)wipe_scr_end; + fromold = (uint8_t *)wipe_scr_start; + fromnew = (uint8_t *)wipe_scr_end; - for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep) + if (!r_blendmethod) { - for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep) + for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep) { - int fglevel; + for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep) + { + int fglevel; - fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2; - if (fglevel >= 63) - { - to[x] = fromnew[x]; - } - else if (fglevel == 0) - { - to[x] = fromold[x]; - done = false; - } - else - { - int bglevel = 64-fglevel; - DWORD *fg2rgb = Col2RGB8[fglevel]; - DWORD *bg2rgb = Col2RGB8[bglevel]; - DWORD fg = fg2rgb[fromnew[x]]; - DWORD bg = bg2rgb[fromold[x]]; - fg = (fg+bg) | 0x1f07c1f; - to[x] = RGB32k.All[fg & (fg>>15)]; - done = false; + fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2; + if (fglevel >= 63) + { + to[x] = fromnew[x]; + } + else if (fglevel == 0) + { + to[x] = fromold[x]; + done = false; + } + else + { + int bglevel = 64-fglevel; + uint32_t *fg2rgb = Col2RGB8[fglevel]; + uint32_t *bg2rgb = Col2RGB8[bglevel]; + uint32_t fg = fg2rgb[fromnew[x]]; + uint32_t bg = bg2rgb[fromold[x]]; + fg = (fg+bg) | 0x1f07c1f; + to[x] = RGB32k.All[fg & (fg>>15)]; + done = false; + } } + fromold += SCREENWIDTH; + fromnew += SCREENWIDTH; + to += SCREENPITCH; } - fromold += SCREENWIDTH; - fromnew += SCREENWIDTH; - to += SCREENPITCH; - } + } + else + { + for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep) + { + for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep) + { + int fglevel; + + fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2; + if (fglevel >= 63) + { + to[x] = fromnew[x]; + } + else if (fglevel == 0) + { + to[x] = fromold[x]; + done = false; + } + else + { + int bglevel = 64-fglevel; + + const PalEntry* pal = GPalette.BaseColors; + + uint32_t fg = fromnew[x]; + uint32_t bg = fromold[x]; + int r = MIN((pal[fg].r * fglevel + pal[bg].r * bglevel) >> 8, 63); + int g = MIN((pal[fg].g * fglevel + pal[bg].g * bglevel) >> 8, 63); + int b = MIN((pal[fg].b * fglevel + pal[bg].b * bglevel) >> 8, 63); + to[x] = RGB256k.RGB[r][g][b]; + done = false; + } + } + fromold += SCREENWIDTH; + fromnew += SCREENWIDTH; + to += SCREENPITCH; + } + } return done || (burntime > 40); } @@ -335,31 +379,53 @@ bool wipe_doFade (int ticks) fade += ticks * 2; if (fade > 64) { - screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_end); + screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_end); return true; } else { int x, y; int bglevel = 64 - fade; - DWORD *fg2rgb = Col2RGB8[fade]; - DWORD *bg2rgb = Col2RGB8[bglevel]; - BYTE *fromnew = (BYTE *)wipe_scr_end; - BYTE *fromold = (BYTE *)wipe_scr_start; - BYTE *to = screen->GetBuffer(); + uint32_t *fg2rgb = Col2RGB8[fade]; + uint32_t *bg2rgb = Col2RGB8[bglevel]; + uint8_t *fromnew = (uint8_t *)wipe_scr_end; + uint8_t *fromold = (uint8_t *)wipe_scr_start; + uint8_t *to = screen->GetBuffer(); + const PalEntry *pal = GPalette.BaseColors; - for (y = 0; y < SCREENHEIGHT; y++) + if (!r_blendmethod) { - for (x = 0; x < SCREENWIDTH; x++) + for (y = 0; y < SCREENHEIGHT; y++) { - DWORD fg = fg2rgb[fromnew[x]]; - DWORD bg = bg2rgb[fromold[x]]; - fg = (fg+bg) | 0x1f07c1f; - to[x] = RGB32k.All[fg & (fg>>15)]; + for (x = 0; x < SCREENWIDTH; x++) + { + uint32_t fg = fg2rgb[fromnew[x]]; + uint32_t bg = bg2rgb[fromold[x]]; + fg = (fg+bg) | 0x1f07c1f; + to[x] = RGB32k.All[fg & (fg>>15)]; + } + fromnew += SCREENWIDTH; + fromold += SCREENWIDTH; + to += SCREENPITCH; + } + } + else + { + for (y = 0; y < SCREENHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH; x++) + { + uint32_t fg = fromnew[x]; + uint32_t bg = fromold[x]; + int r = MIN((pal[fg].r * (64-bglevel) + pal[bg].r * bglevel) >> 8, 63); + int g = MIN((pal[fg].g * (64-bglevel) + pal[bg].g * bglevel) >> 8, 63); + int b = MIN((pal[fg].b * (64-bglevel) + pal[bg].b * bglevel) >> 8, 63); + to[x] = RGB256k.RGB[r][g][b]; + } + fromnew += SCREENWIDTH; + fromold += SCREENWIDTH; + to += SCREENPITCH; } - fromnew += SCREENWIDTH; - fromold += SCREENWIDTH; - to += SCREENPITCH; } } return false; @@ -382,12 +448,15 @@ static bool (*wipes[])(int) = // Returns true if the wipe should be performed. bool wipe_StartScreen (int type) { + if (screen->IsBgra()) + return false; + CurrentWipeType = clamp(type, 0, wipe_NUMWIPES - 1); if (CurrentWipeType) { wipe_scr_start = new short[SCREENWIDTH * SCREENHEIGHT / 2]; - screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start); + screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_start); return true; } return false; @@ -395,11 +464,15 @@ bool wipe_StartScreen (int type) void wipe_EndScreen (void) { + if (screen->IsBgra()) + return; + if (CurrentWipeType) { wipe_scr_end = new short[SCREENWIDTH * SCREENHEIGHT / 2]; - screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_end); - screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start); // restore start scr. + screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_end); + screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_start); // restore start scr. + // Initialize the wipe (*wipes[(CurrentWipeType-1)*3])(0); } @@ -410,6 +483,9 @@ bool wipe_ScreenWipe (int ticks) { bool rc; + if (screen->IsBgra()) + return true; + if (CurrentWipeType == wipe_None) return true; @@ -423,6 +499,9 @@ bool wipe_ScreenWipe (int ticks) // Final things for the wipe void wipe_Cleanup() { + if (screen->IsBgra()) + return; + if (wipe_scr_start != NULL) { delete[] wipe_scr_start; diff --git a/src/f_wipe.h b/src/f_wipe.h index 8f8bed7fef..0f94e6a065 100644 --- a/src/f_wipe.h +++ b/src/f_wipe.h @@ -34,7 +34,7 @@ void wipe_Cleanup (); // The buffer must have an additional 5 rows not included in height // to use for a seeding area. -int wipe_CalcBurn (BYTE *buffer, int width, int height, int density); +int wipe_CalcBurn (uint8_t *buffer, int width, int height, int density); enum { diff --git a/src/files.cpp b/src/files.cpp index 527fa47495..0de13e8ea8 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -33,9 +33,7 @@ ** */ -#ifdef _WIN32 -#define USE_WINDOWS_DWORD -#endif +// This also pulls in windows.h #include "LzmaDec.h" #include "files.h" @@ -392,7 +390,7 @@ ISzAlloc g_Alloc = { SzAlloc, SzFree }; FileReaderLZMA::FileReaderLZMA (FileReader &file, size_t uncompressed_size, bool zip) : File(file), SawEOF(false) { - BYTE header[4 + LZMA_PROPS_SIZE]; + uint8_t header[4 + LZMA_PROPS_SIZE]; int err; assert(zip == true); diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 51c1dd05bc..580080d9de 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -71,6 +71,7 @@ #include "r_utility.h" #include "math/cmath.h" #include "g_levellocals.h" +#include "actorinlines.h" static FRandom pr_script("FScript"); diff --git a/src/g_game.cpp b/src/g_game.cpp index 1a5eb2026f..11c5210e52 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -45,7 +45,6 @@ #include "m_random.h" #include "m_crc32.h" #include "i_system.h" -#include "i_input.h" #include "p_saveg.h" #include "p_tick.h" #include "d_main.h" @@ -234,7 +233,7 @@ FString shotfile; AActor* bodyque[BODYQUESIZE]; int bodyqueslot; -void R_ExecuteSetViewSize (void); +void R_ExecuteSetViewSize (FViewWindow &viewwindow); FString savename; FString BackupSaveName; diff --git a/src/g_input.h b/src/g_input.h new file mode 100644 index 0000000000..1a998014bd --- /dev/null +++ b/src/g_input.h @@ -0,0 +1,9 @@ +#pragma once + +// These were in i_input.h, which differed between platforms and on Windows caused problems with its +// inclusion of system specific data, so it has been separated into this platform independent file. +void I_PutInClipboard (const char *str); +FString I_GetFromClipboard (bool use_primary_selection); +void I_SetMouseCapture(); +void I_ReleaseMouseCapture(); + diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index eb9731ddcf..cbcb2d9cd1 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -9,7 +9,16 @@ class player_t; class FConfigFile; -struct visstyle_t; + +// This encapsulates the fields of vissprite_t that can be altered by AlterWeaponSprite +struct visstyle_t +{ + bool Invert; + float Alpha; + ERenderStyle RenderStyle; +}; + + /************************************************************************/ /* Class definitions */ diff --git a/src/g_level.cpp b/src/g_level.cpp index d64bbd081e..f2ca8aea15 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -91,6 +91,7 @@ #include "g_hub.h" #include "g_levellocals.h" +#include "actorinlines.h" #include @@ -1389,7 +1390,7 @@ void G_InitLevelLocals () level_info_t *info; BaseBlendA = 0.0f; // Remove underwater blend effect, if any - NormalLight.Maps = realcolormaps; + NormalLight.Maps = realcolormaps.Maps; // [BB] Instead of just setting the color, we also have to reset Desaturate and build the lights. NormalLight.ChangeColor (PalEntry (255, 255, 255), 0); @@ -1417,6 +1418,7 @@ void G_InitLevelLocals () R_SetDefaultColormap (info->FadeTable); if (strnicmp (info->FadeTable, "COLORMAP", 8) != 0) { + level.fadeto = 0xff939393; //[SP] Hexen True-color compatibility, just use gray. level.flags |= LEVEL_HASFADETABLE; } /* @@ -1484,7 +1486,7 @@ bool FLevelLocals::IsJumpingAllowed() const return false; if (dmflags & DF_YES_JUMP) return true; - return !(level.flags & LEVEL_JUMP_NO); + return !(flags & LEVEL_JUMP_NO); } //========================================================================== @@ -1498,7 +1500,7 @@ bool FLevelLocals::IsCrouchingAllowed() const return false; if (dmflags & DF_YES_CROUCH) return true; - return !(level.flags & LEVEL_CROUCH_NO); + return !(flags & LEVEL_CROUCH_NO); } //========================================================================== @@ -1512,7 +1514,7 @@ bool FLevelLocals::IsFreelookAllowed() const return false; if (dmflags & DF_YES_FREELOOK) return true; - return !(level.flags & LEVEL_FREELOOK_NO); + return !(flags & LEVEL_FREELOOK_NO); } //========================================================================== diff --git a/src/g_level.h b/src/g_level.h index 0f8906b6a1..59f396bd8d 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -35,16 +35,12 @@ #define __G_LEVEL_H__ #include "doomtype.h" -#include "doomdef.h" #include "sc_man.h" -#include "s_sound.h" -#include "p_acs.h" -#include "textures/textures.h" #include "resourcefiles/file_zip.h" struct level_info_t; struct cluster_info_t; -class FScanner; +class FSerializer; #if defined(_MSC_VER) #pragma section(".yreg$u",read) @@ -55,6 +51,24 @@ class FScanner; #define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used)) #endif +// The structure used to control scripts between maps +struct acsdefered_t +{ + enum EType + { + defexecute, + defexealways, + defsuspend, + defterminate + } type; + int script; + int args[3]; + int playernum; +}; + +FSerializer &Serialize(FSerializer &arc, const char *key, acsdefered_t &defer, acsdefered_t *def); + + struct FIntermissionDescriptor; struct FIntermissionAction; @@ -175,7 +189,7 @@ enum ELevelFlags : unsigned int LEVEL_CHANGEMAPCHEAT = 0x40000000, // Don't display cluster messages LEVEL_VISITED = 0x80000000, // Used for intermission map - // The flags QWORD is now split into 2 DWORDs + // The flags uint64_t is now split into 2 DWORDs LEVEL2_RANDOMPLAYERSTARTS = 0x00000001, // Select single player starts randomnly (no voodoo dolls) LEVEL2_ALLMAP = 0x00000002, // The player picked up a map on this level @@ -223,7 +237,8 @@ enum ELevelFlags : unsigned int // More flags! LEVEL3_FORCEFAKECONTRAST = 0x00000001, // forces fake contrast even with fog enabled - LEVEL3_REMOVEITEMS = 0x00000002, // kills all INVBAR items on map change. + LEVEL3_REMOVEITEMS = 0x00000002, // kills all INVBAR items on map change. + LEVEL3_ATTENUATE = 0x00000004, // attenuate lights? }; @@ -339,7 +354,7 @@ struct level_info_t TArray specialactions; - TArray PrecacheSounds; + TArray PrecacheSounds; TArray PrecacheTextures; TArray PrecacheClasses; diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 4019064366..98ccdc3e88 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -2,6 +2,7 @@ #include "g_level.h" #include "r_defs.h" +#include "portal.h" struct FLevelLocals { @@ -136,3 +137,56 @@ inline int sector_t::GetOppositePortalGroup(int plane) { return level.sectorPortals[Portals[plane]].mDestination->PortalGroup; } + +inline bool sector_t::PortalBlocksView(int plane) +{ + if (GetPortalType(plane) != PORTS_LINKEDPORTAL) return false; + return !!(planes[plane].Flags & (PLANEF_NORENDER | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); +} + +inline bool sector_t::PortalBlocksSight(int plane) +{ + return PLANEF_LINKED != (planes[plane].Flags & (PLANEF_NORENDER | PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED | PLANEF_LINKED)); +} + +inline bool sector_t::PortalBlocksMovement(int plane) +{ + return PLANEF_LINKED != (planes[plane].Flags & (PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED | PLANEF_LINKED)); +} + +inline bool sector_t::PortalBlocksSound(int plane) +{ + return PLANEF_LINKED != (planes[plane].Flags & (PLANEF_BLOCKSOUND | PLANEF_DISABLED | PLANEF_OBSTRUCTED | PLANEF_LINKED)); +} + +inline bool sector_t::PortalIsLinked(int plane) +{ + return (GetPortalType(plane) == PORTS_LINKEDPORTAL); +} + +inline FLinePortal *line_t::getPortal() const +{ + return portalindex >= linePortals.Size() ? (FLinePortal*)NULL : &linePortals[portalindex]; +} + +// returns true if the portal is crossable by actors +inline bool line_t::isLinePortal() const +{ + return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_PASSABLE); +} + +// returns true if the portal needs to be handled by the renderer +inline bool line_t::isVisualPortal() const +{ + return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_VISIBLE); +} + +inline line_t *line_t::getPortalDestination() const +{ + return portalindex >= linePortals.Size() ? (line_t*)NULL : linePortals[portalindex].mDestination; +} + +inline int line_t::getPortalAlignment() const +{ + return portalindex >= linePortals.Size() ? 0 : linePortals[portalindex].mAlign; +} diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 231d376eb8..0bead4ed80 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1216,7 +1216,6 @@ DEFINE_MAP_OPTION(hazardflash, true) info->hazardflash = V_GetColor(NULL, parse.sc); } - //========================================================================== // // All flag based map options diff --git a/src/g_pch.h b/src/g_pch.h index e5653796ba..cad5a84098 100644 --- a/src/g_pch.h +++ b/src/g_pch.h @@ -21,3 +21,10 @@ #include #include #include +#include +#include + +// These two headers get included nearly everywhere so it doesn't matter if changing them forces a few more recompiles. +// The overall savings from PCHing them are more significant. +#include "tarray.h" +#include "zstring.h" \ No newline at end of file diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp similarity index 97% rename from src/gl/dynlights/a_dynlight.cpp rename to src/g_shared/a_dynlight.cpp index bd25752165..9400826f6c 100644 --- a/src/gl/dynlights/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -72,6 +72,7 @@ #include "doomstat.h" #include "serializer.h" #include "g_levellocals.h" +#include "actorinlines.h" #include "gl/renderer/gl_renderer.h" @@ -81,8 +82,14 @@ #include "gl/utility/gl_templates.h" #include "gl/system//gl_interface.h" -EXTERN_CVAR(Int, vid_renderer) +CUSTOM_CVAR (Bool, gl_lights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self) AActor::RecreateAllAttachedLights(); + else AActor::DeleteAllAttachedLights(); +} + +CVAR (Bool, gl_attachedlights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); //========================================================================== // @@ -233,10 +240,6 @@ void ADynamicLight::Deactivate(AActor *activator) //========================================================================== void ADynamicLight::Tick() { - if (vid_renderer == 0) - { - return; - } if (IsOwned()) { if (!target || !target->state) @@ -748,11 +751,11 @@ void ADynamicLight::UnlinkLight () if (owned && target != NULL) { // Delete reference in owning actor - for(int c=target->dynamiclights.Size()-1; c>=0; c--) + for(int c=target->AttachedLights.Size()-1; c>=0; c--) { - if (target->dynamiclights[c] == this) + if (target->AttachedLights[c] == this) { - target->dynamiclights.Delete(c); + target->AttachedLights.Delete(c); break; } } @@ -769,23 +772,6 @@ void ADynamicLight::OnDestroy() } -//========================================================================== -// -// Needed for garbage collection -// -//========================================================================== - -size_t AActor::PropagateMark() -{ - for (unsigned i=0; i LightDefaults; +TDeletingArray LightDefaults; //----------------------------------------------------------------------------- // @@ -205,7 +199,7 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const else light->m_cycler.SetParams(float(light->args[LIGHT_INTENSITY]), float(light->args[LIGHT_SECONDARY_INTENSITY]), pulseTime, oldtype == PulseLight); light->m_cycler.ShouldCycle(true); light->m_cycler.SetCycleType(CYCLE_Sin); - light->m_currentRadius = light->m_cycler.GetVal(); + light->m_currentRadius = (float)light->m_cycler.GetVal(); if (light->m_currentRadius <= 0) light->m_currentRadius = 1; light->swapped = m_swapped; } @@ -214,7 +208,7 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const { case 0: light->flags4 &= ~MF4_ATTENUATE; break; case 1: light->flags4 |= MF4_ATTENUATE; break; - default: if (glset.attenuate) light->flags4 |= MF4_ATTENUATE; else light->flags4 &= ~MF4_ATTENUATE; break; + default: if (level.flags3 & LEVEL3_ATTENUATE) light->flags4 |= MF4_ATTENUATE; else light->flags4 &= ~MF4_ATTENUATE; break; } } @@ -277,7 +271,7 @@ extern int ScriptDepth; // //========================================================================== -inline float gl_ParseFloat(FScanner &sc) +inline float ParseFloat(FScanner &sc) { sc.GetFloat(); @@ -285,7 +279,7 @@ inline float gl_ParseFloat(FScanner &sc) } -inline int gl_ParseInt(FScanner &sc) +inline int ParseInt(FScanner &sc) { sc.GetNumber(); @@ -293,7 +287,7 @@ inline int gl_ParseInt(FScanner &sc) } -inline char *gl_ParseString(FScanner &sc) +inline char *ParseString(FScanner &sc) { sc.GetString(); @@ -301,16 +295,16 @@ inline char *gl_ParseString(FScanner &sc) } -void gl_ParseTriple(FScanner &sc, float floatVal[3]) +static void ParseTriple(FScanner &sc, float floatVal[3]) { for (int i = 0; i < 3; i++) { - floatVal[i] = gl_ParseFloat(sc); + floatVal[i] = ParseFloat(sc); } } -void gl_AddLightDefaults(FLightDefaults *defaults) +static void AddLightDefaults(FLightDefaults *defaults) { FLightDefaults *temp; unsigned int i; @@ -327,7 +321,8 @@ void gl_AddLightDefaults(FLightDefaults *defaults) } } - if (gl.legacyMode && (defaults->GetAttenuate())) + // If the current renderer cannot handle attenuated lights we need to reduce the radius here to account for the far more bright lights this would create. + if (/*!Renderer->CanAttenuate() &&*/ (defaults->GetAttenuate())) { defaults->SetArg(LIGHT_INTENSITY, defaults->GetArg(LIGHT_INTENSITY) * 2 / 3); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, defaults->GetArg(LIGHT_SECONDARY_INTENSITY) * 2 / 3); @@ -343,7 +338,7 @@ void gl_AddLightDefaults(FLightDefaults *defaults) // //----------------------------------------------------------------------------- -void gl_ParsePointLight(FScanner &sc) +static void ParsePointLight(FScanner &sc) { int type; float floatTriple[3]; @@ -373,39 +368,39 @@ void gl_ParsePointLight(FScanner &sc) ScriptDepth--; break; case LIGHTTAG_COLOR: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SIZE: - intVal = clamp(gl_ParseInt(sc), 1, 1024); + intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_INTENSITY, intVal); break; case LIGHTTAG_SUBTRACTIVE: - defaults->SetSubtractive(gl_ParseInt(sc) != 0); + defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_ADDITIVE: - defaults->SetAdditive(gl_ParseInt(sc) != 0); + defaults->SetAdditive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: - defaults->SetHalo(gl_ParseInt(sc) != 0); + defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: - defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: - defaults->SetAttenuate(gl_ParseInt(sc) != 0); + defaults->SetAttenuate(ParseInt(sc) != 0); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } - gl_AddLightDefaults(defaults); + AddLightDefaults(defaults); } else { @@ -420,7 +415,7 @@ void gl_ParsePointLight(FScanner &sc) // //----------------------------------------------------------------------------- -void gl_ParsePulseLight(FScanner &sc) +static void ParsePulseLight(FScanner &sc) { int type; float floatVal, floatTriple[3]; @@ -450,38 +445,38 @@ void gl_ParsePulseLight(FScanner &sc) ScriptDepth--; break; case LIGHTTAG_COLOR: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SIZE: - intVal = clamp(gl_ParseInt(sc), 1, 1024); + intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_INTENSITY, intVal); break; case LIGHTTAG_SECSIZE: - intVal = clamp(gl_ParseInt(sc), 1, 1024); + intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); break; case LIGHTTAG_INTERVAL: - floatVal = gl_ParseFloat(sc); + floatVal = ParseFloat(sc); defaults->SetParameter(floatVal * TICRATE); break; case LIGHTTAG_SUBTRACTIVE: - defaults->SetSubtractive(gl_ParseInt(sc) != 0); + defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: - defaults->SetHalo(gl_ParseInt(sc) != 0); + defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: - defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: - defaults->SetAttenuate(gl_ParseInt(sc) != 0); + defaults->SetAttenuate(ParseInt(sc) != 0); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); @@ -489,7 +484,7 @@ void gl_ParsePulseLight(FScanner &sc) } defaults->OrderIntensities(); - gl_AddLightDefaults(defaults); + AddLightDefaults(defaults); } else { @@ -504,7 +499,7 @@ void gl_ParsePulseLight(FScanner &sc) // //----------------------------------------------------------------------------- -void gl_ParseFlickerLight(FScanner &sc) +void ParseFlickerLight(FScanner &sc) { int type; float floatVal, floatTriple[3]; @@ -534,45 +529,45 @@ void gl_ParseFlickerLight(FScanner &sc) ScriptDepth--; break; case LIGHTTAG_COLOR: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SIZE: - intVal = clamp(gl_ParseInt(sc), 1, 1024); + intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_INTENSITY, intVal); break; case LIGHTTAG_SECSIZE: - intVal = clamp(gl_ParseInt(sc), 1, 1024); + intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); break; case LIGHTTAG_CHANCE: - floatVal = gl_ParseFloat(sc); + floatVal = ParseFloat(sc); defaults->SetParameter(floatVal*360.); break; case LIGHTTAG_SUBTRACTIVE: - defaults->SetSubtractive(gl_ParseInt(sc) != 0); + defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: - defaults->SetHalo(gl_ParseInt(sc) != 0); + defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: - defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: - defaults->SetAttenuate(gl_ParseInt(sc) != 0); + defaults->SetAttenuate(ParseInt(sc) != 0); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } defaults->OrderIntensities(); - gl_AddLightDefaults(defaults); + AddLightDefaults(defaults); } else { @@ -587,7 +582,7 @@ void gl_ParseFlickerLight(FScanner &sc) // //----------------------------------------------------------------------------- -void gl_ParseFlickerLight2(FScanner &sc) +void ParseFlickerLight2(FScanner &sc) { int type; float floatVal, floatTriple[3]; @@ -617,38 +612,38 @@ void gl_ParseFlickerLight2(FScanner &sc) ScriptDepth--; break; case LIGHTTAG_COLOR: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SIZE: - intVal = clamp(gl_ParseInt(sc), 1, 1024); + intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_INTENSITY, intVal); break; case LIGHTTAG_SECSIZE: - intVal = clamp(gl_ParseInt(sc), 1, 1024); + intVal = clamp(ParseInt(sc), 1, 1024); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); break; case LIGHTTAG_INTERVAL: - floatVal = gl_ParseFloat(sc); + floatVal = ParseFloat(sc); defaults->SetParameter(floatVal * 360.); break; case LIGHTTAG_SUBTRACTIVE: - defaults->SetSubtractive(gl_ParseInt(sc) != 0); + defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: - defaults->SetHalo(gl_ParseInt(sc) != 0); + defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: - defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: - defaults->SetAttenuate(gl_ParseInt(sc) != 0); + defaults->SetAttenuate(ParseInt(sc) != 0); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); @@ -660,7 +655,7 @@ void gl_ParseFlickerLight2(FScanner &sc) defaults->SetArg(LIGHT_SECONDARY_INTENSITY, defaults->GetArg(LIGHT_INTENSITY)); defaults->SetArg(LIGHT_INTENSITY, v); } - gl_AddLightDefaults(defaults); + AddLightDefaults(defaults); } else { @@ -675,7 +670,7 @@ void gl_ParseFlickerLight2(FScanner &sc) // //----------------------------------------------------------------------------- -void gl_ParseSectorLight(FScanner &sc) +static void ParseSectorLight(FScanner &sc) { int type; float floatVal; @@ -705,36 +700,36 @@ void gl_ParseSectorLight(FScanner &sc) ScriptDepth--; break; case LIGHTTAG_COLOR: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: - gl_ParseTriple(sc, floatTriple); + ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SCALE: - floatVal = gl_ParseFloat(sc); + floatVal = ParseFloat(sc); defaults->SetArg(LIGHT_SCALE, clamp((int)(floatVal * 255), 1, 1024)); break; case LIGHTTAG_SUBTRACTIVE: - defaults->SetSubtractive(gl_ParseInt(sc) != 0); + defaults->SetSubtractive(ParseInt(sc) != 0); break; case LIGHTTAG_HALO: - defaults->SetHalo(gl_ParseInt(sc) != 0); + defaults->SetHalo(ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: - defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + defaults->SetDontLightSelf(ParseInt(sc) != 0); break; case LIGHTTAG_ATTENUATE: - defaults->SetAttenuate(gl_ParseInt(sc) != 0); + defaults->SetAttenuate(ParseInt(sc) != 0); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } - gl_AddLightDefaults(defaults); + AddLightDefaults(defaults); } else { @@ -749,7 +744,7 @@ void gl_ParseSectorLight(FScanner &sc) // //----------------------------------------------------------------------------- -void gl_AddLightAssociation(const char *actor, const char *frame, const char *light) +static void AddLightAssociation(const char *actor, const char *frame, const char *light) { FLightAssociation *temp; unsigned int i; @@ -778,7 +773,7 @@ void gl_AddLightAssociation(const char *actor, const char *frame, const char *li // //----------------------------------------------------------------------------- -void gl_ParseFrame(FScanner &sc, FString name) +static void ParseFrame(FScanner &sc, FString name) { int type, startDepth; FString frameName; @@ -811,8 +806,8 @@ void gl_ParseFrame(FScanner &sc, FString name) ScriptDepth--; break; case LIGHTTAG_LIGHT: - gl_ParseString(sc); - gl_AddLightAssociation(name, frameName, sc.String); + ParseString(sc); + AddLightAssociation(name, frameName, sc.String); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); @@ -832,7 +827,7 @@ void gl_ParseFrame(FScanner &sc, FString name) // //----------------------------------------------------------------------------- -void gl_ParseObject(FScanner &sc) +void ParseObject(FScanner &sc) { int type; FString name; @@ -840,7 +835,7 @@ void gl_ParseObject(FScanner &sc) // get name sc.GetString(); name = sc.String; - if (!PClass::FindClass(name)) + if (!PClass::FindActor(name)) sc.ScriptMessage("Warning: dynamic lights attached to non-existent actor %s\n", name.GetChars()); // check for opening brace @@ -861,7 +856,7 @@ void gl_ParseObject(FScanner &sc) ScriptDepth--; break; case LIGHTTAG_FRAME: - gl_ParseFrame(sc, name); + ParseFrame(sc, name); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); @@ -875,25 +870,6 @@ void gl_ParseObject(FScanner &sc) } -//----------------------------------------------------------------------------- -// -// -// -//----------------------------------------------------------------------------- - -void gl_ReleaseLights() -{ - unsigned int i; - - for (i = 0; i < LightDefaults.Size(); i++) - { - delete LightDefaults[i]; - } - - LightAssociations.Clear(); - LightDefaults.Clear(); -} - //========================================================================== // // @@ -950,7 +926,7 @@ enum // There is no functionality for this stuff! // //========================================================================== -bool gl_ParseShader(FScanner &sc) +bool ParseShader(FScanner &sc) { int ShaderDepth = 0; @@ -983,8 +959,6 @@ bool gl_ParseShader(FScanner &sc) // // Light associations per actor class // -// Turn this inefficient mess into something that can be used at run time. -// //========================================================================== class FInternalLightAssociation @@ -1039,20 +1013,6 @@ FInternalLightAssociation::FInternalLightAssociation(FLightAssociation * asso) } } - -//========================================================================== -// -// -// -//========================================================================== - -inline TDeletingArray * gl_GetActorLights(AActor * actor) -{ - return (TDeletingArray*)actor->lightassociations; -} - -TDeletingArray< TDeletingArray * > AssoDeleter; - //========================================================================== // // State lights @@ -1069,7 +1029,7 @@ TArray StateLights; // //========================================================================== -void gl_InitializeActorLights() +void InitializeActorLights() { for(unsigned int i=0;ilightassociations) - { - TDeletingArray *p =new TDeletingArray; - defaults->lightassociations = p; - AssoDeleter.Push(p); - } - TDeletingArray * lights = gl_GetActorLights(defaults); - if (iasso->Light()==NULL) - { - // The definition was not valid. - delete iasso; - } - else - { - lights->Push(iasso); - } - } + // put this in the class data arena so that we do not have to worry about deleting it ourselves. + void *mem = ClassDataAllocator.Alloc(sizeof(FInternalLightAssociation)); + FInternalLightAssociation * iasso = new(mem) FInternalLightAssociation(&LightAssociations[i]); + if (iasso->Light() != nullptr) + ti->LightAssociations.Push(iasso); } } // we don't need the parser data for the light associations anymore @@ -1134,24 +1077,23 @@ void gl_InitializeActorLights() // //========================================================================== -void gl_AttachLight(AActor *actor, unsigned int count, const FLightDefaults *lightdef) +void AActor::AttachLight(unsigned int count, const FLightDefaults *lightdef) { ADynamicLight *light; - // I'm skipping the single rotations because that really doesn't make sense! - if (count < actor->dynamiclights.Size()) + if (count < AttachedLights.Size()) { - light = barrier_cast(actor->dynamiclights[count]); + light = barrier_cast(AttachedLights[count]); assert(light != NULL); } else { - light = Spawn(actor->Pos(), NO_REPLACE); - light->target = actor; + light = Spawn(Pos(), NO_REPLACE); + light->target = this; light->owned = true; light->ObjectFlags |= OF_Transient; //light->flags4 |= MF4_ATTENUATE; - actor->dynamiclights.Push(light); + AttachedLights.Push(light); } light->flags2&=~MF2_DORMANT; lightdef->ApplyProperties(light); @@ -1163,51 +1105,59 @@ void gl_AttachLight(AActor *actor, unsigned int count, const FLightDefaults *lig // //========================================================================== -void gl_SetActorLights(AActor *actor) +void AActor::SetDynamicLights() { - TArray * l = gl_GetActorLights(actor); + TArray & LightAssociations = GetClass()->LightAssociations; unsigned int count = 0; - All.Clock(); - if (actor->state == NULL) return; - if (l) + if (state == NULL) return; + if (LightAssociations.Size() > 0) { - TArray & LightAssociations=*l; ADynamicLight *lights, *tmpLight; unsigned int i; - int sprite = actor->sprite; - int frame = actor->frame; - lights = tmpLight = NULL; - for (i = 0; i < LightAssociations.Size(); i++) { if (LightAssociations[i]->Sprite() == sprite && (LightAssociations[i]->Frame()==frame || LightAssociations[i]->Frame()==-1)) { - gl_AttachLight(actor, count++, LightAssociations[i]->Light()); + AttachLight(count++, LightAssociations[i]->Light()); } } } - if (count == 0 && actor->state->Light > 0) + if (count == 0 && state->Light > 0) { - for(int i= actor->state->Light; StateLights[i] != NULL; i++) + for(int i= state->Light; StateLights[i] != NULL; i++) { if (StateLights[i] != (FLightDefaults*)-1) { - gl_AttachLight(actor, count++, StateLights[i]); + AttachLight(count++, StateLights[i]); } } } - for(;countdynamiclights.Size();count++) + for(;countdynamiclights[count]->flags2 |= MF2_DORMANT; - memset(actor->dynamiclights[count]->args, 0, 3*sizeof(actor->args[0])); + AttachedLights[count]->flags2 |= MF2_DORMANT; + memset(AttachedLights[count]->args, 0, 3*sizeof(args[0])); } - All.Unclock(); +} + +//========================================================================== +// +// Needed for garbage collection +// +//========================================================================== + +size_t AActor::PropagateMark() +{ + for (unsigned i = 0; i it; AActor * a; @@ -1224,7 +1174,7 @@ void gl_DeleteAllAttachedLights() while ((a=it.Next())) { - a->dynamiclights.Clear(); + a->AttachedLights.Clear(); } TThinkerIterator it2; @@ -1236,8 +1186,6 @@ void gl_DeleteAllAttachedLights() if (l->owned) l->Destroy(); l=ll; } - - } //========================================================================== @@ -1246,14 +1194,14 @@ void gl_DeleteAllAttachedLights() // //========================================================================== -void gl_RecreateAllAttachedLights() +void AActor::RecreateAllAttachedLights() { TThinkerIterator it; AActor * a; while ((a=it.Next())) { - gl_SetActorLights(a); + a->SetDynamicLights(); } } @@ -1264,7 +1212,7 @@ void gl_RecreateAllAttachedLights() // by LoadDynLightDefs, which wasn't simply integrated into ParseDefs // because of the way the code needs to load two out of five lumps. //========================================================================== -void gl_DoParseDefs(FScanner &sc, int workingLump) +static void DoParseDefs(FScanner &sc, int workingLump) { int recursion=0; int lump, type; @@ -1289,32 +1237,32 @@ void gl_DoParseDefs(FScanner &sc, int workingLump) sc.ScriptError("Lump '%s' not found", sc.String); FScanner newscanner(lump); - gl_DoParseDefs(newscanner, lump); + DoParseDefs(newscanner, lump); break; } case LIGHT_POINT: - gl_ParsePointLight(sc); + ParsePointLight(sc); break; case LIGHT_PULSE: - gl_ParsePulseLight(sc); + ParsePulseLight(sc); break; case LIGHT_FLICKER: - gl_ParseFlickerLight(sc); + ParseFlickerLight(sc); break; case LIGHT_FLICKER2: - gl_ParseFlickerLight2(sc); + ParseFlickerLight2(sc); break; case LIGHT_SECTOR: - gl_ParseSectorLight(sc); + ParseSectorLight(sc); break; case LIGHT_OBJECT: - gl_ParseObject(sc); + ParseObject(sc); break; case LIGHT_CLEAR: - gl_ReleaseLights(); + // This has been intentionally removed break; case TAG_SHADER: - gl_ParseShader(sc); + ParseShader(sc); break; case TAG_CLEARSHADERS: break; @@ -1355,7 +1303,7 @@ void gl_DoParseDefs(FScanner &sc, int workingLump) // //========================================================================== -void gl_LoadGLDefs(const char *defsLump) +static void LoadGLDefs(const char *defsLump) { int workingLump, lastLump; static const char *gldefsnames[] = { "GLDEFS", defsLump, nullptr }; @@ -1364,7 +1312,7 @@ void gl_LoadGLDefs(const char *defsLump) while ((workingLump = Wads.FindLumpMulti(gldefsnames, &lastLump)) != -1) { FScanner sc(workingLump); - gl_DoParseDefs(sc, workingLump); + DoParseDefs(sc, workingLump); } } @@ -1375,12 +1323,12 @@ void gl_LoadGLDefs(const char *defsLump) // //========================================================================== -void gl_ParseDefs() +void ParseGLDefs() { const char *defsLump = NULL; - atterm( gl_ReleaseLights ); - gl_ReleaseLights(); + LightAssociations.Clear(); + LightDefaults.Clear(); gl_DestroyUserShaders(); switch (gameinfo.gametype) { @@ -1403,11 +1351,10 @@ void gl_ParseDefs() break; } gl_ParseVavoomSkybox(); - gl_LoadGLDefs(defsLump); - gl_InitializeActorLights(); + LoadGLDefs(defsLump); + InitializeActorLights(); } - //========================================================================== // // diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 6835cefeb8..feb7e62560 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -159,7 +159,7 @@ void DEarthquake::Tick () double DEarthquake::GetModWave(double waveMultiplier) const { - double time = m_Countdown - r_TicFracF; + double time = m_Countdown - r_viewpoint.TicFrac; return g_sin(waveMultiplier * time * (M_PI * 2 / TICRATE)); } diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 97b1b54d6e..f4a3d2c6eb 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -1101,13 +1101,13 @@ void DrawHUD() } else { - if (AspectTallerThanWide(WidescreenRatio)) + if (AspectTallerThanWide(r_viewwindow.WidescreenRatio)) { - hudheight = hudwidth * 30 / AspectMultiplier(WidescreenRatio); // BaseRatioSizes is inverted for this mode + hudheight = hudwidth * 30 / AspectMultiplier(r_viewwindow.WidescreenRatio); // BaseRatioSizes is inverted for this mode } else { - hudheight = hudwidth * 30 / (48*48/AspectMultiplier(WidescreenRatio)); + hudheight = hudwidth * 30 / (48*48/AspectMultiplier(r_viewwindow.WidescreenRatio)); } } } diff --git a/src/g_statusbar/sbar.h b/src/g_statusbar/sbar.h index 0e1eca144f..689bdc4ac8 100644 --- a/src/g_statusbar/sbar.h +++ b/src/g_statusbar/sbar.h @@ -38,6 +38,7 @@ #include "dobject.h" #include "v_collection.h" #include "v_text.h" +#include "r_data/renderstyle.h" class player_t; struct FRemapTable; @@ -343,9 +344,9 @@ public: DBaseStatusBar (int reltop, int hres=320, int vres=200); void OnDestroy() override; - void AttachMessage (DHUDMessage *msg, uint32 id=0, int layer=HUDMSGLayer_Default); + void AttachMessage (DHUDMessage *msg, uint32_t id=0, int layer=HUDMSGLayer_Default); DHUDMessage *DetachMessage (DHUDMessage *msg); - DHUDMessage *DetachMessage (uint32 id); + DHUDMessage *DetachMessage (uint32_t id); void DetachAllMessages (); void ShowPlayerName (); double GetDisplacement() { return Displacement; } diff --git a/src/g_statusbar/sbar_mugshot.cpp b/src/g_statusbar/sbar_mugshot.cpp index 18e0fec42a..650a37063c 100644 --- a/src/g_statusbar/sbar_mugshot.cpp +++ b/src/g_statusbar/sbar_mugshot.cpp @@ -41,6 +41,7 @@ #include "sbarinfo.h" #include "templates.h" #include "r_utility.h" +#include "actorinlines.h" #define ST_RAMPAGEDELAY (2*TICRATE) #define ST_MUCHPAIN 20 diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp index db3b5deb5a..7be5073ef1 100644 --- a/src/g_statusbar/shared_sbar.cpp +++ b/src/g_statusbar/shared_sbar.cpp @@ -611,7 +611,7 @@ void DBaseStatusBar::DrawCrosshair () ST_LoadCrosshair(); // Don't draw the crosshair if there is none - if (CrosshairImage == NULL || gamestate == GS_TITLELEVEL || camera->health <= 0) + if (CrosshairImage == NULL || gamestate == GS_TITLELEVEL || r_viewpoint.camera->health <= 0) { return; } diff --git a/src/g_statusbar/strife_sbar.cpp b/src/g_statusbar/strife_sbar.cpp index 9726574827..13410e8f86 100644 --- a/src/g_statusbar/strife_sbar.cpp +++ b/src/g_statusbar/strife_sbar.cpp @@ -33,23 +33,22 @@ class FHealthBar : public FTexture public: FHealthBar (); - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); + const uint8_t *GetColumn (unsigned int column, const Span **spans_out); + const uint8_t *GetPixels (); bool CheckModified (); - void Unload (); void SetVial (int level); protected: - BYTE Pixels[200*2]; - BYTE Colors[8]; + uint8_t Pixels[200*2]; + uint8_t Colors[8]; static const Span DummySpan[2]; int VialLevel; bool NeedRefresh; void MakeTexture (); - void FillBar (int min, int max, BYTE light, BYTE dark); + void FillBar (int min, int max, uint8_t light, uint8_t dark); }; const FTexture::Span FHealthBar::DummySpan[2] = { { 0, 2 }, { 0, 0 } }; @@ -59,7 +58,7 @@ FHealthBar::FHealthBar () { int i; - static const BYTE rgbs[8*3] = + static const uint8_t rgbs[8*3] = { 180, 228, 128, // light green 128, 180, 80, // dark green @@ -91,11 +90,7 @@ bool FHealthBar::CheckModified () return NeedRefresh; } -void FHealthBar::Unload () -{ -} - -const BYTE *FHealthBar::GetColumn (unsigned int column, const Span **spans_out) +const uint8_t *FHealthBar::GetColumn (unsigned int column, const Span **spans_out) { if (NeedRefresh) { @@ -112,7 +107,7 @@ const BYTE *FHealthBar::GetColumn (unsigned int column, const Span **spans_out) return Pixels + column*2; } -const BYTE *FHealthBar::GetPixels () +const uint8_t *FHealthBar::GetPixels () { if (NeedRefresh) { @@ -171,7 +166,7 @@ void FHealthBar::MakeTexture () } } -void FHealthBar::FillBar (int min, int max, BYTE light, BYTE dark) +void FHealthBar::FillBar (int min, int max, uint8_t light, uint8_t dark) { for (int i = min*2; i < max*2; i++) { @@ -562,7 +557,7 @@ private: int bars = (CurrentPop == POP_Status) ? imgINVPOP : imgINVPOP2; int back = (CurrentPop == POP_Status) ? imgINVPBAK : imgINVPBAK2; // Extrapolate the height of the popscreen for smoother movement - int height = clamp (PopHeight + int(r_TicFracF * PopHeightChange), -POP_HEIGHT, 0); + int height = clamp (PopHeight + int(r_viewpoint.TicFrac * PopHeightChange), -POP_HEIGHT, 0); xscale = CleanXfac; yscale = CleanYfac; @@ -613,7 +608,7 @@ private: if (KeyPopScroll > 0) { // Extrapolate the scroll position for smoother scrolling - int scroll = MAX (0,KeyPopScroll - int(r_TicFracF * (280./KEY_TIME))); + int scroll = MAX (0,KeyPopScroll - int(r_viewpoint.TicFrac * (280./KEY_TIME))); pos -= 10; leftcol = leftcol - 280 + scroll; } diff --git a/src/gi.cpp b/src/gi.cpp index ad5c5a0eea..d309020459 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -125,6 +125,21 @@ const char* GameInfoBorders[] = } \ while (sc.CheckToken(',')); \ } +#define GAMEINFOKEY_SOUNDARRAY(key, variable, length, clear) \ + else if(nextKey.CompareNoCase(variable) == 0) \ + { \ + if (clear) gameinfo.key.Clear(); \ + do \ + { \ + sc.MustGetToken(TK_StringConst); \ + if(length > 0 && strlen(sc.String) > length) \ + { \ + sc.ScriptError("Value for '%s' can not be longer than %d characters.", #key, length); \ + } \ + gameinfo.key[gameinfo.key.Reserve(1)] = FSoundID(sc.String); \ + } \ + while (sc.CheckToken(',')); \ + } #define GAMEINFOKEY_STRING(key, variable) \ else if(nextKey.CompareNoCase(variable) == 0) \ @@ -323,7 +338,7 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_STRINGARRAY(infoPages, "infoPage", 8, true) GAMEINFOKEY_STRINGARRAY(PrecachedClasses, "precacheclasses", 0, false) GAMEINFOKEY_STRINGARRAY(PrecachedTextures, "precachetextures", 0, false) - GAMEINFOKEY_STRINGARRAY(PrecachedSounds, "precachesounds", 0, false) + GAMEINFOKEY_SOUNDARRAY(PrecachedSounds, "precachesounds", 0, false) GAMEINFOKEY_STRINGARRAY(EventHandlers, "addeventhandlers", 0, true) GAMEINFOKEY_STRINGARRAY(EventHandlers, "eventhandlers", 0, false) GAMEINFOKEY_STRING(PauseSign, "pausesign") diff --git a/src/gi.h b/src/gi.h index f20ae0d997..759637dea0 100644 --- a/src/gi.h +++ b/src/gi.h @@ -36,7 +36,6 @@ #include "basictypes.h" #include "zstring.h" -#include "s_sound.h" // Flags are not user configurable and only depend on the standard IWADs #define GI_MAPxx 0x00000001 @@ -123,7 +122,7 @@ struct gameinfo_t TArray PrecachedClasses; TArray PrecachedTextures; - TArray PrecachedSounds; + TArray PrecachedSounds; TArray EventHandlers; FString titleMusic; diff --git a/src/gl/compatibility/gl_20.cpp b/src/gl/compatibility/gl_20.cpp index 7fc8b783bd..7ff43de360 100644 --- a/src/gl/compatibility/gl_20.cpp +++ b/src/gl/compatibility/gl_20.cpp @@ -38,6 +38,7 @@ #include "v_text.h" #include "r_utility.h" #include "g_levellocals.h" +#include "actorinlines.h" #include "gl/dynlights/gl_dynlight.h" #include "gl/utility/gl_geometric.h" #include "gl/renderer/gl_renderer.h" @@ -46,6 +47,7 @@ #include "gl/system/gl_cvars.h" #include "gl/renderer/gl_renderstate.h" #include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_scenedrawer.h" #include "gl/data/gl_vertexbuffer.h" @@ -370,10 +372,10 @@ void FRenderState::DrawColormapOverlay() // //========================================================================== -bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, Vector & up, Vector & right, +bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, FVector3 & nearPt, FVector3 & up, FVector3 & right, float & scale, bool checkside, bool additive) { - Vector fn, pos; + FVector3 fn, pos; DVector3 lpos = light->PosRelative(group); @@ -396,16 +398,12 @@ bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, // project light position onto plane (find closest point on plane) - pos.Set(lpos.X, lpos.Z, lpos.Y); + pos = { (float)lpos.X, (float)lpos.Z, (float)lpos.Y }; fn = p.Normal(); fn.GetRightUp(right, up); -#ifdef _MSC_VER - nearPt = pos + fn * dist; -#else - Vector tmpVec = fn * dist; + FVector3 tmpVec = fn * dist; nearPt = pos + tmpVec; -#endif float cs = 1.0f - (dist / radius); if (additive) cs *= 0.2f; // otherwise the light gets too strong. @@ -415,13 +413,11 @@ bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, if (light->IsSubtractive()) { - Vector v; - gl_RenderState.BlendEquation(GL_FUNC_REVERSE_SUBTRACT); - v.Set(r, g, b); - r = v.Length() - r; - g = v.Length() - g; - b = v.Length() - b; + float length = float(FVector3(r, g, b).Length()); + r = length - r; + g = length - g; + b = length - b; } else { @@ -517,8 +513,8 @@ void GLWall::RenderFogBoundaryCompat() // as the shader version but it's an acceptable compromise. float fogdensity = gl_GetFogDensity(lightlevel, Colormap.FadeColor, Colormap.fogdensity); - float dist1 = Dist2(ViewPos.X, ViewPos.Y, glseg.x1, glseg.y1); - float dist2 = Dist2(ViewPos.X, ViewPos.Y, glseg.x2, glseg.y2); + float dist1 = Dist2(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, glseg.x1, glseg.y1); + float dist2 = Dist2(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, glseg.x2, glseg.y2); // these values were determined by trial and error and are scale dependent! float fogd1 = (0.95f - exp(-fogdensity*dist1 / 62500.f)) * 1.05f; @@ -562,7 +558,7 @@ void GLWall::RenderFogBoundaryCompat() void GLFlat::DrawSubsectorLights(subsector_t * sub, int pass) { Plane p; - Vector nearPt, up, right, t1; + FVector3 nearPt, up, right, t1; float scale; FLightNode * node = sub->lighthead; @@ -602,11 +598,11 @@ void GLFlat::DrawSubsectorLights(subsector_t * sub, int pass) ptr->x = vt->fX(); ptr->z = plane.plane.ZatPoint(vt) + dz; ptr->y = vt->fY(); - t1.Set(ptr->x, ptr->z, ptr->y); - Vector nearToVert = t1 - nearPt; + t1 = { ptr->x, ptr->z, ptr->y }; + FVector3 nearToVert = t1 - nearPt; - ptr->u = (nearToVert.Dot(right) * scale) + 0.5f; - ptr->v = (nearToVert.Dot(up) * scale) + 0.5f; + ptr->u = ((nearToVert | right) * scale) + 0.5f; + ptr->v = ((nearToVert | up) * scale) + 0.5f; ptr++; } GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_FAN); @@ -658,10 +654,10 @@ bool GLWall::PrepareLight(ADynamicLight * light, int pass) { float vtx[] = { glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2 }; Plane p; - Vector nearPt, up, right; + FVector3 nearPt, up, right; float scale; - p.Init(vtx, 4); + p.Set(&glseg); if (!p.ValidNormal()) { @@ -673,15 +669,15 @@ bool GLWall::PrepareLight(ADynamicLight * light, int pass) return false; } - Vector t1; + FVector3 t1; int outcnt[4] = { 0,0,0,0 }; for (int i = 0; i<4; i++) { - t1.Set(&vtx[i * 3]); - Vector nearToVert = t1 - nearPt; - tcs[i].u = (nearToVert.Dot(right) * scale) + 0.5f; - tcs[i].v = (nearToVert.Dot(up) * scale) + 0.5f; + t1 = &vtx[i * 3]; + FVector3 nearToVert = t1 - nearPt; + tcs[i].u = ((nearToVert | right) * scale) + 0.5f; + tcs[i].v = ((nearToVert | up) * scale) + 0.5f; // quick check whether the light touches this polygon if (tcs[i].u<0) outcnt[0]++; @@ -755,7 +751,7 @@ void GLWall::RenderLightsCompat(int pass) // //========================================================================== -void FGLRenderer::RenderMultipassStuff() +void GLSceneDrawer::RenderMultipassStuff() { // First pass: empty background with sector light only @@ -789,7 +785,7 @@ void FGLRenderer::RenderMultipassStuff() // second pass: draw lights glDepthMask(false); - if (mLightCount && !gl_fixedcolormap) + if (GLRenderer->mLightCount && !gl_fixedcolormap) { if (gl_SetupLightTexture()) { diff --git a/src/gl/data/gl_data.cpp b/src/gl/data/gl_data.cpp index a0bbd80029..99ea87b647 100644 --- a/src/gl/data/gl_data.cpp +++ b/src/gl/data/gl_data.cpp @@ -61,7 +61,6 @@ long gl_frameCount; EXTERN_CVAR(Int, gl_lightmode) EXTERN_CVAR(Bool, gl_brightfog) EXTERN_CVAR(Bool, gl_lightadditivesurfaces) -EXTERN_CVAR(Bool, gl_attenuate) CUSTOM_CVAR(Float, maxviewpitch, 90.f, CVAR_ARCHIVE|CVAR_SERVERINFO) @@ -240,7 +239,6 @@ struct FGLROptions : public FOptionalMapinfoData skyfog = 0; brightfog = false; lightmode = -1; - attenuate = -1; nocoloredspritelighting = -1; nolightfade = false; notexturefill = -1; @@ -257,7 +255,6 @@ struct FGLROptions : public FOptionalMapinfoData newopt->outsidefogdensity = outsidefogdensity; newopt->skyfog = skyfog; newopt->lightmode = lightmode; - newopt->attenuate = attenuate; newopt->nocoloredspritelighting = nocoloredspritelighting; newopt->nolightfade = nolightfade; newopt->notexturefill = notexturefill; @@ -272,7 +269,6 @@ struct FGLROptions : public FOptionalMapinfoData int skyfog; int lightmode; int brightfog; - int8_t attenuate; int8_t lightadditivesurfaces; int8_t nocoloredspritelighting; int8_t notexturefill; @@ -378,20 +374,6 @@ DEFINE_MAP_OPTION(lightadditivesurfaces, false) } } -DEFINE_MAP_OPTION(attenuate, false) -{ - FGLROptions *opt = info->GetOptData("gl_renderer"); - if (parse.CheckAssign()) - { - parse.sc.MustGetNumber(); - opt->attenuate = !!parse.sc.Number; - } - else - { - opt->attenuate = true; - } -} - DEFINE_MAP_OPTION(skyrotate, false) { FGLROptions *opt = info->GetOptData("gl_renderer"); @@ -450,8 +432,6 @@ static void ResetOpts() else glset.brightfog = !!glset.map_brightfog; if (glset.map_lightadditivesurfaces == -1) glset.lightadditivesurfaces = gl_lightadditivesurfaces; else glset.lightadditivesurfaces = !!glset.map_lightadditivesurfaces; - if (glset.map_attenuate == -1) glset.attenuate = gl_attenuate; - else glset.attenuate = !!glset.map_attenuate; } void InitGLRMapinfoData() @@ -463,7 +443,6 @@ void InitGLRMapinfoData() gl_SetFogParams(clamp(opt->fogdensity, 0, 255), level.info->outsidefog, clamp(opt->outsidefogdensity, 0, 255), opt->skyfog); glset.map_lightmode = opt->lightmode; glset.map_lightadditivesurfaces = opt->lightadditivesurfaces; - glset.map_attenuate = opt->attenuate; glset.map_brightfog = opt->brightfog; glset.map_nocoloredspritelighting = opt->nocoloredspritelighting; glset.map_notexturefill = opt->notexturefill; @@ -478,7 +457,6 @@ void InitGLRMapinfoData() glset.map_lightmode = -1; glset.map_lightadditivesurfaces = -1; glset.map_brightfog = -1; - glset.map_attenuate = -1; glset.map_nocoloredspritelighting = -1; glset.map_notexturefill = -1; glset.skyrotatevector = FVector3(0, 0, 1); @@ -590,7 +568,7 @@ CCMD(dumpgeometry) subsector_t * sub = sector.subsectors[j]; Printf(PRINT_LOG, " Subsector %d - real sector = %d - %s\n", int(sub-subsectors), sub->sector->sectornum, sub->hacked&1? "hacked":""); - for(DWORD k=0;knumlines;k++) + for(uint32_t k=0;knumlines;k++) { seg_t * seg = sub->firstline + k; if (seg->linedef) diff --git a/src/gl/data/gl_data.h b/src/gl/data/gl_data.h index cf79fe1386..5fecb0a5d0 100644 --- a/src/gl/data/gl_data.h +++ b/src/gl/data/gl_data.h @@ -4,24 +4,22 @@ #include "doomtype.h" #include "vectors.h" +#include "r_utility.h" struct GLRenderSettings { - - SBYTE lightmode; + int8_t lightmode; bool nocoloredspritelighting; bool nolightfade; bool notexturefill; bool brightfog; bool lightadditivesurfaces; - bool attenuate; int8_t map_lightmode; int8_t map_nocoloredspritelighting; int8_t map_notexturefill; int8_t map_brightfog; int8_t map_lightadditivesurfaces; - int8_t map_attenuate; FVector3 skyrotatevector; FVector3 skyrotatevector2; @@ -36,12 +34,11 @@ extern GLRenderSettings glset; #include "a_sharedglobal.h" #include "c_cvars.h" -extern int extralight; EXTERN_CVAR(Int, gl_weaponlight); inline int getExtraLight() { - return extralight * gl_weaponlight; + return r_viewpoint.extralight * gl_weaponlight; } void gl_RecalcVertexHeights(vertex_t * v); @@ -69,7 +66,7 @@ struct FGLLinePortal extern TArray portals; extern TArray linePortalToGL; -extern TArray currentmapsection; +extern TArray currentmapsection; void gl_InitPortals(); void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, const DVector2 &displacement); diff --git a/src/gl/data/gl_portaldata.cpp b/src/gl/data/gl_portaldata.cpp index c705da5891..17a31d5bed 100644 --- a/src/gl/data/gl_portaldata.cpp +++ b/src/gl/data/gl_portaldata.cpp @@ -296,7 +296,7 @@ struct FCoverageBuilder else { // we reached a subsector so we can link the node with this subsector - subsector_t *sub = (subsector_t *)((BYTE *)node - 1); + subsector_t *sub = (subsector_t *)((uint8_t *)node - 1); collect.Push(int(sub-subsectors)); } } @@ -325,9 +325,9 @@ void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, c build.center.y = xs_CRoundToInt(centery / subsector->numlines); build.CollectNode(nodes + numnodes - 1, shape); - coverage->subsectors = new DWORD[build.collect.Size()]; + coverage->subsectors = new uint32_t[build.collect.Size()]; coverage->sscount = build.collect.Size(); - memcpy(coverage->subsectors, &build.collect[0], build.collect.Size() * sizeof(DWORD)); + memcpy(coverage->subsectors, &build.collect[0], build.collect.Size() * sizeof(uint32_t)); } //========================================================================== diff --git a/src/gl/data/gl_setup.cpp b/src/gl/data/gl_setup.cpp index c34b149d58..ade8303aa4 100644 --- a/src/gl/data/gl_setup.cpp +++ b/src/gl/data/gl_setup.cpp @@ -67,7 +67,7 @@ static void DoSetMapSection(subsector_t *sub, int num) for (unsigned a = 0; a < MapSectionCollector.Size(); a++) { sub = MapSectionCollector[a]; - for (DWORD i = 0; i < sub->numlines; i++) + for (uint32_t i = 0; i < sub->numlines; i++) { seg_t * seg = sub->firstline + i; @@ -126,7 +126,7 @@ static int MergeMapSections(int num) cvertex_t vt; // first step: Set mapsection for all vertex positions. - for(DWORD i=0;i<(DWORD)numsegs;i++) + for(uint32_t i=0;i<(uint32_t)numsegs;i++) { seg_t * seg = &segs[i]; int section = seg->Subsector->mapsection; @@ -138,7 +138,7 @@ static int MergeMapSections(int num) } // 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++) + for(uint32_t i=0;i<(uint32_t)numsegs;i++) { seg_t * seg = &segs[i]; int section = seg->Subsector->mapsection; @@ -219,7 +219,7 @@ static void SetMapSections() static void SpreadHackedFlag(subsector_t * sub) { // The subsector pointer hasn't been set yet! - for(DWORD i=0;inumlines;i++) + for(uint32_t i=0;inumlines;i++) { seg_t * seg = sub->firstline + i; @@ -276,7 +276,7 @@ static void PrepareSectorData() if (subsectors[i].sector == subsectors[i].render_sector) { seg_t * seg = subsectors[i].firstline; - for(DWORD j=0;j centroids; + for (unsigned int i = 0; i < level.lines.Size(); i++) + { + FVector2 v1 = { (float)level.lines[i].v1->fX(), (float)level.lines[i].v1->fY() }; + FVector2 v2 = { (float)level.lines[i].v2->fX(), (float)level.lines[i].v2->fY() }; + centroids.Push((v1 + v2) * 0.5f); + } + + // Create a list of level lines we want to add: + TArray line_elements; + for (unsigned int i = 0; i < level.lines.Size(); i++) + { + if (!level.lines[i].backsector) + { + line_elements.Push(i); + } + } + + // GenerateTreeNode needs a buffer where it can store line indices temporarily when sorting lines into the left and right child AABB buckets + TArray work_buffer; + work_buffer.Resize(line_elements.Size() * 2); + + // Generate the AABB tree + GenerateTreeNode(&line_elements[0], (int)line_elements.Size(), ¢roids[0], &work_buffer[0]); + + // Add the lines referenced by the leaf nodes + lines.Resize(level.lines.Size()); + for (unsigned int i = 0; i < level.lines.Size(); i++) + { + const auto &line = level.lines[i]; + auto &treeline = lines[i]; + + treeline.x = (float)line.v1->fX(); + treeline.y = (float)line.v1->fY(); + treeline.dx = (float)line.v2->fX() - treeline.x; + treeline.dy = (float)line.v2->fY() - treeline.y; + } +} + +double LevelAABBTree::RayTest(const DVector3 &ray_start, const DVector3 &ray_end) +{ + // Precalculate some of the variables used by the ray/line intersection test + DVector2 raydelta = ray_end - ray_start; + double raydist2 = raydelta | raydelta; + DVector2 raynormal = DVector2(raydelta.Y, -raydelta.X); + double rayd = raynormal | ray_start; + if (raydist2 < 1.0) + return 1.0f; + + double hit_fraction = 1.0; + + // Walk the tree nodes + int stack[16]; + int stack_pos = 1; + stack[0] = nodes.Size() - 1; // root node is the last node in the list + while (stack_pos > 0) + { + int node_index = stack[stack_pos - 1]; + + if (!OverlapRayAABB(ray_start, ray_end, nodes[node_index])) + { + // If the ray doesn't overlap this node's AABB we're done for this subtree + stack_pos--; + } + else if (nodes[node_index].line_index != -1) // isLeaf(node_index) + { + // We reached a leaf node. Do a ray/line intersection test to see if we hit the line. + hit_fraction = MIN(IntersectRayLine(ray_start, ray_end, nodes[node_index].line_index, raydelta, rayd, raydist2), hit_fraction); + stack_pos--; + } + else if (stack_pos == 16) + { + stack_pos--; // stack overflow - tree is too deep! + } + else + { + // The ray overlaps the node's AABB. Examine its child nodes. + stack[stack_pos - 1] = nodes[node_index].left_node; + stack[stack_pos] = nodes[node_index].right_node; + stack_pos++; + } + } + + return hit_fraction; +} + +bool LevelAABBTree::OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const AABBTreeNode &node) +{ + // To do: simplify test to use a 2D test + DVector3 ray_start = DVector3(ray_start2d, 0.0); + DVector3 ray_end = DVector3(ray_end2d, 0.0); + DVector3 aabb_min = DVector3(node.aabb_left, node.aabb_top, -1.0); + DVector3 aabb_max = DVector3(node.aabb_right, node.aabb_bottom, 1.0); + + // Standard 3D ray/AABB overlapping test. + // The details for the math here can be found in Real-Time Rendering, 3rd Edition. + // We could use a 2D test here instead, which would probably simplify the math. + + DVector3 c = (ray_start + ray_end) * 0.5f; + DVector3 w = ray_end - c; + DVector3 h = (aabb_max - aabb_min) * 0.5f; // aabb.extents(); + + c -= (aabb_max + aabb_min) * 0.5f; // aabb.center(); + + DVector3 v = DVector3(abs(w.X), abs(w.Y), abs(w.Z)); + + if (abs(c.X) > v.X + h.X || abs(c.Y) > v.Y + h.Y || abs(c.Z) > v.Z + h.Z) + return false; // disjoint; + + if (abs(c.Y * w.Z - c.Z * w.Y) > h.Y * v.Z + h.Z * v.Y || + abs(c.X * w.Z - c.Z * w.X) > h.X * v.Z + h.Z * v.X || + abs(c.X * w.Y - c.Y * w.X) > h.X * v.Y + h.Y * v.X) + return false; // disjoint; + + return true; // overlap; +} + +double LevelAABBTree::IntersectRayLine(const DVector2 &ray_start, const DVector2 &ray_end, int line_index, const DVector2 &raydelta, double rayd, double raydist2) +{ + // Check if two line segments intersects (the ray and the line). + // The math below does this by first finding the fractional hit for an infinitely long ray line. + // If that hit is within the line segment (0 to 1 range) then it calculates the fractional hit for where the ray would hit. + // + // This algorithm is homemade - I would not be surprised if there's a much faster method out there. + + const double epsilon = 0.0000001; + const AABBTreeLine &line = lines[line_index]; + + DVector2 raynormal = DVector2(raydelta.Y, -raydelta.X); + + DVector2 line_pos(line.x, line.y); + DVector2 line_delta(line.dx, line.dy); + + double den = raynormal | line_delta; + if (abs(den) > epsilon) + { + double t_line = (rayd - (raynormal | line_pos)) / den; + if (t_line >= 0.0 && t_line <= 1.0) + { + DVector2 linehitdelta = line_pos + line_delta * t_line - ray_start; + double t = (raydelta | linehitdelta) / raydist2; + return t > 0.0 ? t : 1.0; + } + } + + return 1.0; +} + +int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer) +{ + if (num_lines == 0) + return -1; + + // Find bounding box and median of the lines + FVector2 median = FVector2(0.0f, 0.0f); + FVector2 aabb_min, aabb_max; + aabb_min.X = (float)level.lines[lines[0]].v1->fX(); + aabb_min.Y = (float)level.lines[lines[0]].v1->fY(); + aabb_max = aabb_min; + for (int i = 0; i < num_lines; i++) + { + float x1 = (float)level.lines[lines[i]].v1->fX(); + float y1 = (float)level.lines[lines[i]].v1->fY(); + float x2 = (float)level.lines[lines[i]].v2->fX(); + float y2 = (float)level.lines[lines[i]].v2->fY(); + + aabb_min.X = MIN(aabb_min.X, x1); + aabb_min.X = MIN(aabb_min.X, x2); + aabb_min.Y = MIN(aabb_min.Y, y1); + aabb_min.Y = MIN(aabb_min.Y, y2); + aabb_max.X = MAX(aabb_max.X, x1); + aabb_max.X = MAX(aabb_max.X, x2); + aabb_max.Y = MAX(aabb_max.Y, y1); + aabb_max.Y = MAX(aabb_max.Y, y2); + + median += centroids[lines[i]]; + } + median /= (float)num_lines; + + if (num_lines == 1) // Leaf node + { + nodes.Push(AABBTreeNode(aabb_min, aabb_max, lines[0])); + return (int)nodes.Size() - 1; + } + + // Find the longest axis + float axis_lengths[2] = + { + aabb_max.X - aabb_min.X, + aabb_max.Y - aabb_min.Y + }; + int axis_order[2] = { 0, 1 }; + FVector2 axis_plane[2] = { FVector2(1.0f, 0.0f), FVector2(0.0f, 1.0f) }; + std::sort(axis_order, axis_order + 2, [&](int a, int b) { return axis_lengths[a] > axis_lengths[b]; }); + + // Try sort at longest axis, then if that fails then the other one. + // We place the sorted lines into work_buffer and then move the result back to the lines list when done. + int left_count, right_count; + FVector2 axis; + for (int attempt = 0; attempt < 2; attempt++) + { + // Find the sort plane for axis + FVector2 axis = axis_plane[axis_order[attempt]]; + FVector3 plane(axis, -(median | axis)); + + // Sort lines into two based ib whether the line center is on the front or back side of a plane + left_count = 0; + right_count = 0; + for (int i = 0; i < num_lines; i++) + { + int line_index = lines[i]; + + float side = FVector3(centroids[lines[i]], 1.0f) | plane; + if (side >= 0.0f) + { + work_buffer[left_count] = line_index; + left_count++; + } + else + { + work_buffer[num_lines + right_count] = line_index; + right_count++; + } + } + + if (left_count != 0 && right_count != 0) + break; + } + + // Check if something went wrong when sorting and do a random sort instead + if (left_count == 0 || right_count == 0) + { + left_count = num_lines / 2; + right_count = num_lines - left_count; + } + else + { + // Move result back into lines list: + for (int i = 0; i < left_count; i++) + lines[i] = work_buffer[i]; + for (int i = 0; i < right_count; i++) + lines[i + left_count] = work_buffer[num_lines + i]; + } + + // Create child nodes: + int left_index = -1; + int right_index = -1; + if (left_count > 0) + left_index = GenerateTreeNode(lines, left_count, centroids, work_buffer); + if (right_count > 0) + right_index = GenerateTreeNode(lines + left_count, right_count, centroids, work_buffer); + + // Store resulting node and return its index + nodes.Push(AABBTreeNode(aabb_min, aabb_max, left_index, right_index)); + return (int)nodes.Size() - 1; +} diff --git a/src/gl/dynlights/gl_aabbtree.h b/src/gl/dynlights/gl_aabbtree.h new file mode 100644 index 0000000000..0de075be58 --- /dev/null +++ b/src/gl/dynlights/gl_aabbtree.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "vectors.h" + +// Node in a binary AABB tree +struct AABBTreeNode +{ + AABBTreeNode(const FVector2 &aabb_min, const FVector2 &aabb_max, int line_index) : aabb_left(aabb_min.X), aabb_top(aabb_min.Y), aabb_right(aabb_max.X), aabb_bottom(aabb_max.Y), left_node(-1), right_node(-1), line_index(line_index) { } + AABBTreeNode(const FVector2 &aabb_min, const FVector2 &aabb_max, int left, int right) : aabb_left(aabb_min.X), aabb_top(aabb_min.Y), aabb_right(aabb_max.X), aabb_bottom(aabb_max.Y), left_node(left), right_node(right), line_index(-1) { } + + // Axis aligned bounding box for the node + float aabb_left, aabb_top; + float aabb_right, aabb_bottom; + + // Child node indices + int left_node; + int right_node; + + // AABBTreeLine index if it is a leaf node. Index is -1 if it is not. + int line_index; + + // Padding to keep 16-byte length (this structure is uploaded to the GPU) + int padding; +}; + +// Line segment for leaf nodes in an AABB tree +struct AABBTreeLine +{ + float x, y; + float dx, dy; +}; + +// Axis aligned bounding box tree used for ray testing lines. +class LevelAABBTree +{ +public: + // Constructs a tree for the current level + LevelAABBTree(); + + // Nodes in the AABB tree. Last node is the root node. + TArray nodes; + + // Line segments for the leaf nodes in the tree. + TArray lines; + + // Shoot a ray from ray_start to ray_end and return the closest hit as a fractional value between 0 and 1. Returns 1 if no line was hit. + double RayTest(const DVector3 &ray_start, const DVector3 &ray_end); + +private: + // Test if a ray overlaps an AABB node or not + bool OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const AABBTreeNode &node); + + // Intersection test between a ray and a line segment + double IntersectRayLine(const DVector2 &ray_start, const DVector2 &ray_end, int line_index, const DVector2 &raydelta, double rayd, double raydist2); + + // Generate a tree node and its children recursively + int GenerateTreeNode(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer); +}; diff --git a/src/gl/dynlights/gl_dynlight.h b/src/gl/dynlights/gl_dynlight.h index 4733d853f4..74e49babaf 100644 --- a/src/gl/dynlights/gl_dynlight.h +++ b/src/gl/dynlights/gl_dynlight.h @@ -24,134 +24,10 @@ #define __GLC_DYNLIGHT_H #include "c_cvars.h" +#include "a_dynlight.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 FSerializer; -class FLightDefaults; - - -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 -#define MF4_ATTENUATE MF4_INCOMBAT - -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 -{ - friend class FLightDefaults; - DECLARE_CLASS (ADynamicLight, AActor) -public: - virtual void Tick(); - void Serialize(FSerializer &arc); - void PostSerialize(); - BYTE GetRed() const { return args[LIGHT_RED]; } - BYTE GetGreen() const { return args[LIGHT_GREEN]; } - BYTE GetBlue() const { return args[LIGHT_BLUE]; } - float GetRadius() const { return (IsActive() ? m_currentRadius * 2.f : 0.f); } - void LinkLight(); - void UnlinkLight(); - size_t PointerSubstitution (DObject *old, DObject *notOld); - - void BeginPlay(); - void SetOrigin (double x, double y, double z, bool moving = false); - void PostBeginPlay(); - void OnDestroy() override; - void Activate(AActor *activator); - void Deactivate(AActor *activator); - void SetOffset(const DVector3 &pos); - 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: - double DistToSeg(const DVector3 &pos, seg_t *seg); - void CollectWithinRadius(const DVector3 &pos, subsector_t *subSec, float radius); - -protected: - DVector3 m_off; - float m_currentRadius; - unsigned int m_lastUpdate; - FCycler m_cycler; - subsector_t * subsector; - -public: - int m_tickCount; - BYTE lightflags; - BYTE lighttype; - bool owned; - bool halo; - BYTE color2[3]; - bool visibletoplayer; - bool swapped; - int bufferindex; - - -}; - -enum -{ - STAT_DLIGHT=64 -}; - struct FDynLightData { TArray arrays[3]; diff --git a/src/gl/dynlights/gl_dynlight1.cpp b/src/gl/dynlights/gl_dynlight1.cpp index 2f8ef44edc..7988ab51fe 100644 --- a/src/gl/dynlights/gl_dynlight1.cpp +++ b/src/gl/dynlights/gl_dynlight1.cpp @@ -31,6 +31,8 @@ #include "vectors.h" #include "gl/gl_functions.h" #include "g_level.h" +#include "actorinlines.h" +#include "a_dynlight.h" #include "gl/system/gl_interface.h" #include "gl/renderer/gl_renderer.h" @@ -49,16 +51,12 @@ // //========================================================================== -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 (Bool, gl_light_sprites, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Bool, gl_light_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +CVAR (Bool, gl_light_shadowmap, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +CVAR(Int, gl_attenuate, -1, 0); // This is mainly a debug option. //========================================================================== // @@ -99,15 +97,24 @@ bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FD if (light->IsSubtractive()) { - Vector v; + DVector3 v(r, g, b); + float length = (float)v.Length(); - v.Set(r, g, b); - r = v.Length() - r; - g = v.Length() - g; - b = v.Length() - b; + r = length - r; + g = length - g; + b = length - b; i = 1; } + // Store attenuate flag in the sign bit of the float. + float shadowIndex = GLRenderer->mShadowMap.ShadowMapIndex(light) + 1.0f; + bool attenuate; + + if (gl_attenuate == -1) attenuate = !!(light->flags4 & MF4_ATTENUATE); + else attenuate = !!gl_attenuate; + + shadowIndex = -shadowIndex; + float *data = &ldata.arrays[i][ldata.arrays[i].Reserve(8)]; data[0] = pos.X; data[1] = pos.Z; @@ -116,7 +123,7 @@ bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FD data[4] = r; data[5] = g; data[6] = b; - data[7] = !!(light->flags4 & MF4_ATTENUATE); + data[7] = shadowIndex; return true; } diff --git a/src/gl/dynlights/gl_shadowmap.cpp b/src/gl/dynlights/gl_shadowmap.cpp new file mode 100644 index 0000000000..9a23e41145 --- /dev/null +++ b/src/gl/dynlights/gl_shadowmap.cpp @@ -0,0 +1,206 @@ +// +//--------------------------------------------------------------------------- +// 1D dynamic shadow maps +// Copyright(C) 2017 Magnus Norddahl +// All rights reserved. +// +// 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 3 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, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include "gl/system/gl_system.h" +#include "gl/shaders/gl_shader.h" +#include "gl/dynlights/gl_shadowmap.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_debug.h" +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_postprocessstate.h" +#include "gl/renderer/gl_renderbuffers.h" +#include "gl/shaders/gl_shadowmapshader.h" +#include "r_state.h" + +/* + The 1D shadow maps are stored in a 1024x1024 texture as float depth values (R32F). + + Each line in the texture is assigned to a single light. For example, to grab depth values for light 20 + the fragment shader (main.fp) needs to sample from row 20. That is, the V texture coordinate needs + to be 20.5/1024. + + mLightToShadowmap is a hash map storing which line each ADynamicLight is assigned to. The public + ShadowMapIndex function allows the main rendering to find the index and upload that along with the + normal light data. From there, the main.fp shader can sample from the shadow map texture, which + is currently always bound to texture unit 16. + + The texel row for each light is split into four parts. One for each direction, like a cube texture, + but then only in 2D where this reduces itself to a square. When main.fp samples from the shadow map + it first decides in which direction the fragment is (relative to the light), like cubemap sampling does + for 3D, but once again just for the 2D case. + + Texels 0-255 is Y positive, 256-511 is X positive, 512-767 is Y negative and 768-1023 is X negative. + + Generating the shadow map itself is done by FShadowMap::Update(). The shadow map texture's FBO is + bound and then a screen quad is drawn to make a fragment shader cover all texels. For each fragment + it shoots a ray and collects the distance to what it hit. + + The shadowmap.fp shader knows which light and texel it is processing by mapping gl_FragCoord.y back + to the light index, and it knows which direction to ray trace by looking at gl_FragCoord.x. For + example, if gl_FragCoord.y is 20.5, then it knows its processing light 20, and if gl_FragCoord.x is + 127.5, then it knows we are shooting straight ahead for the Y positive direction. + + Ray testing is done by uploading two GPU storage buffers - one holding AABB tree nodes, and one with + the line segments at the leaf nodes of the tree. The fragment shader then performs a test same way + as on the CPU, except everything uses indexes as pointers are not allowed in GLSL. +*/ + +void FShadowMap::Update() +{ + if (!IsEnabled()) + return; + + UploadAABBTree(); + UploadLights(); + + FGLDebug::PushGroup("ShadowMap"); + FGLPostProcessState savedState; + + GLRenderer->mBuffers->BindShadowMapFB(); + + GLRenderer->mShadowMapShader->Bind(); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, mLightList); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, mNodesBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, mLinesBuffer); + + glViewport(0, 0, 1024, 1024); + GLRenderer->RenderScreenQuad(); + + const auto &viewport = GLRenderer->mScreenViewport; + glViewport(viewport.left, viewport.top, viewport.width, viewport.height); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, 0); + + GLRenderer->mBuffers->BindShadowMapTexture(16); + + FGLDebug::PopGroup(); +} + +bool FShadowMap::ShadowTest(ADynamicLight *light, const DVector3 &pos) +{ + if (IsEnabled() && mAABBTree) + return mAABBTree->RayTest(light->Pos(), pos) >= 1.0f; + else + return true; +} + +bool FShadowMap::IsEnabled() const +{ + return gl_light_shadowmap && !!(gl.flags & RFL_SHADER_STORAGE_BUFFER); +} + +int FShadowMap::ShadowMapIndex(ADynamicLight *light) +{ + if (IsEnabled()) + return mLightToShadowmap[light]; + else + return 1024; +} + +void FShadowMap::UploadLights() +{ + mLights.Clear(); + mLightToShadowmap.Clear(mLightToShadowmap.CountUsed() * 2); // To do: allow clearing a TMap while building up a reserve + + TThinkerIterator it(STAT_DLIGHT); + while (true) + { + ADynamicLight *light = it.Next(); + if (!light) break; + + mLightToShadowmap[light] = mLights.Size() / 4; + + mLights.Push(light->X()); + mLights.Push(light->Y()); + mLights.Push(light->Z()); + mLights.Push(light->GetRadius()); + + if (mLights.Size() == 1024) // Only 1024 lights for now + break; + } + + while (mLights.Size() < 1024 * 4) + mLights.Push(0.0f); + + if (mLightList == 0) + glGenBuffers(1, (GLuint*)&mLightList); + + int oldBinding = 0; + glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &oldBinding); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, mLightList); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float) * mLights.Size(), &mLights[0], GL_STATIC_DRAW); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding); +} + +void FShadowMap::UploadAABBTree() +{ + if (numnodes != mLastNumNodes || numsegs != mLastNumSegs) // To do: there is probably a better way to detect a map change than this.. + Clear(); + + if (mAABBTree) + return; + + mAABBTree.reset(new LevelAABBTree()); + + int oldBinding = 0; + glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &oldBinding); + + glGenBuffers(1, (GLuint*)&mNodesBuffer); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, mNodesBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(AABBTreeNode) * mAABBTree->nodes.Size(), &mAABBTree->nodes[0], GL_STATIC_DRAW); + + glGenBuffers(1, (GLuint*)&mLinesBuffer); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, mLinesBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(AABBTreeLine) * mAABBTree->lines.Size(), &mAABBTree->lines[0], GL_STATIC_DRAW); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding); +} + +void FShadowMap::Clear() +{ + if (mLightList != 0) + { + glDeleteBuffers(1, (GLuint*)&mLightList); + mLightList = 0; + } + + if (mNodesBuffer != 0) + { + glDeleteBuffers(1, (GLuint*)&mNodesBuffer); + mNodesBuffer = 0; + } + + if (mLinesBuffer != 0) + { + glDeleteBuffers(1, (GLuint*)&mLinesBuffer); + mLinesBuffer = 0; + } + + mAABBTree.reset(); + + mLastNumNodes = numnodes; + mLastNumSegs = numsegs; +} diff --git a/src/gl/dynlights/gl_shadowmap.h b/src/gl/dynlights/gl_shadowmap.h new file mode 100644 index 0000000000..e2b5b371fb --- /dev/null +++ b/src/gl/dynlights/gl_shadowmap.h @@ -0,0 +1,60 @@ + +#pragma once + +#include "gl/dynlights/gl_aabbtree.h" +#include "tarray.h" +#include + +class ADynamicLight; + +class FShadowMap +{ +public: + FShadowMap() { } + ~FShadowMap() { Clear(); } + + // Release resources + void Clear(); + + // Update shadow map texture + void Update(); + + // Return the assigned shadow map index for a given light + int ShadowMapIndex(ADynamicLight *light); + + // Test if a world position is in shadow relative to the specified light and returns false if it is + bool ShadowTest(ADynamicLight *light, const DVector3 &pos); + + // Returns true if gl_light_shadowmap is enabled and supported by the hardware + bool IsEnabled() const; + +private: + // Upload the AABB-tree to the GPU + void UploadAABBTree(); + + // Upload light list to the GPU + void UploadLights(); + + // OpenGL storage buffer with the list of lights in the shadow map texture + int mLightList = 0; + + // Working buffer for creating the list of lights. Stored here to avoid allocating memory each frame + TArray mLights; + + // The assigned shadow map index for each light + TMap mLightToShadowmap; + + // OpenGL storage buffers for the AABB tree + int mNodesBuffer = 0; + int mLinesBuffer = 0; + + // Used to detect when a level change requires the AABB tree to be regenerated + int mLastNumNodes = 0; + int mLastNumSegs = 0; + + // AABB-tree of the level, used for ray tests + std::unique_ptr mAABBTree; + + FShadowMap(const FShadowMap &) = delete; + FShadowMap &operator=(FShadowMap &) = delete; +}; diff --git a/src/gl/gl_functions.h b/src/gl/gl_functions.h index aa08d19be8..2401926fff 100644 --- a/src/gl/gl_functions.h +++ b/src/gl/gl_functions.h @@ -8,6 +8,5 @@ class AActor; void gl_PreprocessLevel(); void gl_CleanLevelData(); void gl_LinkLights(); -void gl_SetActorLights(AActor *); #endif diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp index fe974f4dfd..b8dcc8c7ca 100644 --- a/src/gl/models/gl_models.cpp +++ b/src/gl/models/gl_models.cpp @@ -1023,7 +1023,7 @@ void gl_RenderModel(GLSprite * spr) if (spr->actor->renderflags & RF_INTERPOLATEANGLES) { // [Nash] use interpolated angles - DRotator Angles = spr->actor->InterpolatedAngles(r_TicFracF); + DRotator Angles = spr->actor->InterpolatedAngles(r_viewpoint.TicFrac); angle = Angles.Yaw.Degrees; } diff --git a/src/gl/models/gl_models.h b/src/gl/models/gl_models.h index c37b69b5ee..a1ed4014d6 100644 --- a/src/gl/models/gl_models.h +++ b/src/gl/models/gl_models.h @@ -59,7 +59,7 @@ public: 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; - virtual void AddSkins(BYTE *hitlist) = 0; + virtual void AddSkins(uint8_t *hitlist) = 0; void DestroyVertexBuffer() { delete mVBuf; @@ -185,7 +185,7 @@ public: virtual int FindFrame(const char * name); virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0); virtual void LoadGeometry(); - virtual void AddSkins(BYTE *hitlist); + virtual void AddSkins(uint8_t *hitlist); void UnloadGeometry(); void BuildVertexBuffer(); @@ -292,7 +292,7 @@ public: virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0); void LoadGeometry(); void BuildVertexBuffer(); - virtual void AddSkins(BYTE *hitlist); + virtual void AddSkins(uint8_t *hitlist); }; struct FVoxelVertexHash @@ -335,7 +335,7 @@ protected: TArray 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); + 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, uint8_t color, FVoxelMap &check); unsigned int AddVertex(FModelVertex &vert, FVoxelMap &check); public: @@ -345,7 +345,7 @@ public: void Initialize(); virtual int FindFrame(const char * name); virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0); - virtual void AddSkins(BYTE *hitlist); + virtual void AddSkins(uint8_t *hitlist); FTextureID GetPaletteTexture() const { return mPalette; } void BuildVertexBuffer(); float getAspectFactor(); diff --git a/src/gl/models/gl_models_md3.cpp b/src/gl/models/gl_models_md3.cpp index b914737141..6336ce2772 100644 --- a/src/gl/models/gl_models_md3.cpp +++ b/src/gl/models/gl_models_md3.cpp @@ -300,7 +300,7 @@ void FMD3Model::BuildVertexBuffer() // //=========================================================================== -void FMD3Model::AddSkins(BYTE *hitlist) +void FMD3Model::AddSkins(uint8_t *hitlist) { for (int i = 0; i < numSurfaces; i++) { diff --git a/src/gl/models/gl_voxels.cpp b/src/gl/models/gl_voxels.cpp index 6e84bd7abf..360ef4434a 100644 --- a/src/gl/models/gl_voxels.cpp +++ b/src/gl/models/gl_voxels.cpp @@ -65,8 +65,8 @@ public: FVoxelTexture(FVoxel *voxel); ~FVoxelTexture(); - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); + const uint8_t *GetColumn (unsigned int column, const Span **spans_out); + const uint8_t *GetPixels (); void Unload (); int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf); @@ -74,7 +74,7 @@ public: protected: FVoxel *SourceVox; - BYTE *Pixels; + uint8_t *Pixels; }; @@ -107,20 +107,20 @@ FVoxelTexture::~FVoxelTexture() { } -const BYTE *FVoxelTexture::GetColumn (unsigned int column, const Span **spans_out) +const uint8_t *FVoxelTexture::GetColumn (unsigned int column, const Span **spans_out) { // not needed return NULL; } -const BYTE *FVoxelTexture::GetPixels () +const uint8_t *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]; + Pixels = new uint8_t[256]; - BYTE *pp = SourceVox->Palette; + uint8_t *pp = SourceVox->Palette; if(pp != NULL) { @@ -137,7 +137,7 @@ const BYTE *FVoxelTexture::GetPixels () { for(int i=0;i<256;i++, pp+=3) { - Pixels[i] = (BYTE)i; + Pixels[i] = (uint8_t)i; } } } @@ -165,14 +165,14 @@ void FVoxelTexture::Unload () int FVoxelTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) { PalEntry pe[256]; - BYTE bitmap[256]; - BYTE *pp = SourceVox->Palette; + uint8_t bitmap[256]; + uint8_t *pp = SourceVox->Palette; if(pp != NULL) { for(int i=0;i<256;i++, pp+=3) { - bitmap[i] = (BYTE)i; + bitmap[i] = (uint8_t)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); @@ -183,7 +183,7 @@ int FVoxelTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, F { for(int i=0;i<256;i++, pp+=3) { - bitmap[i] = (BYTE)i; + bitmap[i] = (uint8_t)i; pe[i] = GPalette.BaseColors[i]; pe[i].a = 255; } @@ -239,7 +239,7 @@ unsigned int FVoxelModel::AddVertex(FModelVertex &vert, FVoxelMap &check) // //=========================================================================== -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) +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, uint8_t col, FVoxelMap &check) { float PivotX = mVoxel->Mips[0].Pivot.X; float PivotY = mVoxel->Mips[0].Pivot.Y; @@ -289,7 +289,7 @@ void FVoxelModel::AddFace(int x1, int y1, int z1, int x2, int y2, int z2, int x3 void FVoxelModel::MakeSlabPolys(int x, int y, kvxslab_t *voxptr, FVoxelMap &check) { - const BYTE *col = voxptr->col; + const uint8_t *col = voxptr->col; int zleng = voxptr->zleng; int ztop = voxptr->ztop; int cull = voxptr->backfacecull; @@ -342,13 +342,13 @@ void FVoxelModel::Initialize() FVoxelMipLevel *mip = &mVoxel->Mips[0]; for (int x = 0; x < mip->SizeX; x++) { - BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; + uint8_t *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)) + for (; voxptr < voxend; voxptr = (kvxslab_t *)((uint8_t *)voxptr + voxptr->zleng + 3)) { MakeSlabPolys(x, y, voxptr, check); } @@ -394,7 +394,7 @@ void FVoxelModel::BuildVertexBuffer() // //=========================================================================== -void FVoxelModel::AddSkins(BYTE *hitlist) +void FVoxelModel::AddSkins(uint8_t *hitlist) { hitlist[mPalette.GetIndex()] |= FTexture::TEX_Flat; } diff --git a/src/gl/models/tab_anorms.h b/src/gl/models/tab_anorms.h index 1d91fff4f0..dfd9f691df 100644 --- a/src/gl/models/tab_anorms.h +++ b/src/gl/models/tab_anorms.h @@ -3,487 +3,487 @@ #endif { --0.525731, 0.000000, 0.850651}, +-0.525731f, 0.000000f, 0.850651f}, { --0.442863, 0.238856, 0.864188}, +-0.442863f, 0.238856f, 0.864188f}, { --0.295242, 0.000000, 0.955423}, +-0.295242f, 0.000000f, 0.955423f}, { --0.309017, 0.500000, 0.809017}, +-0.309017f, 0.500000f, 0.809017f}, { --0.162460, 0.262866, 0.951056}, +-0.162460f, 0.262866f, 0.951056f}, { -0.000000, 0.000000, 1.000000}, +0.000000f, 0.000000f, 1.000000f}, { -0.000000, 0.850651, 0.525731}, +0.000000f, 0.850651f, 0.525731f}, { --0.147621, 0.716567, 0.681718}, +-0.147621f, 0.716567f, 0.681718f}, { -0.147621, 0.716567, 0.681718}, +0.147621f, 0.716567f, 0.681718f}, { -0.000000, 0.525731, 0.850651}, +0.000000f, 0.525731f, 0.850651f}, { -0.309017, 0.500000, 0.809017}, +0.309017f, 0.500000f, 0.809017f}, { -0.525731, 0.000000, 0.850651}, +0.525731f, 0.000000f, 0.850651f}, { -0.295242, 0.000000, 0.955423}, +0.295242f, 0.000000f, 0.955423f}, { -0.442863, 0.238856, 0.864188}, +0.442863f, 0.238856f, 0.864188f}, { -0.162460, 0.262866, 0.951056}, +0.162460f, 0.262866f, 0.951056f}, { --0.681718, 0.147621, 0.716567}, +-0.681718f, 0.147621f, 0.716567f}, { --0.809017, 0.309017, 0.500000}, +-0.809017f, 0.309017f, 0.500000f}, { --0.587785, 0.425325, 0.688191}, +-0.587785f, 0.425325f, 0.688191f}, { --0.850651, 0.525731, 0.000000}, +-0.850651f, 0.525731f, 0.000000f}, { --0.864188, 0.442863, 0.238856}, +-0.864188f, 0.442863f, 0.238856f}, { --0.716567, 0.681718, 0.147621}, +-0.716567f, 0.681718f, 0.147621f}, { --0.688191, 0.587785, 0.425325}, +-0.688191f, 0.587785f, 0.425325f}, { --0.500000, 0.809017, 0.309017}, +-0.500000f, 0.809017f, 0.309017f}, { --0.238856, 0.864188, 0.442863}, +-0.238856f, 0.864188f, 0.442863f}, { --0.425325, 0.688191, 0.587785}, +-0.425325f, 0.688191f, 0.587785f}, { --0.716567, 0.681718, -0.147621}, +-0.716567f, 0.681718f, -0.147621f}, { --0.500000, 0.809017, -0.309017}, +-0.500000f, 0.809017f, -0.309017f}, { --0.525731, 0.850651, 0.000000}, +-0.525731f, 0.850651f, 0.000000f}, { -0.000000, 0.850651, -0.525731}, +0.000000f, 0.850651f, -0.525731f}, { --0.238856, 0.864188, -0.442863}, +-0.238856f, 0.864188f, -0.442863f}, { -0.000000, 0.955423, -0.295242}, +0.000000f, 0.955423f, -0.295242f}, { --0.262866, 0.951056, -0.162460}, +-0.262866f, 0.951056f, -0.162460f}, { -0.000000, 1.000000, 0.000000}, +0.000000f, 1.000000f, 0.000000f}, { -0.000000, 0.955423, 0.295242}, +0.000000f, 0.955423f, 0.295242f}, { --0.262866, 0.951056, 0.162460}, +-0.262866f, 0.951056f, 0.162460f}, { -0.238856, 0.864188, 0.442863}, +0.238856f, 0.864188f, 0.442863f}, { -0.262866, 0.951056, 0.162460}, +0.262866f, 0.951056f, 0.162460f}, { -0.500000, 0.809017, 0.309017}, +0.500000f, 0.809017f, 0.309017f}, { -0.238856, 0.864188, -0.442863}, +0.238856f, 0.864188f, -0.442863f}, { -0.262866, 0.951056, -0.162460}, +0.262866f, 0.951056f, -0.162460f}, { -0.500000, 0.809017, -0.309017}, +0.500000f, 0.809017f, -0.309017f}, { -0.850651, 0.525731, 0.000000}, +0.850651f, 0.525731f, 0.000000f}, { -0.716567, 0.681718, 0.147621}, +0.716567f, 0.681718f, 0.147621f}, { -0.716567, 0.681718, -0.147621}, +0.716567f, 0.681718f, -0.147621f}, { -0.525731, 0.850651, 0.000000}, +0.525731f, 0.850651f, 0.000000f}, { -0.425325, 0.688191, 0.587785}, +0.425325f, 0.688191f, 0.587785f}, { -0.864188, 0.442863, 0.238856}, +0.864188f, 0.442863f, 0.238856f}, { -0.688191, 0.587785, 0.425325}, +0.688191f, 0.587785f, 0.425325f}, { -0.809017, 0.309017, 0.500000}, +0.809017f, 0.309017f, 0.500000f}, { -0.681718, 0.147621, 0.716567}, +0.681718f, 0.147621f, 0.716567f}, { -0.587785, 0.425325, 0.688191}, +0.587785f, 0.425325f, 0.688191f}, { -0.955423, 0.295242, 0.000000}, +0.955423f, 0.295242f, 0.000000f}, { -1.000000, 0.000000, 0.000000}, +1.000000f, 0.000000f, 0.000000f}, { -0.951056, 0.162460, 0.262866}, +0.951056f, 0.162460f, 0.262866f}, { -0.850651, -0.525731, 0.000000}, +0.850651f, -0.525731f, 0.000000f}, { -0.955423, -0.295242, 0.000000}, +0.955423f, -0.295242f, 0.000000f}, { -0.864188, -0.442863, 0.238856}, +0.864188f, -0.442863f, 0.238856f}, { -0.951056, -0.162460, 0.262866}, +0.951056f, -0.162460f, 0.262866f}, { -0.809017, -0.309017, 0.500000}, +0.809017f, -0.309017f, 0.500000f}, { -0.681718, -0.147621, 0.716567}, +0.681718f, -0.147621f, 0.716567f}, { -0.850651, 0.000000, 0.525731}, +0.850651f, 0.000000f, 0.525731f}, { -0.864188, 0.442863, -0.238856}, +0.864188f, 0.442863f, -0.238856f}, { -0.809017, 0.309017, -0.500000}, +0.809017f, 0.309017f, -0.500000f}, { -0.951056, 0.162460, -0.262866}, +0.951056f, 0.162460f, -0.262866f}, { -0.525731, 0.000000, -0.850651}, +0.525731f, 0.000000f, -0.850651f}, { -0.681718, 0.147621, -0.716567}, +0.681718f, 0.147621f, -0.716567f}, { -0.681718, -0.147621, -0.716567}, +0.681718f, -0.147621f, -0.716567f}, { -0.850651, 0.000000, -0.525731}, +0.850651f, 0.000000f, -0.525731f}, { -0.809017, -0.309017, -0.500000}, +0.809017f, -0.309017f, -0.500000f}, { -0.864188, -0.442863, -0.238856}, +0.864188f, -0.442863f, -0.238856f}, { -0.951056, -0.162460, -0.262866}, +0.951056f, -0.162460f, -0.262866f}, { -0.147621, 0.716567, -0.681718}, +0.147621f, 0.716567f, -0.681718f}, { -0.309017, 0.500000, -0.809017}, +0.309017f, 0.500000f, -0.809017f}, { -0.425325, 0.688191, -0.587785}, +0.425325f, 0.688191f, -0.587785f}, { -0.442863, 0.238856, -0.864188}, +0.442863f, 0.238856f, -0.864188f}, { -0.587785, 0.425325, -0.688191}, +0.587785f, 0.425325f, -0.688191f}, { -0.688191, 0.587785, -0.425325}, +0.688191f, 0.587785f, -0.425325f}, { --0.147621, 0.716567, -0.681718}, +-0.147621f, 0.716567f, -0.681718f}, { --0.309017, 0.500000, -0.809017}, +-0.309017f, 0.500000f, -0.809017f}, { -0.000000, 0.525731, -0.850651}, +0.000000f, 0.525731f, -0.850651f}, { --0.525731, 0.000000, -0.850651}, +-0.525731f, 0.000000f, -0.850651f}, { --0.442863, 0.238856, -0.864188}, +-0.442863f, 0.238856f, -0.864188f}, { --0.295242, 0.000000, -0.955423}, +-0.295242f, 0.000000f, -0.955423f}, { --0.162460, 0.262866, -0.951056}, +-0.162460f, 0.262866f, -0.951056f}, { -0.000000, 0.000000, -1.000000}, +0.000000f, 0.000000f, -1.000000f}, { -0.295242, 0.000000, -0.955423}, +0.295242f, 0.000000f, -0.955423f}, { -0.162460, 0.262866, -0.951056}, +0.162460f, 0.262866f, -0.951056f}, { --0.442863, -0.238856, -0.864188}, +-0.442863f, -0.238856f, -0.864188f}, { --0.309017, -0.500000, -0.809017}, +-0.309017f, -0.500000f, -0.809017f}, { --0.162460, -0.262866, -0.951056}, +-0.162460f, -0.262866f, -0.951056f}, { -0.000000, -0.850651, -0.525731}, +0.000000f, -0.850651f, -0.525731f}, { --0.147621, -0.716567, -0.681718}, +-0.147621f, -0.716567f, -0.681718f}, { -0.147621, -0.716567, -0.681718}, +0.147621f, -0.716567f, -0.681718f}, { -0.000000, -0.525731, -0.850651}, +0.000000f, -0.525731f, -0.850651f}, { -0.309017, -0.500000, -0.809017}, +0.309017f, -0.500000f, -0.809017f}, { -0.442863, -0.238856, -0.864188}, +0.442863f, -0.238856f, -0.864188f}, { -0.162460, -0.262866, -0.951056}, +0.162460f, -0.262866f, -0.951056f}, { -0.238856, -0.864188, -0.442863}, +0.238856f, -0.864188f, -0.442863f}, { -0.500000, -0.809017, -0.309017}, +0.500000f, -0.809017f, -0.309017f}, { -0.425325, -0.688191, -0.587785}, +0.425325f, -0.688191f, -0.587785f}, { -0.716567, -0.681718, -0.147621}, +0.716567f, -0.681718f, -0.147621f}, { -0.688191, -0.587785, -0.425325}, +0.688191f, -0.587785f, -0.425325f}, { -0.587785, -0.425325, -0.688191}, +0.587785f, -0.425325f, -0.688191f}, { -0.000000, -0.955423, -0.295242}, +0.000000f, -0.955423f, -0.295242f}, { -0.000000, -1.000000, 0.000000}, +0.000000f, -1.000000f, 0.000000f}, { -0.262866, -0.951056, -0.162460}, +0.262866f, -0.951056f, -0.162460f}, { -0.000000, -0.850651, 0.525731}, +0.000000f, -0.850651f, 0.525731f}, { -0.000000, -0.955423, 0.295242}, +0.000000f, -0.955423f, 0.295242f}, { -0.238856, -0.864188, 0.442863}, +0.238856f, -0.864188f, 0.442863f}, { -0.262866, -0.951056, 0.162460}, +0.262866f, -0.951056f, 0.162460f}, { -0.500000, -0.809017, 0.309017}, +0.500000f, -0.809017f, 0.309017f}, { -0.716567, -0.681718, 0.147621}, +0.716567f, -0.681718f, 0.147621f}, { -0.525731, -0.850651, 0.000000}, +0.525731f, -0.850651f, 0.000000f}, { --0.238856, -0.864188, -0.442863}, +-0.238856f, -0.864188f, -0.442863f}, { --0.500000, -0.809017, -0.309017}, +-0.500000f, -0.809017f, -0.309017f}, { --0.262866, -0.951056, -0.162460}, +-0.262866f, -0.951056f, -0.162460f}, { --0.850651, -0.525731, 0.000000}, +-0.850651f, -0.525731f, 0.000000f}, { --0.716567, -0.681718, -0.147621}, +-0.716567f, -0.681718f, -0.147621f}, { --0.716567, -0.681718, 0.147621}, +-0.716567f, -0.681718f, 0.147621f}, { --0.525731, -0.850651, 0.000000}, +-0.525731f, -0.850651f, 0.000000f}, { --0.500000, -0.809017, 0.309017}, +-0.500000f, -0.809017f, 0.309017f}, { --0.238856, -0.864188, 0.442863}, +-0.238856f, -0.864188f, 0.442863f}, { --0.262866, -0.951056, 0.162460}, +-0.262866f, -0.951056f, 0.162460f}, { --0.864188, -0.442863, 0.238856}, +-0.864188f, -0.442863f, 0.238856f}, { --0.809017, -0.309017, 0.500000}, +-0.809017f, -0.309017f, 0.500000f}, { --0.688191, -0.587785, 0.425325}, +-0.688191f, -0.587785f, 0.425325f}, { --0.681718, -0.147621, 0.716567}, +-0.681718f, -0.147621f, 0.716567f}, { --0.442863, -0.238856, 0.864188}, +-0.442863f, -0.238856f, 0.864188f}, { --0.587785, -0.425325, 0.688191}, +-0.587785f, -0.425325f, 0.688191f}, { --0.309017, -0.500000, 0.809017}, +-0.309017f, -0.500000f, 0.809017f}, { --0.147621, -0.716567, 0.681718}, +-0.147621f, -0.716567f, 0.681718f}, { --0.425325, -0.688191, 0.587785}, +-0.425325f, -0.688191f, 0.587785f}, { --0.162460, -0.262866, 0.951056}, +-0.162460f, -0.262866f, 0.951056f}, { -0.442863, -0.238856, 0.864188}, +0.442863f, -0.238856f, 0.864188f}, { -0.162460, -0.262866, 0.951056}, +0.162460f, -0.262866f, 0.951056f}, { -0.309017, -0.500000, 0.809017}, +0.309017f, -0.500000f, 0.809017f}, { -0.147621, -0.716567, 0.681718}, +0.147621f, -0.716567f, 0.681718f}, { -0.000000, -0.525731, 0.850651}, +0.000000f, -0.525731f, 0.850651f}, { -0.425325, -0.688191, 0.587785}, +0.425325f, -0.688191f, 0.587785f}, { -0.587785, -0.425325, 0.688191}, +0.587785f, -0.425325f, 0.688191f}, { -0.688191, -0.587785, 0.425325}, +0.688191f, -0.587785f, 0.425325f}, { --0.955423, 0.295242, 0.000000}, +-0.955423f, 0.295242f, 0.000000f}, { --0.951056, 0.162460, 0.262866}, +-0.951056f, 0.162460f, 0.262866f}, { --1.000000, 0.000000, 0.000000}, +-1.000000f, 0.000000f, 0.000000f}, { --0.850651, 0.000000, 0.525731}, +-0.850651f, 0.000000f, 0.525731f}, { --0.955423, -0.295242, 0.000000}, +-0.955423f, -0.295242f, 0.000000f}, { --0.951056, -0.162460, 0.262866}, +-0.951056f, -0.162460f, 0.262866f}, { --0.864188, 0.442863, -0.238856}, +-0.864188f, 0.442863f, -0.238856f}, { --0.951056, 0.162460, -0.262866}, +-0.951056f, 0.162460f, -0.262866f}, { --0.809017, 0.309017, -0.500000}, +-0.809017f, 0.309017f, -0.500000f}, { --0.864188, -0.442863, -0.238856}, +-0.864188f, -0.442863f, -0.238856f}, { --0.951056, -0.162460, -0.262866}, +-0.951056f, -0.162460f, -0.262866f}, { --0.809017, -0.309017, -0.500000}, +-0.809017f, -0.309017f, -0.500000f}, { --0.681718, 0.147621, -0.716567}, +-0.681718f, 0.147621f, -0.716567f}, { --0.681718, -0.147621, -0.716567}, +-0.681718f, -0.147621f, -0.716567f}, { --0.850651, 0.000000, -0.525731}, +-0.850651f, 0.000000f, -0.525731f}, { --0.688191, 0.587785, -0.425325}, +-0.688191f, 0.587785f, -0.425325f}, { --0.587785, 0.425325, -0.688191}, +-0.587785f, 0.425325f, -0.688191f}, { --0.425325, 0.688191, -0.587785}, +-0.425325f, 0.688191f, -0.587785f}, { --0.425325, -0.688191, -0.587785}, +-0.425325f, -0.688191f, -0.587785f}, { --0.587785, -0.425325, -0.688191}, +-0.587785f, -0.425325f, -0.688191f}, { --0.688191, -0.587785, -0.425325}, +-0.688191f, -0.587785f, -0.425325f}, diff --git a/src/gl/renderer/gl_2ddrawer.cpp b/src/gl/renderer/gl_2ddrawer.cpp index 1907edeefd..9d2449cbd9 100644 --- a/src/gl/renderer/gl_2ddrawer.cpp +++ b/src/gl/renderer/gl_2ddrawer.cpp @@ -139,7 +139,7 @@ void F2DDrawer::AddTexture(FTexture *img, DrawParms &parms) { color = PalEntry(light, light, light); } - color.a = (BYTE)(parms.Alpha * 255); + color.a = (uint8_t)(parms.Alpha * 255); // scissor test doesn't use the current viewport for the coordinates, so use real screen coordinates dg.mScissor[0] = GLRenderer->ScreenToWindowX(parms.lclip); @@ -253,7 +253,7 @@ void F2DDrawer::AddDim(PalEntry color, float damount, int x1, int y1, int w, int // //========================================================================== -void F2DDrawer::AddClear(int left, int top, int right, int bottom, int palcolor, uint32 color) +void F2DDrawer::AddClear(int left, int top, int right, int bottom, int palcolor, uint32_t color) { PalEntry p = palcolor == -1 || color != 0 ? (PalEntry)color : GPalette.BaseColors[palcolor]; AddDim(p, 1.f, left, top, right - left, bottom - top); @@ -311,7 +311,7 @@ void F2DDrawer::AddFlatFill(int left, int top, int right, int bottom, FTexture * // //========================================================================== -void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color) +void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color) { PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor]; p.a = 255; @@ -344,7 +344,7 @@ void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32 col // //========================================================================== -void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32 color) +void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32_t color) { PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor]; p.a = 255; @@ -372,7 +372,7 @@ void F2DDrawer::Draw() F2DDrawer::EDrawType lasttype = DrawTypeTexture; if (mData.Size() == 0) return; - SBYTE savedlightmode = glset.lightmode; + int8_t savedlightmode = glset.lightmode; // lightmode is only relevant for automap subsectors, // but We cannot use the software light mode here because it doesn't properly calculate the light for 2D rendering. if (glset.lightmode == 8) glset.lightmode = 0; diff --git a/src/gl/renderer/gl_2ddrawer.h b/src/gl/renderer/gl_2ddrawer.h index 61f3153d38..80003b159e 100644 --- a/src/gl/renderer/gl_2ddrawer.h +++ b/src/gl/renderer/gl_2ddrawer.h @@ -57,15 +57,15 @@ class F2DDrawer : public FSimpleVertexBuffer public: void AddTexture(FTexture *img, DrawParms &parms); void AddDim(PalEntry color, float damount, int x1, int y1, int w, int h); - void AddClear(int left, int top, int right, int bottom, int palcolor, uint32 color); + void AddClear(int left, int top, int right, int bottom, int palcolor, uint32_t color); void AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin); void AddPoly(FTexture *texture, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel); - void AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color); - void AddPixel(int x1, int y1, int palcolor, uint32 color); + void AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color); + void AddPixel(int x1, int y1, int palcolor, uint32_t color); void Draw(); void Clear(); diff --git a/src/gl/renderer/gl_colormap.h b/src/gl/renderer/gl_colormap.h index 2c9fb91a4c..4c755856bc 100644 --- a/src/gl/renderer/gl_colormap.h +++ b/src/gl/renderer/gl_colormap.h @@ -79,5 +79,4 @@ struct FColormap }; - #endif diff --git a/src/gl/renderer/gl_lightdata.cpp b/src/gl/renderer/gl_lightdata.cpp index c017453bda..180edcc986 100644 --- a/src/gl/renderer/gl_lightdata.cpp +++ b/src/gl/renderer/gl_lightdata.cpp @@ -59,7 +59,6 @@ CUSTOM_CVAR(Bool, gl_enhanced_nightvision, true, CVAR_ARCHIVE|CVAR_NOINITCALL) } CVAR(Bool, gl_brightfog, false, CVAR_ARCHIVE); CVAR(Bool, gl_lightadditivesurfaces, false, CVAR_ARCHIVE); -CVAR(Bool, gl_attenuate, false, CVAR_ARCHIVE); @@ -253,7 +252,7 @@ static PalEntry gl_CalcLightColor(int light, PalEntry pe, int blendfactor) g = (mixlight + pe.g * blendfactor) / 255; b = (mixlight + pe.b * blendfactor) / 255; } - return PalEntry(255, BYTE(r), BYTE(g), BYTE(b)); + return PalEntry(255, uint8_t(r), uint8_t(g), uint8_t(b)); } //========================================================================== diff --git a/src/gl/renderer/gl_lightdata.h b/src/gl/renderer/gl_lightdata.h index 7af5e7a4f8..7b5f356ecb 100644 --- a/src/gl/renderer/gl_lightdata.h +++ b/src/gl/renderer/gl_lightdata.h @@ -42,9 +42,6 @@ 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; diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 72121d338f..4688e0393e 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -529,7 +529,7 @@ void FGLRenderer::CreateTonemapPalette() { for (int b = 0; b < 64; b++) { - PalEntry color = GPalette.BaseColors[(BYTE)PTM_BestColor((uint32 *)GPalette.BaseColors, (r << 2) | (r >> 4), (g << 2) | (g >> 4), (b << 2) | (b >> 4), 0, 256)]; + PalEntry color = GPalette.BaseColors[(uint8_t)PTM_BestColor((uint32_t *)GPalette.BaseColors, (r << 2) | (r >> 4), (g << 2) | (g >> 4), (b << 2) | (b >> 4), 0, 256)]; int index = ((r * 64 + g) * 64 + b) * 4; lut[index] = color.r; lut[index + 1] = color.g; @@ -832,7 +832,7 @@ void FGLRenderer::ClearBorders() // [SP] Re-implemented BestColor for more precision rather than speed. This function is only ever called once until the game palette is changed. -int FGLRenderer::PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num) +int FGLRenderer::PTM_BestColor (const uint32_t *pal_in, int r, int g, int b, int first, int num) { const PalEntry *pal = (const PalEntry *)pal_in; static double powtable[256]; @@ -840,7 +840,7 @@ int FGLRenderer::PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int f static float trackpowtable = 0.; double fbestdist, fdist; - int bestcolor; + int bestcolor = 0; if (firstTime || trackpowtable != gl_paltonemap_powtable) { diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index fad79767c6..74dc36e729 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -26,7 +26,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" @@ -741,6 +740,49 @@ void FGLRenderBuffers::BindEyeFB(int eye, bool readBuffer) glBindFramebuffer(readBuffer ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER, mEyeFBs[eye]); } +//========================================================================== +// +// Shadow map texture and frame buffers +// +//========================================================================== + +void FGLRenderBuffers::BindShadowMapFB() +{ + CreateShadowMap(); + glBindFramebuffer(GL_FRAMEBUFFER, mShadowMapFB); +} + +void FGLRenderBuffers::BindShadowMapTexture(int texunit) +{ + CreateShadowMap(); + glActiveTexture(GL_TEXTURE0 + texunit); + glBindTexture(GL_TEXTURE_2D, mShadowMapTexture); +} + +void FGLRenderBuffers::CreateShadowMap() +{ + if (mShadowMapTexture != 0) + return; + + GLint activeTex, textureBinding, frameBufferBinding; + glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex); + glActiveTexture(GL_TEXTURE0); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding); + + mShadowMapTexture = Create2DTexture("ShadowMap", GL_R32F, 1024, 1024); + 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); + + mShadowMapFB = CreateFrameBuffer("ShadowMapFB", mShadowMapTexture); + + glBindTexture(GL_TEXTURE_2D, textureBinding); + glActiveTexture(activeTex); + glBindFramebuffer(GL_FRAMEBUFFER, frameBufferBinding); +} + //========================================================================== // // Makes the scene frame buffer active (multisample, depth, stecil, etc.) diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index f2f7b7cb9f..5df3bcca05 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -49,6 +49,9 @@ public: void BindEyeTexture(int eye, int texunit); void BindEyeFB(int eye, bool readBuffer = false); + void BindShadowMapFB(); + void BindShadowMapTexture(int index); + enum { NumBloomLevels = 4 }; FGLBloomTextureLevel BloomLevels[NumBloomLevels]; @@ -89,6 +92,7 @@ private: void CreateBloom(int width, int height); void CreateExposureLevels(int width, int height); void CreateEyeBuffers(int eye); + void CreateShadowMap(); void CreateAmbientOcclusion(int width, int height); GLuint Create2DTexture(const FString &name, GLuint format, int width, int height, const void *data = nullptr); GLuint Create2DMultisampleTexture(const FString &name, GLuint format, int width, int height, int samples, bool fixedSampleLocations); @@ -133,6 +137,10 @@ private: TArray mEyeTextures; TArray mEyeFBs; + // Shadow map texture + GLuint mShadowMapTexture = 0; + GLuint mShadowMapFB = 0; + static bool FailedCreate; static bool BuffersActive; }; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 6021411bee..f5bf481b66 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -60,6 +60,7 @@ #include "gl/shaders/gl_fxaashader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/shaders/gl_present3dRowshader.h" +#include "gl/shaders/gl_shadowmapshader.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" @@ -125,6 +126,7 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mSSAOCombineShader = nullptr; mFXAAShader = nullptr; mFXAALumaShader = nullptr; + mShadowMapShader = nullptr; } void gl_LoadModels(); @@ -153,6 +155,7 @@ void FGLRenderer::Initialize(int width, int height) mPresent3dCheckerShader = new FPresent3DCheckerShader(); mPresent3dColumnShader = new FPresent3DColumnShader(); mPresent3dRowShader = new FPresent3DRowShader(); + mShadowMapShader = new FShadowMapShader(); m2DDrawer = new F2DDrawer; // needed for the core profile, because someone decided it was a good idea to remove the default VAO. @@ -186,7 +189,7 @@ void FGLRenderer::Initialize(int width, int height) FGLRenderer::~FGLRenderer() { gl_FlushModels(); - gl_DeleteAllAttachedLights(); + AActor::DeleteAllAttachedLights(); FMaterial::FlushAll(); if (m2DDrawer != nullptr) delete m2DDrawer; if (mShaderManager != NULL) delete mShaderManager; @@ -223,6 +226,7 @@ FGLRenderer::~FGLRenderer() if (mTonemapPalette) delete mTonemapPalette; if (mColormapShader) delete mColormapShader; if (mLensShader) delete mLensShader; + if (mShadowMapShader) delete mShadowMapShader; delete mFXAAShader; delete mFXAALumaShader; } diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index b784e74fdb..0619da0c65 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -6,6 +6,7 @@ #include "vectors.h" #include "r_renderer.h" #include "gl/data/gl_matrix.h" +#include "gl/dynlights/gl_shadowmap.h" struct particle_t; class FCanvasTexture; @@ -40,6 +41,7 @@ class FPresent3DColumnShader; class FPresent3DRowShader; class F2DDrawer; class FHardwareTexture; +class FShadowMapShader; inline float DEG2RAD(float deg) { @@ -123,6 +125,9 @@ public: FPresent3DCheckerShader *mPresent3dCheckerShader; FPresent3DColumnShader *mPresent3dColumnShader; FPresent3DRowShader *mPresent3dRowShader; + FShadowMapShader *mShadowMapShader; + + FShadowMap mShadowMap; FTexture *gllight; FTexture *glpart2; @@ -153,23 +158,8 @@ public: int ScreenToWindowX(int x); int ScreenToWindowY(int y); - angle_t FrustumAngle(); - void SetViewArea(); - void Set3DViewport(bool mainview); - void Reset3DViewport(); - 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(DAngle viewangle); - void SetupView(float viewx, float viewy, float viewz, DAngle viewangle, bool mirror, bool planemirror); - void Initialize(int width, int height); - void CreateScene(); - void RenderMultipassStuff(); - void RenderScene(int recursion); - void RenderTranslucent(); - void DrawScene(int drawmode); - void DrawBlend(sector_t * viewsector); void DrawPSprite (player_t * player,DPSprite *psp,float sx, float sy, bool hudModelStep, int OverrideShader, bool alphatexture); void DrawPlayerSprites(sector_t * viewsector, bool hudModelStep); @@ -187,12 +177,9 @@ public: void SetupLevel(); void RenderScreenQuad(); - void SetFixedColormap (player_t *player); - void WriteSavePic (player_t *player, FileWriter *file, int width, int height); - void EndDrawScene(sector_t * viewsector); - void UpdateCameraExposure(); void PostProcessScene(); void AmbientOccludeScene(); + void UpdateCameraExposure(); void BloomScene(); void TonemapScene(); void ColormapScene(); @@ -204,10 +191,6 @@ public: void DrawPresentTexture(const GL_IRECT &box, bool applyGamma); void Flush(); - void SetProjection(float fov, float ratio, float fovratio); - void SetProjection(VSMatrix matrix); // raw matrix input from stereo 3d modes - void SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror); - void ProcessScene(bool toscreen = false); bool StartOffscreen(); void EndOffscreen(); @@ -216,7 +199,7 @@ public: double originx, double originy, double scalex, double scaley, DAngle rotation, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip); - int PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num); + int PTM_BestColor (const uint32_t *pal_in, int r, int g, int b, int first, int num); static float GetZNear() { return 5.f; } static float GetZFar() { return 65536.f; } diff --git a/src/gl/scene/gl_bsp.cpp b/src/gl/scene/gl_bsp.cpp index 9b78850a90..fc80ed8743 100644 --- a/src/gl/scene/gl_bsp.cpp +++ b/src/gl/scene/gl_bsp.cpp @@ -33,35 +33,31 @@ #include "p_effect.h" #include "po_man.h" #include "doomdata.h" +#include "g_levellocals.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_scenedrawer.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) -extern fixed_t viewx, viewy; - -static void UnclipSubsector(subsector_t *sub) +void GLSceneDrawer::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(); + angle_t startAngle = clipper.GetClipAngle(seg->v2); + angle_t endAngle = clipper.GetClipAngle(seg->v1); // Back side, i.e. backface culling - read: endAngle >= startAngle! if (startAngle-endAngle >= ANGLE_180) @@ -81,11 +77,7 @@ static void UnclipSubsector(subsector_t *sub) // //========================================================================== -// 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, bool portalclip) +void GLSceneDrawer::AddLine (seg_t *seg, bool portalclip) { #ifdef _DEBUG if (seg->linedef->Index() == 38) @@ -94,7 +86,6 @@ static void AddLine (seg_t *seg, bool portalclip) } #endif - angle_t startAngle, endAngle; sector_t * backsector = NULL; sector_t bs; @@ -104,8 +95,8 @@ static void AddLine (seg_t *seg, bool portalclip) if (clipres == GLPortal::PClip_InFront) return; } - startAngle = seg->v2->GetClipAngle(); - endAngle = seg->v1->GetClipAngle(); + angle_t startAngle = clipper.GetClipAngle(seg->v2); + angle_t endAngle = clipper.GetClipAngle(seg->v1); // Back side, i.e. backface culling - read: endAngle >= startAngle! if (startAngle-endAngleflags |= SSECF_DRAWN; - BYTE ispoly = BYTE(seg->sidedef->Flags & WALLF_POLYOBJ); + uint8_t ispoly = uint8_t(seg->sidedef->Flags & WALLF_POLYOBJ); if (!seg->backsector) { @@ -201,7 +192,7 @@ static void AddLine (seg_t *seg, bool portalclip) // //========================================================================== -static void PolySubsector(subsector_t * sub) +void GLSceneDrawer::PolySubsector(subsector_t * sub) { int count = sub->numlines; seg_t * line = sub->firstline; @@ -225,7 +216,7 @@ static void PolySubsector(subsector_t * sub) // //========================================================================== -static void RenderPolyBSPNode (void *node) +void GLSceneDrawer::RenderPolyBSPNode (void *node) { while (!((size_t)node & 1)) // Keep going until found a subsector { @@ -248,7 +239,7 @@ static void RenderPolyBSPNode (void *node) node = bsp->children[side]; } - PolySubsector ((subsector_t *)((BYTE *)node - 1)); + PolySubsector ((subsector_t *)((uint8_t *)node - 1)); } //========================================================================== @@ -258,7 +249,7 @@ static void RenderPolyBSPNode (void *node) // //========================================================================== -static void AddPolyobjs(subsector_t *sub) +void GLSceneDrawer::AddPolyobjs(subsector_t *sub) { if (sub->BSP == NULL || sub->BSP->bDirty) { @@ -286,7 +277,7 @@ static void AddPolyobjs(subsector_t *sub) // //========================================================================== -static inline void AddLines(subsector_t * sub, sector_t * sector) +void GLSceneDrawer::AddLines(subsector_t * sub, sector_t * sector) { currentsector = sector; currentsubsector = sub; @@ -330,7 +321,7 @@ inline bool PointOnLine(const DVector2 &pos, const line_t *line) return fabs(v) <= EQUAL_EPSILON; } -static inline void AddSpecialPortalLines(subsector_t * sub, sector_t * sector, line_t *line) +void GLSceneDrawer::AddSpecialPortalLines(subsector_t * sub, sector_t * sector, line_t *line) { currentsector = sector; currentsubsector = sub; @@ -358,9 +349,8 @@ static inline void AddSpecialPortalLines(subsector_t * sub, sector_t * sector, l // //========================================================================== -static inline void RenderThings(subsector_t * sub, sector_t * sector) +void GLSceneDrawer::RenderThings(subsector_t * sub, sector_t * sector) { - SetupSprite.Clock(); sector_t * sec=sub->sector; // Handle all things in sector. @@ -373,7 +363,7 @@ static inline void RenderThings(subsector_t * sub, sector_t * sector) FIntCVar *cvar = thing->GetClass()->distancecheck; if (cvar != NULL && *cvar >= 0) { - double dist = (thing->Pos() - ViewPos).LengthSquared(); + double dist = (thing->Pos() - r_viewpoint.Pos).LengthSquared(); double check = (double)**cvar; if (dist >= check * check) { @@ -390,7 +380,7 @@ static inline void RenderThings(subsector_t * sub, sector_t * sector) FIntCVar *cvar = thing->GetClass()->distancecheck; if (cvar != NULL && *cvar >= 0) { - double dist = (thing->Pos() - ViewPos).LengthSquared(); + double dist = (thing->Pos() - r_viewpoint.Pos).LengthSquared(); double check = (double)**cvar; if (dist >= check * check) { @@ -413,7 +403,7 @@ static inline void RenderThings(subsector_t * sub, sector_t * sector) // //========================================================================== -static void DoSubsector(subsector_t * sub) +void GLSceneDrawer::DoSubsector(subsector_t * sub) { unsigned int i; sector_t * sector; @@ -468,7 +458,7 @@ static void DoSubsector(subsector_t * sub) { SetupSprite.Clock(); - for (i = ParticlesInSubsec[DWORD(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) + for (i = ParticlesInSubsec[uint32_t(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) { GLRenderer->ProcessParticle(&Particles[i], fakesector); } @@ -512,7 +502,7 @@ static void DoSubsector(subsector_t * sub) fakesector = gl_FakeFlat(sector, &fake, false); } - BYTE &srf = gl_drawinfo->sectorrenderflags[sub->render_sector->sectornum]; + uint8_t &srf = gl_drawinfo->sectorrenderflags[sub->render_sector->sectornum]; if (!(srf & SSRF_PROCESSED)) { srf |= SSRF_PROCESSED; @@ -558,7 +548,7 @@ static void DoSubsector(subsector_t * sub) // //========================================================================== -void gl_RenderBSPNode (void *node) +void GLSceneDrawer::RenderBSPNode (void *node) { if (numnodes == 0) { @@ -573,7 +563,7 @@ void gl_RenderBSPNode (void *node) int side = R_PointOnSide(viewx, viewy, bsp); // Recursively divide front space (toward the viewer). - gl_RenderBSPNode (bsp->children[side]); + RenderBSPNode (bsp->children[side]); // Possibly divide back space (away from the viewer). side ^= 1; @@ -587,7 +577,7 @@ void gl_RenderBSPNode (void *node) node = bsp->children[side]; } - DoSubsector ((subsector_t *)((BYTE *)node - 1)); + DoSubsector ((subsector_t *)((uint8_t *)node - 1)); } diff --git a/src/gl/scene/gl_clipper.cpp b/src/gl/scene/gl_clipper.cpp index cd076d58aa..f82375800a 100644 --- a/src/gl/scene/gl_clipper.cpp +++ b/src/gl/scene/gl_clipper.cpp @@ -36,28 +36,12 @@ */ #include "gl/scene/gl_clipper.h" +#include "g_levellocals.h" - -ClipNode * ClipNode::freelist; -int Clipper::anglecache; - - -//----------------------------------------------------------------------------- -// -// Destructor -// -//----------------------------------------------------------------------------- - -Clipper::~Clipper() +Clipper::Clipper() { - Clear(); - while (ClipNode::freelist != NULL) - { - ClipNode * node = ClipNode::freelist; - ClipNode::freelist = node->next; - delete node; - } + starttime = I_MSTime(); } //----------------------------------------------------------------------------- @@ -78,7 +62,7 @@ void Clipper::RemoveRange(ClipNode * range) if (range->next) range->next->prev = range->prev; } - range->Free(); + Free(range); } //----------------------------------------------------------------------------- @@ -97,7 +81,7 @@ void Clipper::Clear() { temp = node; node = node->next; - temp->Free(); + Free(temp); } node = silhouette; @@ -105,12 +89,12 @@ void Clipper::Clear() { temp = node; node = node->next; - temp->Free(); + Free(temp); } cliphead = NULL; silhouette = NULL; - anglecache++; + starttime = I_MSTime(); } //----------------------------------------------------------------------------- @@ -126,7 +110,7 @@ void Clipper::SetSilhouette() while (node != NULL) { - ClipNode *snode = ClipNode::NewRange(node->start, node->end); + ClipNode *snode = NewRange(node->start, node->end); if (silhouette == NULL) silhouette = snode; snode->prev = last; if (last != NULL) last->next = snode; @@ -135,7 +119,6 @@ void Clipper::SetSilhouette() } } - //----------------------------------------------------------------------------- // // IsRangeVisible @@ -227,7 +210,7 @@ void Clipper::AddClipRange(angle_t start, angle_t end) //just add range node = cliphead; prevNode = NULL; - temp = ClipNode::NewRange(start, end); + temp = NewRange(start, end); while (node != NULL && node->start < end) { @@ -259,7 +242,7 @@ void Clipper::AddClipRange(angle_t start, angle_t end) } else { - temp = ClipNode::NewRange(start, end); + temp = NewRange(start, end); cliphead = temp; return; } @@ -343,7 +326,7 @@ void Clipper::DoRemoveClipRange(angle_t start, angle_t end) } else if (node->start < start && node->end > end) { - temp=ClipNode::NewRange(end, node->end); + temp = NewRange(end, node->end); node->end=start; temp->next=node->next; temp->prev=node; @@ -389,18 +372,10 @@ angle_t Clipper::AngleToPseudo(angle_t ang) // //----------------------------------------------------------------------------- -fixed_t viewx, viewy; - -void R_SetView() -{ - viewx = FLOAT2FIXED(ViewPos.X); - viewy = FLOAT2FIXED(ViewPos.Y); -} - angle_t R_PointToPseudoAngle(double x, double y) { - double vecx = x - ViewPos.X; - double vecy = y - ViewPos.Y; + double vecx = x - r_viewpoint.Pos.X; + double vecy = y - r_viewpoint.Pos.Y; if (vecx == 0 && vecy == 0) { @@ -411,7 +386,7 @@ angle_t R_PointToPseudoAngle(double x, double y) double result = vecy / (fabs(vecx) + fabs(vecy)); if (vecx < 0) { - result = 2.f - result; + result = 2. - result; } return xs_Fix<30>::ToFix(result); } @@ -427,7 +402,7 @@ angle_t R_PointToPseudoAngle(double x, double y) // if some part of the bbox might be visible. // //----------------------------------------------------------------------------- - static const BYTE checkcoord[12][4] = // killough -- static const + static const uint8_t checkcoord[12][4] = // killough -- static const { {3,0,2,1}, {3,0,2,0}, @@ -447,12 +422,12 @@ bool Clipper::CheckBox(const float *bspcoord) angle_t angle1, angle2; int boxpos; - const BYTE* check; + const uint8_t* check; // Find the corners of the box // that define the edges from current viewpoint. - boxpos = (ViewPos.X <= bspcoord[BOXLEFT] ? 0 : ViewPos.X < bspcoord[BOXRIGHT ] ? 1 : 2) + - (ViewPos.Y >= bspcoord[BOXTOP ] ? 0 : ViewPos.Y > bspcoord[BOXBOTTOM] ? 4 : 8); + boxpos = (r_viewpoint.Pos.X <= bspcoord[BOXLEFT] ? 0 : r_viewpoint.Pos.X < bspcoord[BOXRIGHT ] ? 1 : 2) + + (r_viewpoint.Pos.Y >= bspcoord[BOXTOP ] ? 0 : r_viewpoint.Pos.Y > bspcoord[BOXBOTTOM] ? 4 : 8); if (boxpos == 5) return true; diff --git a/src/gl/scene/gl_clipper.h b/src/gl/scene/gl_clipper.h index 8da75080e8..9494a1243f 100644 --- a/src/gl/scene/gl_clipper.h +++ b/src/gl/scene/gl_clipper.h @@ -4,57 +4,41 @@ #include "doomtype.h" #include "xs_Float.h" #include "r_utility.h" +#include "memarena.h" + +angle_t R_PointToPseudoAngle(double x, double y); + +// Used to speed up angle calculations during clipping +inline angle_t vertex_t::GetClipAngle() +{ + return R_PointToPseudoAngle(p.X, p.Y); +} 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 - bool blocked; + unsigned starttime; + TStaticArray anglecache; + FMemArena nodearena; + ClipNode * freelist = nullptr; + + ClipNode * clipnodes = nullptr; + ClipNode * cliphead = nullptr; + ClipNode * silhouette = nullptr; // will be preserved even when RemoveClipRange is called + bool blocked = false; static angle_t AngleToPseudo(angle_t ang); bool IsRangeVisible(angle_t startangle, angle_t endangle); @@ -65,18 +49,36 @@ class Clipper public: - static int anglecache; - - Clipper() - { - blocked = false; - clipnodes=cliphead=NULL; - } - - ~Clipper(); + Clipper(); void Clear(); + void Free(ClipNode *node) + { + node->next = freelist; + freelist = node; + } + + ClipNode * GetNew() + { + if (freelist) + { + ClipNode * p = freelist; + freelist = p->next; + return p; + } + else return (ClipNode*)nodearena.Alloc(sizeof(ClipNode)); + } + + ClipNode * NewRange(angle_t start, angle_t end) + { + ClipNode * c = GetNew(); + + c->start = start; + c->end = end; + c->next = c->prev = NULL; + return c; + } void SetSilhouette(); @@ -142,18 +144,13 @@ public: } bool CheckBox(const float *bspcoord); + + // Used to speed up angle calculations during clipping + inline angle_t GetClipAngle(vertex_t *v) + { + return v->angletime == starttime ? v->viewangle : (v->angletime = starttime, v->viewangle = R_PointToPseudoAngle(v->p.X, v->p.Y)); + } + }; - -extern Clipper clipper; - -angle_t R_PointToPseudoAngle(double x, double y); -void R_SetView(); - -// 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(p.X, p.Y)); -} - #endif \ No newline at end of file diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index 7e9a3e6d28..a3de4b1cc9 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -299,7 +299,7 @@ void GLDrawList::SortWallIntoPlane(SortNode * head,SortNode * sort) GLFlat * fh=&flats[drawitems[head->itemindex].index]; GLWall * ws=&walls[drawitems[sort->itemindex].index]; - bool ceiling = fh->z > ViewPos.Z; + bool ceiling = fh->z > r_viewpoint.Pos.Z; if ((ws->ztop[0] > fh->z || ws->ztop[1] > fh->z) && (ws->zbottom[0] < fh->z || ws->zbottom[1] < fh->z)) { @@ -362,7 +362,7 @@ void GLDrawList::SortSpriteIntoPlane(SortNode * head,SortNode * sort) GLFlat * fh=&flats[drawitems[head->itemindex].index]; GLSprite * ss=&sprites[drawitems[sort->itemindex].index]; - bool ceiling = fh->z > ViewPos.Z; + bool ceiling = fh->z > r_viewpoint.Pos.Z; if ((ss->z1>fh->z && ss->z2z) || ss->modelframe) { @@ -770,7 +770,7 @@ void GLDrawList::DoDrawSorted(SortNode * head) if (drawitems[head->itemindex].rendertype == GLDIT_FLAT) { z = flats[drawitems[head->itemindex].index].z; - relation = z > ViewPos.Z ? 1 : -1; + relation = z > r_viewpoint.Pos.Z ? 1 : -1; } @@ -1186,9 +1186,9 @@ void FDrawInfo::DrawFloodedPlane(wallseg * ws, float planez, sector_t * sec, boo gl_SetFog(lightlevel, rel, &Colormap, false); gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false); - float fviewx = ViewPos.X; - float fviewy = ViewPos.Y; - float fviewz = ViewPos.Z; + float fviewx = r_viewpoint.Pos.X; + float fviewy = r_viewpoint.Pos.Y; + float fviewz = r_viewpoint.Pos.Z; gl_SetPlaneTextureRotation(&plane, gltexture); gl_RenderState.Apply(); @@ -1239,7 +1239,7 @@ void FDrawInfo::FloodUpperGap(seg_t * seg) double frontz = fakefsector->ceilingplane.ZatPoint(seg->v1); if (fakebsector->GetTexture(sector_t::ceiling)==skyflatnum) return; - if (backz < ViewPos.Z) return; + if (backz < r_viewpoint.Pos.Z) return; if (seg->sidedef == seg->linedef->sidedef[0]) { @@ -1292,7 +1292,7 @@ void FDrawInfo::FloodLowerGap(seg_t * seg) if (fakebsector->GetTexture(sector_t::floor) == skyflatnum) return; - if (fakebsector->GetPlaneTexZ(sector_t::floor) > ViewPos.Z) return; + if (fakebsector->GetPlaneTexZ(sector_t::floor) > r_viewpoint.Pos.Z) return; if (seg->sidedef == seg->linedef->sidedef[0]) { diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index 0e7296ee36..4c09dbce7d 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -203,12 +203,12 @@ struct FDrawInfo struct SubsectorHackInfo { subsector_t * sub; - BYTE flags; + uint8_t flags; }; - TArray sectorrenderflags; - TArray ss_renderflags; - TArray no_renderflags; + TArray sectorrenderflags; + TArray ss_renderflags; + TArray no_renderflags; TArray MissingUpperTextures; TArray MissingLowerTextures; diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index 61d146dd15..1197fa6f36 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -36,6 +36,7 @@ #include "portal.h" #include "templates.h" #include "g_levellocals.h" +#include "actorinlines.h" #include "gl/system/gl_interface.h" #include "gl/system/gl_cvars.h" @@ -610,7 +611,7 @@ void GLFlat::ProcessSector(sector_t * frontsector) // // // - if (frontsector->floorplane.ZatPoint(ViewPos) <= ViewPos.Z) + if (frontsector->floorplane.ZatPoint(r_viewpoint.Pos) <= r_viewpoint.Pos.Z) { // process the original floor first. @@ -670,7 +671,7 @@ void GLFlat::ProcessSector(sector_t * frontsector) // // // - if (frontsector->ceilingplane.ZatPoint(ViewPos) >= ViewPos.Z) + if (frontsector->ceilingplane.ZatPoint(r_viewpoint.Pos) >= r_viewpoint.Pos.Z) { // process the original ceiling first. @@ -760,7 +761,7 @@ void GLFlat::ProcessSector(sector_t * frontsector) double ff_top = rover->top.plane->ZatPoint(sector->centerspot); if (ff_top < lastceilingheight) { - if (ViewPos.Z <= rover->top.plane->ZatPoint(ViewPos)) + if (r_viewpoint.Pos.Z <= rover->top.plane->ZatPoint(r_viewpoint.Pos)) { SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG)); Colormap.FadeColor = frontsector->ColorMap->Fade; @@ -774,7 +775,7 @@ void GLFlat::ProcessSector(sector_t * frontsector) double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot); if (ff_bottom < lastceilingheight) { - if (ViewPos.Z <= rover->bottom.plane->ZatPoint(ViewPos)) + if (r_viewpoint.Pos.Z <= rover->bottom.plane->ZatPoint(r_viewpoint.Pos)) { SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG)); Colormap.FadeColor = frontsector->ColorMap->Fade; @@ -800,7 +801,7 @@ void GLFlat::ProcessSector(sector_t * frontsector) double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot); if (ff_bottom > lastfloorheight || (rover->flags&FF_FIX)) { - if (ViewPos.Z >= rover->bottom.plane->ZatPoint(ViewPos)) + if (r_viewpoint.Pos.Z >= rover->bottom.plane->ZatPoint(r_viewpoint.Pos)) { SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG)); Colormap.FadeColor = frontsector->ColorMap->Fade; @@ -821,7 +822,7 @@ void GLFlat::ProcessSector(sector_t * frontsector) double ff_top = rover->top.plane->ZatPoint(sector->centerspot); if (ff_top > lastfloorheight) { - if (ViewPos.Z >= rover->top.plane->ZatPoint(ViewPos)) + if (r_viewpoint.Pos.Z >= rover->top.plane->ZatPoint(r_viewpoint.Pos)) { SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG)); Colormap.FadeColor = frontsector->ColorMap->Fade; diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 967f6b3fc1..1f8f521edd 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -35,6 +35,8 @@ #include "portal.h" #include "p_maputl.h" #include "d_player.h" +#include "g_levellocals.h" +#include "actorinlines.h" #include "gl/system/gl_interface.h" #include "gl/system/gl_framebuffer.h" @@ -49,6 +51,7 @@ #include "gl/scene/gl_clipper.h" #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_portal.h" +#include "gl/scene/gl_scenedrawer.h" #include "gl/shaders/gl_shader.h" #include "gl/stereo3d/scoped_color_mask.h" #include "gl/textures/gl_material.h" @@ -70,8 +73,7 @@ EXTERN_CVAR(Bool, gl_portals) EXTERN_CVAR(Bool, gl_noquery) EXTERN_CVAR(Int, r_mirror_recursions) -extern bool r_showviewer; - +GLSceneDrawer *GLPortal::drawer; TArray GLPortal::portals; TArray GLPortal::planestack; int GLPortal::recursion; @@ -282,15 +284,15 @@ bool GLPortal::Start(bool usestencil, bool doquery) } // save viewpoint - savedViewPos = ViewPos; - savedViewActorPos = ViewActorPos; - savedshowviewer = r_showviewer; - savedAngle = ViewAngle; + savedViewPos = r_viewpoint.Pos; + savedViewActorPos = r_viewpoint.ActorPos; + savedshowviewer = r_viewpoint.showviewer; + savedAngles = r_viewpoint.Angles; savedviewactor=GLRenderer->mViewActor; savedviewarea=in_area; - savedviewpath[0] = ViewPath[0]; - savedviewpath[1] = ViewPath[1]; - savedvisibility = camera ? camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0); + savedviewpath[0] = r_viewpoint.Path[0]; + savedviewpath[1] = r_viewpoint.Path[1]; + savedvisibility = r_viewpoint.camera ? r_viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0); PrevPortal = GLRenderer->mCurrentPortal; @@ -306,14 +308,14 @@ bool GLPortal::Start(bool usestencil, bool doquery) inline void GLPortal::ClearClipper() { - DAngle angleOffset = deltaangle(savedAngle, ViewAngle); + DAngle angleOffset = deltaangle(savedAngles.Yaw, r_viewpoint.Angles.Yaw); - clipper.Clear(); + drawer->clipper.Clear(); static int call=0; // Set the clipper to the minimal visible area - clipper.SafeAddClipRange(0,0xffffffff); + drawer->clipper.SafeAddClipRange(0,0xffffffff); for (unsigned int i = 0; i < lines.Size(); i++) { DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - savedViewPos).Angle() + angleOffset; @@ -321,16 +323,16 @@ inline void GLPortal::ClearClipper() if (deltaangle(endAngle, startAngle) < 0) { - clipper.SafeRemoveClipRangeRealAngles(startAngle.BAMs(), endAngle.BAMs()); + drawer->clipper.SafeRemoveClipRangeRealAngles(startAngle.BAMs(), endAngle.BAMs()); } } // and finally clip it to the visible area - angle_t a1 = GLRenderer->FrustumAngle(); - if (a1 < ANGLE_180) clipper.SafeAddClipRangeRealAngles(ViewAngle.BAMs() + a1, ViewAngle.BAMs() - a1); + angle_t a1 = drawer->FrustumAngle(); + if (a1 < ANGLE_180) drawer->clipper.SafeAddClipRangeRealAngles(r_viewpoint.Angles.Yaw.BAMs() + a1, r_viewpoint.Angles.Yaw.BAMs() - a1); // lock the parts that have just been clipped out. - clipper.SetSilhouette(); + drawer->clipper.SetSilhouette(); } //----------------------------------------------------------------------------- @@ -352,16 +354,16 @@ void GLPortal::End(bool usestencil) if (needdepth) FDrawInfo::EndDrawInfo(); // Restore the old view - ViewPath[0] = savedviewpath[0]; - ViewPath[1] = savedviewpath[1]; - ViewPos = savedViewPos; - r_showviewer = savedshowviewer; - ViewActorPos = savedViewActorPos; - ViewAngle = savedAngle; + r_viewpoint.Path[0] = savedviewpath[0]; + r_viewpoint.Path[1] = savedviewpath[1]; + r_viewpoint.Pos = savedViewPos; + r_viewpoint.showviewer = savedshowviewer; + r_viewpoint.ActorPos = savedViewActorPos; + r_viewpoint.Angles = savedAngles; GLRenderer->mViewActor=savedviewactor; in_area=savedviewarea; - if (camera != nullptr) camera->renderflags = (camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - GLRenderer->SetupView(ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + if (r_viewpoint.camera != nullptr) r_viewpoint.camera->renderflags = (r_viewpoint.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; + drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); { ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // no graphics @@ -413,14 +415,14 @@ void GLPortal::End(bool usestencil) glDepthMask(true); } // Restore the old view - r_showviewer = savedshowviewer; - ViewActorPos = savedViewActorPos; - ViewPos = savedViewPos; - ViewAngle = savedAngle; + r_viewpoint.showviewer = savedshowviewer; + r_viewpoint.ActorPos = savedViewActorPos; + r_viewpoint.Pos = savedViewPos; + r_viewpoint.Angles = savedAngles; GLRenderer->mViewActor=savedviewactor; in_area=savedviewarea; - if (camera != nullptr) camera->renderflags |= savedvisibility; - GLRenderer->SetupView(ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + if (r_viewpoint.camera != nullptr) r_viewpoint.camera->renderflags |= savedvisibility; + drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); // This draws a valid z-buffer into the stencil's contents to ensure it // doesn't get overwritten by the level's geometry. @@ -617,7 +619,7 @@ static int skyboxrecursion=0; void GLSkyboxPortal::DrawContents() { int old_pm = PlaneMirrorMode; - int saved_extralight = extralight; + int saved_extralight = r_viewpoint.extralight; if (skyboxrecursion >= 3) { @@ -628,41 +630,41 @@ void GLSkyboxPortal::DrawContents() skyboxrecursion++; AActor *origin = portal->mSkybox; portal->mFlags |= PORTSF_INSKYBOX; - extralight = 0; + r_viewpoint.extralight = 0; PlaneMirrorMode = 0; bool oldclamp = gl_RenderState.SetDepthClamp(false); - ViewPos = origin->InterpolatedPosition(r_TicFracF); - ViewActorPos = origin->Pos(); - ViewAngle += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * r_TicFracF); + r_viewpoint.Pos = origin->InterpolatedPosition(r_viewpoint.TicFrac); + r_viewpoint.ActorPos = origin->Pos(); + r_viewpoint.Angles.Yaw += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * r_viewpoint.TicFrac); // Don't let the viewpoint be too close to a floor or ceiling double floorh = origin->Sector->floorplane.ZatPoint(origin->Pos()); double ceilh = origin->Sector->ceilingplane.ZatPoint(origin->Pos()); - if (ViewPos.Z < floorh + 4) ViewPos.Z = floorh + 4; - if (ViewPos.Z > ceilh - 4) ViewPos.Z = ceilh - 4; + if (r_viewpoint.Pos.Z < floorh + 4) r_viewpoint.Pos.Z = floorh + 4; + if (r_viewpoint.Pos.Z > ceilh - 4) r_viewpoint.Pos.Z = ceilh - 4; GLRenderer->mViewActor = origin; inskybox = true; - GLRenderer->SetupView(ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); - GLRenderer->SetViewArea(); + drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + drawer->SetViewArea(); ClearClipper(); - int mapsection = R_PointInSubsector(ViewPos)->mapsection; + int mapsection = R_PointInSubsector(r_viewpoint.Pos)->mapsection; SaveMapSection(); currentmapsection[mapsection >> 3] |= 1 << (mapsection & 7); - GLRenderer->DrawScene(DM_SKYPORTAL); + drawer->DrawScene(DM_SKYPORTAL); portal->mFlags &= ~PORTSF_INSKYBOX; inskybox = false; gl_RenderState.SetDepthClamp(oldclamp); skyboxrecursion--; PlaneMirrorMode = old_pm; - extralight = saved_extralight; + r_viewpoint.extralight = saved_extralight; RestoreMapSection(); } @@ -690,7 +692,7 @@ GLSectorStackPortal::~GLSectorStackPortal() // //----------------------------------------------------------------------------- -static BYTE SetCoverage(void *node) +static uint8_t SetCoverage(void *node) { if (numnodes == 0) { @@ -699,13 +701,13 @@ static BYTE SetCoverage(void *node) if (!((size_t)node & 1)) // Keep going until found a subsector { node_t *bsp = (node_t *)node; - BYTE coverage = SetCoverage(bsp->children[0]) | SetCoverage(bsp->children[1]); + uint8_t coverage = SetCoverage(bsp->children[0]) | SetCoverage(bsp->children[1]); gl_drawinfo->no_renderflags[bsp-nodes] = coverage; return coverage; } else { - subsector_t *sub = (subsector_t *)((BYTE *)node - 1); + subsector_t *sub = (subsector_t *)((uint8_t *)node - 1); return gl_drawinfo->ss_renderflags[sub-subsectors] & SSRF_SEEN; } } @@ -735,28 +737,28 @@ void GLSectorStackPortal::DrawContents() { FPortal *portal = origin; - ViewPos += origin->mDisplacement; - ViewActorPos += origin->mDisplacement; + r_viewpoint.Pos += origin->mDisplacement; + r_viewpoint.ActorPos += origin->mDisplacement; GLRenderer->mViewActor = NULL; // avoid recursions! if (origin->plane != -1) instack[origin->plane]++; - GLRenderer->SetupView(ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); SaveMapSection(); SetupCoverage(); ClearClipper(); // If the viewpoint is not within the portal, we need to invalidate the entire clip area. // The portal will re-validate the necessary parts when its subsectors get traversed. - subsector_t *sub = R_PointInSubsector(ViewPos); + subsector_t *sub = R_PointInSubsector(r_viewpoint.Pos); if (!(gl_drawinfo->ss_renderflags[sub - ::subsectors] & SSRF_SEEN)) { - clipper.SafeAddClipRange(0, ANGLE_MAX); - clipper.SetBlocked(true); + drawer->clipper.SafeAddClipRange(0, ANGLE_MAX); + drawer->clipper.SetBlocked(true); } - GLRenderer->DrawScene(DM_PORTAL); + drawer->DrawScene(DM_PORTAL); RestoreMapSection(); if (origin->plane != -1) instack[origin->plane]--; @@ -791,19 +793,19 @@ void GLPlaneMirrorPortal::DrawContents() int old_pm = PlaneMirrorMode; // the player is always visible in a mirror. - r_showviewer = true; + r_viewpoint.showviewer = true; - double planez = origin->ZatPoint(ViewPos); - ViewPos.Z = 2 * planez - ViewPos.Z; + double planez = origin->ZatPoint(r_viewpoint.Pos); + r_viewpoint.Pos.Z = 2 * planez - r_viewpoint.Pos.Z; GLRenderer->mViewActor = NULL; PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; PlaneMirrorFlag++; - GLRenderer->SetupView(ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); ClearClipper(); gl_RenderState.SetClipHeight(planez, PlaneMirrorMode < 0 ? -1.f : 1.f); - GLRenderer->DrawScene(DM_PORTAL); + drawer->DrawScene(DM_PORTAL); gl_RenderState.SetClipHeight(0.f, 0.f); PlaneMirrorFlag--; PlaneMirrorMode = old_pm; @@ -845,7 +847,7 @@ void GLLinePortal::PushState() void GLLinePortal::PopState() { FStateVec4 &v = gl_RenderState.GetClipLine(); - float e; + float e = 0; planestack.Pop(e); planestack.Pop(v.vec[3]); planestack.Pop(v.vec[2]); @@ -861,7 +863,7 @@ int GLLinePortal::ClipSeg(seg_t *seg) { return PClip_Inside; // should be handled properly. } - return P_ClipLineToPortal(linedef, line(), ViewPos) ? PClip_InFront : PClip_Inside; + return P_ClipLineToPortal(linedef, line(), r_viewpoint.Pos) ? PClip_InFront : PClip_Inside; } int GLLinePortal::ClipSubsector(subsector_t *sub) @@ -908,32 +910,32 @@ void GLMirrorPortal::DrawContents() } GLRenderer->mClipPortal = this; - DAngle StartAngle = ViewAngle; - DVector3 StartPos = ViewPos; + DAngle StartAngle = r_viewpoint.Angles.Yaw; + DVector3 StartPos = r_viewpoint.Pos; vertex_t *v1 = linedef->v1; vertex_t *v2 = linedef->v2; // the player is always visible in a mirror. - r_showviewer = true; + r_viewpoint.showviewer = true; // Reflect the current view behind the mirror. if (linedef->Delta().X == 0) { // vertical mirror - ViewPos.X = 2 * v1->fX() - StartPos.X; + r_viewpoint.Pos.X = 2 * v1->fX() - StartPos.X; // Compensation for reendering inaccuracies - if (StartPos.X < v1->fX()) ViewPos.X -= 0.1; - else ViewPos.X += 0.1; + if (StartPos.X < v1->fX()) r_viewpoint.Pos.X -= 0.1; + else r_viewpoint.Pos.X += 0.1; } else if (linedef->Delta().Y == 0) { // horizontal mirror - ViewPos.Y = 2*v1->fY() - StartPos.Y; + r_viewpoint.Pos.Y = 2*v1->fY() - StartPos.Y; // Compensation for reendering inaccuracies - if (StartPos.YfY()) ViewPos.Y -= 0.1; - else ViewPos.Y += 0.1; + if (StartPos.YfY()) r_viewpoint.Pos.Y -= 0.1; + else r_viewpoint.Pos.Y += 0.1; } else { @@ -950,35 +952,35 @@ void GLMirrorPortal::DrawContents() // the above two cases catch len == 0 double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); - ViewPos.X = (x1 + r * dx)*2 - x; - ViewPos.Y = (y1 + r * dy)*2 - y; + r_viewpoint.Pos.X = (x1 + r * dx)*2 - x; + r_viewpoint.Pos.Y = (y1 + r * dy)*2 - y; // Compensation for reendering inaccuracies FVector2 v(-dx, dy); v.MakeUnit(); - ViewPos.X+= v[1] * renderdepth / 2; - ViewPos.Y+= v[0] * renderdepth / 2; + r_viewpoint.Pos.X+= v[1] * renderdepth / 2; + r_viewpoint.Pos.Y+= v[0] * renderdepth / 2; } - ViewAngle = linedef->Delta().Angle() * 2. - StartAngle; + r_viewpoint.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle; GLRenderer->mViewActor = NULL; MirrorFlag++; - GLRenderer->SetupView(ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); - clipper.Clear(); + drawer->clipper.Clear(); - angle_t af = GLRenderer->FrustumAngle(); - if (afFrustumAngle(); + if (afclipper.SafeAddClipRangeRealAngles(r_viewpoint.Angles.Yaw.BAMs()+af, r_viewpoint.Angles.Yaw.BAMs()-af); angle_t a2 = linedef->v1->GetClipAngle(); angle_t a1 = linedef->v2->GetClipAngle(); - clipper.SafeAddClipRange(a1,a2); + drawer->clipper.SafeAddClipRange(a1,a2); gl_RenderState.SetClipLine(linedef); gl_RenderState.EnableClipLine(true); - GLRenderer->DrawScene(DM_PORTAL); + drawer->DrawScene(DM_PORTAL); gl_RenderState.EnableClipLine(false); MirrorFlag--; @@ -1011,23 +1013,23 @@ void GLLineToLinePortal::DrawContents() GLRenderer->mClipPortal = this; line_t *origin = glport->lines[0]->mOrigin; - P_TranslatePortalXY(origin, ViewPos.X, ViewPos.Y); - P_TranslatePortalXY(origin, ViewActorPos.X, ViewActorPos.Y); - P_TranslatePortalAngle(origin, ViewAngle); - P_TranslatePortalZ(origin, ViewPos.Z); - P_TranslatePortalXY(origin, ViewPath[0].X, ViewPath[0].Y); - P_TranslatePortalXY(origin, ViewPath[1].X, ViewPath[1].Y); - if (!r_showviewer && camera != nullptr && P_PointOnLineSidePrecise(ViewPath[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(ViewPath[1], glport->lines[0]->mDestination)) + P_TranslatePortalXY(origin, r_viewpoint.Pos.X, r_viewpoint.Pos.Y); + P_TranslatePortalXY(origin, r_viewpoint.ActorPos.X, r_viewpoint.ActorPos.Y); + P_TranslatePortalAngle(origin, r_viewpoint.Angles.Yaw); + P_TranslatePortalZ(origin, r_viewpoint.Pos.Z); + P_TranslatePortalXY(origin, r_viewpoint.Path[0].X, r_viewpoint.Path[0].Y); + P_TranslatePortalXY(origin, r_viewpoint.Path[1].X, r_viewpoint.Path[1].Y); + if (!r_viewpoint.showviewer && r_viewpoint.camera != nullptr && P_PointOnLineSidePrecise(r_viewpoint.Path[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(r_viewpoint.Path[1], glport->lines[0]->mDestination)) { - double distp = (ViewPath[0] - ViewPath[1]).Length(); + double distp = (r_viewpoint.Path[0] - r_viewpoint.Path[1]).Length(); if (distp > EQUAL_EPSILON) { - double dist1 = (ViewPos - ViewPath[0]).Length(); - double dist2 = (ViewPos - ViewPath[1]).Length(); + double dist1 = (r_viewpoint.Pos - r_viewpoint.Path[0]).Length(); + double dist2 = (r_viewpoint.Pos - r_viewpoint.Path[1]).Length(); if (dist1 + dist2 < distp + 1) { - camera->renderflags |= RF_MAYBEINVISIBLE; + r_viewpoint.camera->renderflags |= RF_MAYBEINVISIBLE; } } } @@ -1047,12 +1049,12 @@ void GLLineToLinePortal::DrawContents() } GLRenderer->mViewActor = nullptr; - GLRenderer->SetupView(ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); ClearClipper(); gl_RenderState.SetClipLine(glport->lines[0]->mDestination); gl_RenderState.EnableClipLine(true); - GLRenderer->DrawScene(DM_PORTAL); + drawer->DrawScene(DM_PORTAL); gl_RenderState.EnableClipLine(false); RestoreMapSection(); } @@ -1091,9 +1093,9 @@ GLHorizonPortal::GLHorizonPortal(GLHorizonInfo * pt, bool local) // create the vertex data for this horizon portal. GLSectorPlane * sp = &origin->plane; - const float vx = ViewPos.X; - const float vy = ViewPos.Y; - const float vz = ViewPos.Z; + const float vx = r_viewpoint.Pos.X; + const float vy = r_viewpoint.Pos.Y; + const float vz = r_viewpoint.Pos.Z; const float z = sp->Texheight; const float tz = (z - vz); @@ -1164,7 +1166,7 @@ void GLHorizonPortal::DrawContents() PortalAll.Unclock(); return; } - gl_RenderState.SetCameraPos(ViewPos.X, ViewPos.Y, ViewPos.Z); + gl_RenderState.SetCameraPos(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z); if (gltexture && gltexture->tex->isFullbright()) @@ -1241,7 +1243,7 @@ void GLEEHorizonPortal::DrawContents() horz.colormap = sector->ColorMap; if (portal->mType == PORTS_PLANE) { - horz.plane.Texheight = ViewPos.Z + fabs(horz.plane.Texheight); + horz.plane.Texheight = r_viewpoint.Pos.Z + fabs(horz.plane.Texheight); } GLHorizonPortal ceil(&horz, true); ceil.DrawContents(); @@ -1254,7 +1256,7 @@ void GLEEHorizonPortal::DrawContents() horz.colormap = sector->ColorMap; if (portal->mType == PORTS_PLANE) { - horz.plane.Texheight = ViewPos.Z - fabs(horz.plane.Texheight); + horz.plane.Texheight = r_viewpoint.Pos.Z - fabs(horz.plane.Texheight); } GLHorizonPortal floor(&horz, true); floor.DrawContents(); diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index 3adaa4aa48..128db2dc89 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -77,6 +77,7 @@ extern UniqueList UniqueHorizons; extern UniqueList UniquePlaneMirrors; extern UniqueList UniqueLineToLines; struct GLEEHorizonPortal; +class GLSceneDrawer; class GLPortal { @@ -90,6 +91,7 @@ protected: static int renderdepth; public: + static GLSceneDrawer *drawer; static int PlaneMirrorMode; static int inupperstack; static int instack[2]; @@ -101,14 +103,14 @@ private: DVector3 savedviewpath[2]; DVector3 savedViewPos; DVector3 savedViewActorPos; - DAngle savedAngle; + DRotator savedAngles; bool savedshowviewer; AActor * savedviewactor; area_t savedviewarea; ActorRenderFlags savedvisibility; GLPortal *PrevPortal; GLPortal *PrevClipPortal; - TArray savedmapsection; + TArray savedmapsection; TArray mPrimIndices; protected: diff --git a/src/gl/scene/gl_renderhacks.cpp b/src/gl/scene/gl_renderhacks.cpp index 4dd7b7d819..f942584af4 100644 --- a/src/gl/scene/gl_renderhacks.cpp +++ b/src/gl/scene/gl_renderhacks.cpp @@ -260,13 +260,13 @@ bool FDrawInfo::DoOneSectorUpper(subsector_t * subsec, float Planez) { // Is there a one-sided wall in this sector? // Do this first to avoid unnecessary recursion - for (DWORD i = 0; i < subsec->numlines; i++) + for (uint32_t i = 0; i < subsec->numlines; i++) { if (subsec->firstline[i].backsector == NULL) return false; if (subsec->firstline[i].PartnerSeg == NULL) return false; } - for (DWORD i = 0; i < subsec->numlines; i++) + for (uint32_t i = 0; i < subsec->numlines; i++) { seg_t * seg = subsec->firstline + i; subsector_t * backsub = seg->PartnerSeg->Subsector; @@ -318,13 +318,13 @@ bool FDrawInfo::DoOneSectorLower(subsector_t * subsec, float Planez) { // Is there a one-sided wall in this subsector? // Do this first to avoid unnecessary recursion - for (DWORD i = 0; i < subsec->numlines; i++) + for (uint32_t i = 0; i < subsec->numlines; i++) { if (subsec->firstline[i].backsector == NULL) return false; if (subsec->firstline[i].PartnerSeg == NULL) return false; } - for (DWORD i = 0; i < subsec->numlines; i++) + for (uint32_t i = 0; i < subsec->numlines; i++) { seg_t * seg = subsec->firstline + i; subsector_t * backsub = seg->PartnerSeg->Subsector; @@ -377,13 +377,13 @@ bool FDrawInfo::DoFakeBridge(subsector_t * subsec, float Planez) { // Is there a one-sided wall in this sector? // Do this first to avoid unnecessary recursion - for (DWORD i = 0; i < subsec->numlines; i++) + for (uint32_t i = 0; i < subsec->numlines; i++) { if (subsec->firstline[i].backsector == NULL) return false; if (subsec->firstline[i].PartnerSeg == NULL) return false; } - for (DWORD i = 0; i < subsec->numlines; i++) + for (uint32_t i = 0; i < subsec->numlines; i++) { seg_t * seg = subsec->firstline + i; subsector_t * backsub = seg->PartnerSeg->Subsector; @@ -430,13 +430,13 @@ bool FDrawInfo::DoFakeCeilingBridge(subsector_t * subsec, float Planez) { // Is there a one-sided wall in this sector? // Do this first to avoid unnecessary recursion - for (DWORD i = 0; i < subsec->numlines; i++) + for (uint32_t i = 0; i < subsec->numlines; i++) { if (subsec->firstline[i].backsector == NULL) return false; if (subsec->firstline[i].PartnerSeg == NULL) return false; } - for (DWORD i = 0; i < subsec->numlines; i++) + for (uint32_t i = 0; i < subsec->numlines; i++) { seg_t * seg = subsec->firstline + i; subsector_t * backsub = seg->PartnerSeg->Subsector; @@ -493,7 +493,7 @@ void FDrawInfo::HandleMissingTextures() HandledSubsectors.Clear(); validcount++; - if (MissingUpperTextures[i].Planez > ViewPos.Z) + if (MissingUpperTextures[i].Planez > r_viewpoint.Pos.Z) { // close the hole only if all neighboring sectors are an exact height match // Otherwise just fill in the missing textures. @@ -565,7 +565,7 @@ void FDrawInfo::HandleMissingTextures() HandledSubsectors.Clear(); validcount++; - if (MissingLowerTextures[i].Planez < ViewPos.Z) + if (MissingLowerTextures[i].Planez < r_viewpoint.Pos.Z) { // close the hole only if all neighboring sectors are an exact height match // Otherwise just fill in the missing textures. @@ -655,7 +655,7 @@ void FDrawInfo::DrawUnhandledMissingTextures() // already done! if (seg->linedef->validcount == validcount) continue; // already done seg->linedef->validcount = validcount; - if (seg->frontsector->GetPlaneTexZ(sector_t::ceiling) < ViewPos.Z) continue; // out of sight + if (seg->frontsector->GetPlaneTexZ(sector_t::ceiling) < r_viewpoint.Pos.Z) continue; // out of sight // FIXME: The check for degenerate subsectors should be more precise if (seg->PartnerSeg && (seg->PartnerSeg->Subsector->flags & SSECF_DEGENERATE)) continue; @@ -677,7 +677,7 @@ void FDrawInfo::DrawUnhandledMissingTextures() if (seg->linedef->validcount == validcount) continue; // already done seg->linedef->validcount = validcount; if (!(sectorrenderflags[seg->backsector->sectornum] & SSRF_RENDERFLOOR)) continue; - if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > ViewPos.Z) continue; // out of sight + if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > r_viewpoint.Pos.Z) continue; // out of sight if (seg->backsector->transdoor) continue; if (seg->backsector->GetTexture(sector_t::floor) == skyflatnum) continue; if (seg->backsector->ValidatePortal(sector_t::floor) != NULL) continue; @@ -732,7 +732,7 @@ bool FDrawInfo::CheckAnchorFloor(subsector_t * sub) if (sub->hacked==3) return true; if (sub->flags & SSECF_DEGENERATE) return false; - for(DWORD j=0;jnumlines;j++) + for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (!seg->PartnerSeg) return true; @@ -781,20 +781,20 @@ bool FDrawInfo::CollectSubsectorsFloor(subsector_t * sub, sector_t * anchor) if (!(sub->flags & SSECF_DEGENERATE)) { // Is not being rendered so don't bother. - if (!(ss_renderflags[DWORD(sub - subsectors)] & SSRF_PROCESSED)) return true; + if (!(ss_renderflags[uint32_t(sub - subsectors)] & SSRF_PROCESSED)) return true; if (sub->render_sector->GetTexture(sector_t::floor) != anchor->GetTexture(sector_t::floor) || sub->render_sector->GetPlaneTexZ(sector_t::floor) != anchor->GetPlaneTexZ(sector_t::floor) || sub->render_sector->GetFloorLight() != anchor->GetFloorLight()) { - if (sub == viewsubsector && ViewPos.Z < anchor->GetPlaneTexZ(sector_t::floor)) inview = true; + if (sub == viewsubsector && r_viewpoint.Pos.Z < anchor->GetPlaneTexZ(sector_t::floor)) inview = true; HandledSubsectors.Push(sub); } } // We can assume that all segs in this subsector are connected to a subsector that has // to be checked as well - for(DWORD j=0;jnumlines;j++) + for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (seg->PartnerSeg) @@ -838,7 +838,7 @@ bool FDrawInfo::CheckAnchorCeiling(subsector_t * sub) if (sub->hacked==3) return true; if (sub->flags & SSECF_DEGENERATE) return false; - for(DWORD j=0;jnumlines;j++) + for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (!seg->PartnerSeg) return true; @@ -883,7 +883,7 @@ bool FDrawInfo::CollectSubsectorsCeiling(subsector_t * sub, sector_t * anchor) if (!(sub->flags & SSECF_DEGENERATE)) { // Is not being rendererd so don't bother. - if (!(ss_renderflags[DWORD(sub-subsectors)]&SSRF_PROCESSED)) return true; + if (!(ss_renderflags[uint32_t(sub-subsectors)]&SSRF_PROCESSED)) return true; if (sub->render_sector->GetTexture(sector_t::ceiling) != anchor->GetTexture(sector_t::ceiling) || sub->render_sector->GetPlaneTexZ(sector_t::ceiling) != anchor->GetPlaneTexZ(sector_t::ceiling) || @@ -895,7 +895,7 @@ bool FDrawInfo::CollectSubsectorsCeiling(subsector_t * sub, sector_t * anchor) // We can assume that all segs in this subsector are connected to a subsector that has // to be checked as well - for(DWORD j=0;jnumlines;j++) + for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (seg->PartnerSeg) @@ -937,7 +937,7 @@ void FDrawInfo::HandleHackedSubsectors() totalssms.Reset(); totalssms.Clock(); - viewsubsector = R_PointInSubsector(ViewPos); + viewsubsector = R_PointInSubsector(r_viewpoint.Pos); // Each subsector may only be processed once in this loop! validcount++; @@ -1037,7 +1037,7 @@ void FDrawInfo::CollectSectorStacksCeiling(subsector_t * sub, sector_t * anchor) if (sub->render_sector->GetGLPortal(sector_t::ceiling) != nullptr) return; // Don't bother processing unrendered subsectors - if (sub->numlines>2 && !(ss_renderflags[DWORD(sub-subsectors)]&SSRF_PROCESSED)) return; + if (sub->numlines>2 && !(ss_renderflags[uint32_t(sub-subsectors)]&SSRF_PROCESSED)) return; // Must be the exact same visplane sector_t * me = gl_FakeFlat(sub->render_sector, &fakesec, false); @@ -1053,7 +1053,7 @@ void FDrawInfo::CollectSectorStacksCeiling(subsector_t * sub, sector_t * anchor) HandledSubsectors.Push (sub); - for(DWORD j=0;jnumlines;j++) + for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (seg->PartnerSeg) @@ -1080,7 +1080,7 @@ void FDrawInfo::CollectSectorStacksFloor(subsector_t * sub, sector_t * anchor) if (sub->render_sector->GetGLPortal(sector_t::floor) != nullptr) return; // Don't bother processing unrendered subsectors - if (sub->numlines>2 && !(ss_renderflags[DWORD(sub-subsectors)]&SSRF_PROCESSED)) return; + if (sub->numlines>2 && !(ss_renderflags[uint32_t(sub-subsectors)]&SSRF_PROCESSED)) return; // Must be the exact same visplane sector_t * me = gl_FakeFlat(sub->render_sector, &fakesec, false); @@ -1096,7 +1096,7 @@ void FDrawInfo::CollectSectorStacksFloor(subsector_t * sub, sector_t * anchor) HandledSubsectors.Push (sub); - for(DWORD j=0;jnumlines;j++) + for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (seg->PartnerSeg) @@ -1130,7 +1130,7 @@ void FDrawInfo::ProcessSectorStacks() if (ss_renderflags[sub-subsectors] & SSRF_PROCESSED) { HandledSubsectors.Clear(); - for(DWORD j=0;jnumlines;j++) + for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (seg->PartnerSeg) @@ -1143,7 +1143,7 @@ void FDrawInfo::ProcessSectorStacks() for(unsigned int j=0;jportalcoverage[sector_t::ceiling].subsectors == NULL) { @@ -1174,7 +1174,7 @@ void FDrawInfo::ProcessSectorStacks() if (ss_renderflags[sub-subsectors] & SSRF_PROCESSED) { HandledSubsectors.Clear(); - for(DWORD j=0;jnumlines;j++) + for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (seg->PartnerSeg) @@ -1188,7 +1188,7 @@ void FDrawInfo::ProcessSectorStacks() for(unsigned int j=0;jportalcoverage[sector_t::floor].subsectors == NULL) { diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 1e4b7ac585..e48ab744e9 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -60,12 +60,11 @@ #include "gl/scene/gl_clipper.h" #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_portal.h" +#include "gl/scene/gl_scenedrawer.h" #include "gl/shaders/gl_shader.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/stereo3d/scoped_view_shifter.h" -#include "gl/textures/gl_translate.h" #include "gl/textures/gl_material.h" -#include "gl/textures/gl_skyboxtexture.h" #include "gl/utility/gl_clock.h" #include "gl/utility/gl_convert.h" #include "gl/utility/gl_templates.h" @@ -88,32 +87,29 @@ EXTERN_CVAR (Float, underwater_fade_scalar) extern int viewpitch; extern bool NoInterpolateView; -extern bool r_showviewer; int gl_fixedcolormap; area_t in_area; -TArray currentmapsection; +TArray currentmapsection; int camtexcount; -void gl_ParseDefs(); - //----------------------------------------------------------------------------- // // R_FrustumAngle // //----------------------------------------------------------------------------- -angle_t FGLRenderer::FrustumAngle() +angle_t GLSceneDrawer::FrustumAngle() { - float tilt= fabs(mAngles.Pitch.Degrees); + float tilt = fabs(GLRenderer->mAngles.Pitch.Degrees); // If the pitch is larger than this you can look all around at a FOV of 90° - if (tilt>46.0f) return 0xffffffff; + if (tilt > 46.0f) return 0xffffffff; // ok, this is a gross hack that barely works... // but at least it doesn't overestimate too much... - double floatangle=2.0+(45.0+((tilt/1.9)))*mCurrentFoV*48.0/AspectMultiplier(WidescreenRatio)/90.0; + double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*GLRenderer->mCurrentFoV*48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0; angle_t a1 = DAngle(floatangle).BAMs(); - if (a1>=ANGLE_180) return 0xffffffff; + if (a1 >= ANGLE_180) return 0xffffffff; return a1; } @@ -122,17 +118,17 @@ angle_t FGLRenderer::FrustumAngle() // Sets the area the camera is in // //----------------------------------------------------------------------------- -void FGLRenderer::SetViewArea() +void GLSceneDrawer::SetViewArea() { // The render_sector is better suited to represent the current position in GL - viewsector = R_PointInSubsector(ViewPos)->render_sector; + r_viewpoint.sector = R_PointInSubsector(r_viewpoint.Pos)->render_sector; // Get the heightsec state from the render sector, not the current one! - if (viewsector->heightsec && !(viewsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) + if (r_viewpoint.sector->heightsec && !(r_viewpoint.sector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) { - in_area = ViewPos.Z <= viewsector->heightsec->floorplane.ZatPoint(ViewPos) ? area_below : - (ViewPos.Z > viewsector->heightsec->ceilingplane.ZatPoint(ViewPos) && - !(viewsector->heightsec->MoreFlags&SECF_FAKEFLOORONLY)) ? area_above : area_normal; + in_area = r_viewpoint.Pos.Z <= r_viewpoint.sector->heightsec->floorplane.ZatPoint(r_viewpoint.Pos) ? area_below : + (r_viewpoint.Pos.Z > r_viewpoint.sector->heightsec->ceilingplane.ZatPoint(r_viewpoint.Pos) && + !(r_viewpoint.sector->heightsec->MoreFlags&SECF_FAKEFLOORONLY)) ? area_above : area_normal; } else { @@ -146,9 +142,9 @@ void FGLRenderer::SetViewArea() // //----------------------------------------------------------------------------- -void FGLRenderer::Reset3DViewport() +void GLSceneDrawer::Reset3DViewport() { - glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); + glViewport(GLRenderer->mScreenViewport.left, GLRenderer->mScreenViewport.top, GLRenderer->mScreenViewport.width, GLRenderer->mScreenViewport.height); } //----------------------------------------------------------------------------- @@ -157,12 +153,12 @@ void FGLRenderer::Reset3DViewport() // //----------------------------------------------------------------------------- -void FGLRenderer::Set3DViewport(bool mainview) +void GLSceneDrawer::Set3DViewport(bool mainview) { - if (mainview && mBuffers->Setup(mScreenViewport.width, mScreenViewport.height, mSceneViewport.width, mSceneViewport.height)) + if (mainview && GLRenderer->mBuffers->Setup(GLRenderer->mScreenViewport.width, GLRenderer->mScreenViewport.height, GLRenderer->mSceneViewport.width, GLRenderer->mSceneViewport.height)) { bool useSSAO = (gl_ssao != 0); - mBuffers->BindSceneFB(useSSAO); + GLRenderer->mBuffers->BindSceneFB(useSSAO); gl_RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS); gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); gl_RenderState.Apply(); @@ -172,10 +168,10 @@ void FGLRenderer::Set3DViewport(bool mainview) // This is faster on newer hardware because it allows the GPU to skip // reading from slower memory where the full buffers are stored. glDisable(GL_SCISSOR_TEST); - glClearColor(mSceneClearColor[0], mSceneClearColor[1], mSceneClearColor[2], 1.0f); + glClearColor(GLRenderer->mSceneClearColor[0], GLRenderer->mSceneClearColor[1], GLRenderer->mSceneClearColor[2], 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - const auto &bounds = mSceneViewport; + const auto &bounds = GLRenderer->mSceneViewport; glViewport(bounds.left, bounds.top, bounds.width, bounds.height); glScissor(bounds.left, bounds.top, bounds.width, bounds.height); @@ -194,14 +190,14 @@ void FGLRenderer::Set3DViewport(bool mainview) // //----------------------------------------------------------------------------- -void FGLRenderer::SetViewAngle(DAngle viewangle) +void GLSceneDrawer::SetViewAngle(DAngle viewangle) { - mAngles.Yaw = float(270.0-viewangle.Degrees); - DVector2 v = ViewAngle.ToVector(); - mViewVector.X = v.X; - mViewVector.Y = v.Y; + GLRenderer->mAngles.Yaw = float(270.0-viewangle.Degrees); + DVector2 v = r_viewpoint.Angles.Yaw.ToVector(); + GLRenderer->mViewVector.X = v.X; + GLRenderer->mViewVector.Y = v.Y; - R_SetViewAngle(); + R_SetViewAngle(r_viewpoint, r_viewwindow); } @@ -212,15 +208,7 @@ void FGLRenderer::SetViewAngle(DAngle viewangle) // //----------------------------------------------------------------------------- -void FGLRenderer::SetProjection(float fov, float ratio, float fovratio) -{ - - float fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovratio)); - gl_RenderState.mProjectionMatrix.perspective(fovy, ratio, GetZNear(), GetZFar()); -} - -// raw matrix input from stereo 3d modes -void FGLRenderer::SetProjection(VSMatrix matrix) +void GLSceneDrawer::SetProjection(VSMatrix matrix) { gl_RenderState.mProjectionMatrix.loadIdentity(); gl_RenderState.mProjectionMatrix.multMatrix(matrix); @@ -232,7 +220,7 @@ void FGLRenderer::SetProjection(VSMatrix matrix) // //----------------------------------------------------------------------------- -void FGLRenderer::SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror) +void GLSceneDrawer::SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror) { float mult = mirror? -1:1; float planemult = planemirror? -glset.pixelstretch : glset.pixelstretch; @@ -252,7 +240,7 @@ void FGLRenderer::SetViewMatrix(float vx, float vy, float vz, bool mirror, bool // Setup the view rotation matrix for the given viewpoint // //----------------------------------------------------------------------------- -void FGLRenderer::SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror) +void GLSceneDrawer::SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror) { SetViewAngle(va); SetViewMatrix(vx, vy, vz, mirror, planemirror); @@ -267,22 +255,24 @@ void FGLRenderer::SetupView(float vx, float vy, float vz, DAngle va, bool mirror // //----------------------------------------------------------------------------- -void FGLRenderer::CreateScene() +void GLSceneDrawer::CreateScene() { + angle_t a1 = FrustumAngle(); + InitClipper(r_viewpoint.Angles.Yaw.BAMs() + a1, r_viewpoint.Angles.Yaw.BAMs() - a1); + // reset the portal manager GLPortal::StartFrame(); - PO_LinkToSubsectors(); ProcessAll.Clock(); // clip the scene and fill the drawlists for(unsigned i=0;iglportal = NULL; - gl_spriteindex=0; + GLRenderer->gl_spriteindex=0; Bsp.Clock(); GLRenderer->mVBO->Map(); - R_SetView(); + SetView(); validcount++; // used for processing sidedefs only once by the renderer. - gl_RenderBSPNode (nodes + numnodes - 1); + RenderBSPNode (nodes + numnodes - 1); if (GLRenderer->mCurrentPortal != NULL) GLRenderer->mCurrentPortal->RenderAttached(); Bsp.Unclock(); @@ -305,14 +295,14 @@ void FGLRenderer::CreateScene() // //----------------------------------------------------------------------------- -void FGLRenderer::RenderScene(int recursion) +void GLSceneDrawer::RenderScene(int recursion) { RenderAll.Clock(); glDepthMask(true); if (!gl_no_skyclear) GLPortal::RenderFirstSkyPortal(recursion); - gl_RenderState.SetCameraPos(ViewPos.X, ViewPos.Y, ViewPos.Z); + gl_RenderState.SetCameraPos(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z); gl_RenderState.EnableFog(true); gl_RenderState.BlendFunc(GL_ONE,GL_ZERO); @@ -328,7 +318,7 @@ void FGLRenderer::RenderScene(int recursion) // if we don't have a persistently mapped buffer, we have to process all the dynamic lights up front, // so that we don't have to do repeated map/unmap calls on the buffer. - bool haslights = mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights; + bool haslights = GLRenderer->mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights; if (gl.lightmethod == LM_DEFERRED && haslights) { GLRenderer->mLights->Begin(); @@ -357,7 +347,7 @@ void FGLRenderer::RenderScene(int recursion) { pass = GLPASS_ALL; } - else + else // GL 2.x legacy mode { // process everything that needs to handle textured dynamic lights. if (haslights) RenderMultipassStuff(); @@ -444,12 +434,12 @@ void FGLRenderer::RenderScene(int recursion) // //----------------------------------------------------------------------------- -void FGLRenderer::RenderTranslucent() +void GLSceneDrawer::RenderTranslucent() { RenderAll.Clock(); glDepthMask(false); - gl_RenderState.SetCameraPos(ViewPos.X, ViewPos.Y, ViewPos.Z); + gl_RenderState.SetCameraPos(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z); // final pass: translucent stuff gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold); @@ -476,7 +466,7 @@ void FGLRenderer::RenderTranslucent() // //----------------------------------------------------------------------------- -void FGLRenderer::DrawScene(int drawmode) +void GLSceneDrawer::DrawScene(int drawmode) { static int recursion=0; static int ssao_portals_available = 0; @@ -497,11 +487,11 @@ void FGLRenderer::DrawScene(int drawmode) ssao_portals_available--; } - if (camera != nullptr) + if (r_viewpoint.camera != nullptr) { - ActorRenderFlags savedflags = camera->renderflags; + ActorRenderFlags savedflags = r_viewpoint.camera->renderflags; CreateScene(); - camera->renderflags = savedflags; + r_viewpoint.camera->renderflags = savedflags; } else { @@ -514,8 +504,8 @@ void FGLRenderer::DrawScene(int drawmode) if (applySSAO && gl_RenderState.GetPassType() == GBUFFER_PASS) { gl_RenderState.EnableDrawBuffers(1); - AmbientOccludeScene(); - mBuffers->BindSceneFB(true); + GLRenderer->AmbientOccludeScene(); + GLRenderer->mBuffers->BindSceneFB(true); gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); gl_RenderState.Apply(); gl_RenderState.ApplyMatrices(); @@ -544,7 +534,7 @@ void gl_FillScreen() // Draws a blend over the entire view // //========================================================================== -void FGLRenderer::DrawBlend(sector_t * viewsector) +void GLSceneDrawer::DrawBlend(sector_t * viewsector) { float blend[4]={0,0,0,0}; PalEntry blendv=0; @@ -582,11 +572,11 @@ void FGLRenderer::DrawBlend(sector_t * viewsector) { double lightbottom; if (i < lightlist.Size() - 1) - lightbottom = lightlist[i + 1].plane.ZatPoint(ViewPos); + lightbottom = lightlist[i + 1].plane.ZatPoint(r_viewpoint.Pos); else - lightbottom = viewsector->floorplane.ZatPoint(ViewPos); + lightbottom = viewsector->floorplane.ZatPoint(r_viewpoint.Pos); - if (lightbottom < ViewPos.Z && (!lightlist[i].caster || !(lightlist[i].caster->flags&FF_FADEWALLS))) + if (lightbottom < r_viewpoint.Pos.Z && (!lightlist[i].caster || !(lightlist[i].caster->flags&FF_FADEWALLS))) { // 3d floor 'fog' is rendered as a blending value blendv = lightlist[i].blend; @@ -605,7 +595,7 @@ void FGLRenderer::DrawBlend(sector_t * viewsector) if (blendv.a == 255) { // The calculated average is too dark so brighten it according to the palettes's overall brightness - int maxcol = MAX(MAX(framebuffer->palette_brightness, blendv.r), MAX(blendv.g, blendv.b)); + int maxcol = MAX(MAX(GLRenderer->framebuffer->palette_brightness, blendv.r), MAX(blendv.g, blendv.b)); blendv.r = blendv.r * 255 / maxcol; blendv.g = blendv.g * 255 / maxcol; blendv.b = blendv.b * 255 / maxcol; @@ -676,7 +666,7 @@ void FGLRenderer::DrawBlend(sector_t * viewsector) //----------------------------------------------------------------------------- -void FGLRenderer::EndDrawScene(sector_t * viewsector) +void GLSceneDrawer::EndDrawScene(sector_t * viewsector) { gl_RenderState.EnableFog(false); @@ -688,19 +678,19 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) { // [BB] The HUD model should be drawn over everything else already drawn. glClear(GL_DEPTH_BUFFER_BIT); - DrawPlayerSprites (viewsector, true); + GLRenderer->DrawPlayerSprites (viewsector, true); } glDisable(GL_STENCIL_TEST); - framebuffer->Begin2D(false); + GLRenderer->framebuffer->Begin2D(false); Reset3DViewport(); // [BB] Only draw the sprites if we didn't render a HUD model before. if ( renderHUDModel == false ) { - DrawPlayerSprites (viewsector, false); + GLRenderer->DrawPlayerSprites (viewsector, false); } if (gl.legacyMode) { @@ -709,7 +699,7 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) gl_RenderState.SetFixedColormap(CM_DEFAULT); gl_RenderState.SetSoftLightLevel(-1); - DrawTargeterSprites(); + GLRenderer->DrawTargeterSprites(); if (!FGLRenderBuffers::IsEnabled()) { DrawBlend(viewsector); @@ -729,13 +719,13 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) // //----------------------------------------------------------------------------- -void FGLRenderer::ProcessScene(bool toscreen) +void GLSceneDrawer::ProcessScene(bool toscreen) { FDrawInfo::StartDrawInfo(); iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0; GLPortal::BeginScene(); - int mapsection = R_PointInSubsector(ViewPos)->mapsection; + int mapsection = R_PointInSubsector(r_viewpoint.Pos)->mapsection; memset(¤tmapsection[0], 0, currentmapsection.Size()); currentmapsection[mapsection>>3] |= 1 << (mapsection & 7); DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN); @@ -749,7 +739,7 @@ void FGLRenderer::ProcessScene(bool toscreen) // //----------------------------------------------------------------------------- -void FGLRenderer::SetFixedColormap (player_t *player) +void GLSceneDrawer::SetFixedColormap (player_t *player) { gl_fixedcolormap=CM_DEFAULT; @@ -760,7 +750,7 @@ void FGLRenderer::SetFixedColormap (player_t *player) if (cplayer->extralight == INT_MIN) { gl_fixedcolormap=CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP; - extralight=0; + r_viewpoint.extralight=0; } else if (cplayer->fixedcolormap != NOFIXEDCOLORMAP) { @@ -795,42 +785,42 @@ void FGLRenderer::SetFixedColormap (player_t *player) // //----------------------------------------------------------------------------- -sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) +sector_t * GLSceneDrawer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) { sector_t * lviewsector; - mSceneClearColor[0] = 0.0f; - mSceneClearColor[1] = 0.0f; - mSceneClearColor[2] = 0.0f; - R_SetupFrame (camera); + GLRenderer->mSceneClearColor[0] = 0.0f; + GLRenderer->mSceneClearColor[1] = 0.0f; + GLRenderer->mSceneClearColor[2] = 0.0f; + R_SetupFrame (r_viewpoint, r_viewwindow, camera); SetViewArea(); // We have to scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1. - double radPitch = ViewPitch.Normalized180().Radians(); + double radPitch = r_viewpoint.Angles.Pitch.Normalized180().Radians(); double angx = cos(radPitch); double angy = sin(radPitch) * glset.pixelstretch; double alen = sqrt(angx*angx + angy*angy); - mAngles.Pitch = (float)RAD2DEG(asin(angy / alen)); - mAngles.Roll.Degrees = ViewRoll.Degrees; + GLRenderer->mAngles.Pitch = (float)RAD2DEG(asin(angy / alen)); + GLRenderer->mAngles.Roll.Degrees = r_viewpoint.Angles.Roll.Degrees; // Scroll the sky - mSky1Pos = (float)fmod(gl_frameMS * level.skyspeed1, 1024.f) * 90.f/256.f; - mSky2Pos = (float)fmod(gl_frameMS * level.skyspeed2, 1024.f) * 90.f/256.f; + GLRenderer->mSky1Pos = (float)fmod(gl_frameMS * level.skyspeed1, 1024.f) * 90.f/256.f; + GLRenderer->mSky2Pos = (float)fmod(gl_frameMS * level.skyspeed2, 1024.f) * 90.f/256.f; if (camera->player && camera->player-players==consoleplayer && ((camera->player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)) && camera==camera->player->mo) { - mViewActor=NULL; + GLRenderer->mViewActor=NULL; } else { - mViewActor=camera; + GLRenderer->mViewActor=camera; } // 'viewsector' will not survive the rendering so it cannot be used anymore below. - lviewsector = viewsector; + lviewsector = r_viewpoint.sector; // Render (potentially) multiple views for stereo 3d float viewShift[3]; @@ -840,46 +830,42 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo { const s3d::EyePose * eye = stereo3dMode.getEyePose(eye_ix); eye->SetUp(); - SetOutputViewport(bounds); + GLRenderer->SetOutputViewport(bounds); Set3DViewport(mainview); - mDrawingScene2D = true; - mCurrentFoV = fov; + GLRenderer->mDrawingScene2D = true; + GLRenderer->mCurrentFoV = fov; // Stereo mode specific perspective projection SetProjection( eye->GetProjection(fov, ratio, fovratio) ); // SetProjection(fov, ratio, fovratio); // switch to perspective mode and set up clipper - SetViewAngle(ViewAngle); + SetViewAngle(r_viewpoint.Angles.Yaw); // Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos eye->GetViewShift(GLRenderer->mAngles.Yaw.Degrees, viewShift); s3d::ScopedViewShifter viewShifter(viewShift); - SetViewMatrix(ViewPos.X, ViewPos.Y, ViewPos.Z, false, false); + SetViewMatrix(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, false, false); gl_RenderState.ApplyMatrices(); - clipper.Clear(); - angle_t a1 = FrustumAngle(); - clipper.SafeAddClipRangeRealAngles(ViewAngle.BAMs() + a1, ViewAngle.BAMs() - a1); - ProcessScene(toscreen); if (mainview && toscreen) EndDrawScene(lviewsector); // do not call this for camera textures. if (mainview && FGLRenderBuffers::IsEnabled()) { - PostProcessScene(); + GLRenderer->PostProcessScene(); // This should be done after postprocessing, not before. - mBuffers->BindCurrentFB(); - glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); + GLRenderer->mBuffers->BindCurrentFB(); + glViewport(GLRenderer->mScreenViewport.left, GLRenderer->mScreenViewport.top, GLRenderer->mScreenViewport.width, GLRenderer->mScreenViewport.height); if (!toscreen) { gl_RenderState.mViewMatrix.loadIdentity(); - gl_RenderState.mProjectionMatrix.ortho(mScreenViewport.left, mScreenViewport.width, mScreenViewport.height, mScreenViewport.top, -1.0f, 1.0f); + gl_RenderState.mProjectionMatrix.ortho(GLRenderer->mScreenViewport.left, GLRenderer->mScreenViewport.width, GLRenderer->mScreenViewport.height, GLRenderer->mScreenViewport.top, -1.0f, 1.0f); gl_RenderState.ApplyMatrices(); } DrawBlend(lviewsector); } - mDrawingScene2D = false; + GLRenderer->mDrawingScene2D = false; if (!stereo3dMode.IsMono() && FGLRenderBuffers::IsEnabled()) - mBuffers->BlitToEyeTexture(eye_ix); + GLRenderer->mBuffers->BlitToEyeTexture(eye_ix); eye->TearDown(); } stereo3dMode.TearDown(); @@ -895,29 +881,20 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo // //----------------------------------------------------------------------------- -void FGLRenderer::RenderView (player_t* player) +void GLSceneDrawer::RenderView (player_t* player) { - OpenGLFrameBuffer* GLTarget = static_cast(screen); - AActor *&LastCamera = GLTarget->LastCamera; checkBenchActive(); - if (player->camera != LastCamera) - { - // If the camera changed don't interpolate - // Otherwise there will be some not so nice effects. - R_ResetViewInterpolation(); - LastCamera=player->camera; - } - gl_RenderState.SetVertexBuffer(mVBO); + gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); GLRenderer->mVBO->Reset(); // reset statistics counters ResetProfilingData(); // Get this before everything else - if (cl_capfps || r_NoInterpolate) r_TicFracF = 1.; - else r_TicFracF = I_GetTimeFrac (&r_FrameTime); + if (cl_capfps || r_NoInterpolate) r_viewpoint.TicFrac = 1.; + else r_viewpoint.TicFrac = I_GetTimeFrac (&r_viewpoint.FrameTime); gl_frameMS = I_MSTime(); P_FindParticleSubsectors (); @@ -934,8 +911,8 @@ void FGLRenderer::RenderView (player_t* player) // now render the main view float fovratio; - float ratio = WidescreenRatio; - if (WidescreenRatio >= 1.3f) + float ratio = r_viewwindow.WidescreenRatio; + if (r_viewwindow.WidescreenRatio >= 1.3f) { fovratio = 1.333333f; } @@ -950,7 +927,8 @@ void FGLRenderer::RenderView (player_t* player) TThinkerIterator it(STAT_DLIGHT); GLRenderer->mLightCount = ((it.Next()) != NULL); - sector_t * viewsector = RenderViewpoint(player->camera, NULL, FieldOfView.Degrees, ratio, fovratio, true, true); + GLRenderer->mShadowMap.Update(); + sector_t * viewsector = RenderViewpoint(player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true); All.Unclock(); } @@ -961,7 +939,7 @@ void FGLRenderer::RenderView (player_t* player) // //=========================================================================== -void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) +void GLSceneDrawer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) { GL_IRECT bounds; @@ -971,7 +949,7 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i bounds.height=height; glFlush(); SetFixedColormap(player); - gl_RenderState.SetVertexBuffer(mVBO); + gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); GLRenderer->mVBO->Reset(); if (!gl.legacyMode) GLRenderer->mLights->Clear(); @@ -979,8 +957,8 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i TThinkerIterator it(STAT_DLIGHT); GLRenderer->mLightCount = ((it.Next()) != NULL); - sector_t *viewsector = RenderViewpoint(players[consoleplayer].camera, &bounds, - FieldOfView.Degrees, 1.6f, 1.6f, true, false); + sector_t *viewsector = RenderViewpoint(players[consoleplayer].camera, &bounds, + r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false); glDisable(GL_STENCIL_TEST); gl_RenderState.SetFixedColormap(CM_DEFAULT); gl_RenderState.SetSoftLightLevel(-1); @@ -989,7 +967,7 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i { DrawBlend(viewsector); } - CopyToBackbuffer(&bounds, false); + GLRenderer->CopyToBackbuffer(&bounds, false); glFlush(); uint8_t * scr = (uint8_t *)M_Malloc(width * height * 3); @@ -1008,23 +986,20 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i struct FGLInterface : public FRenderer { bool UsesColormap() const override; - void PrecacheTexture(FTexture *tex, int cache); - void PrecacheSprite(FTexture *tex, SpriteHits &hits); - void Precache(BYTE *texhitlist, TMap &actorhitlist) override; + void Precache(uint8_t *texhitlist, TMap &actorhitlist) override; void RenderView(player_t *player) override; void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override; - void StateChanged(AActor *actor) override; void StartSerialize(FSerializer &arc) override; void EndSerialize(FSerializer &arc) override; void RenderTextureView (FCanvasTexture *self, AActor *viewpoint, int fov) override; - sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) override; + sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) override; void SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog) override; void PreprocessLevel() override; void CleanLevelData() override; bool RequireGLNodes() override; int GetMaxViewPitch(bool down) override; - void ClearBuffer(int color) override; + void SetClearColor(int color) override; void Init() override; }; @@ -1040,198 +1015,16 @@ bool FGLInterface::UsesColormap() const return false; } -//========================================================================== -// -// DFrameBuffer :: PrecacheTexture -// -//========================================================================== - -void FGLInterface::PrecacheTexture(FTexture *tex, int cache) -{ - if (cache & (FTextureManager::HIT_Wall | FTextureManager::HIT_Flat | FTextureManager::HIT_Sky)) - { - FMaterial * gltex = FMaterial::ValidateTexture(tex, false); - if (gltex) gltex->Precache(); - } - else - { - // make sure that software pixel buffers do not stick around for unneeded textures. - tex->Unload(); - } -} - -//========================================================================== -// -// DFrameBuffer :: PrecacheSprite -// -//========================================================================== - -void FGLInterface::PrecacheSprite(FTexture *tex, SpriteHits &hits) -{ - FMaterial * gltex = FMaterial::ValidateTexture(tex, true); - if (gltex) gltex->PrecacheList(hits); -} - //========================================================================== // // DFrameBuffer :: Precache // //========================================================================== +void gl_PrecacheTexture(uint8_t *texhitlist, TMap &actorhitlist); -void FGLInterface::Precache(BYTE *texhitlist, TMap &actorhitlist) -{ - SpriteHits *spritelist = new SpriteHits[sprites.Size()]; - SpriteHits **spritehitlist = new SpriteHits*[TexMan.NumTextures()]; - TMap::Iterator it(actorhitlist); - TMap::Pair *pair; - BYTE *modellist = new BYTE[Models.Size()]; - memset(modellist, 0, Models.Size()); - memset(spritehitlist, 0, sizeof(SpriteHits**) * TexMan.NumTextures()); - - // this isn't done by the main code so it needs to be done here first: - // check skybox textures and mark the separate faces as used - for (int i = 0; igl_info.bSkybox) - { - FSkyBox *sb = static_cast(tex); - for (int i = 0; i<6; i++) - { - if (sb->faces[i]) - { - int index = sb->faces[i]->id.GetIndex(); - texhitlist[index] |= FTextureManager::HIT_Flat; - } - } - } - } - } - - // Check all used actors. - // 1. mark all sprites associated with its states - // 2. mark all model data and skins associated with its states - while (it.NextPair(pair)) - { - PClassActor *cls = pair->Key; - int gltrans = GLTranslationPalette::GetInternalTranslation(GetDefaultByType(cls)->Translation); - - for (int i = 0; i < cls->NumOwnedStates; i++) - { - spritelist[cls->OwnedStates[i].sprite].Insert(gltrans, true); - FSpriteModelFrame * smf = gl_FindModelFrame(cls, cls->OwnedStates[i].sprite, cls->OwnedStates[i].Frame, false); - if (smf != NULL) - { - for (int i = 0; i < MAX_MODELS_PER_FRAME; i++) - { - if (smf->skinIDs[i].isValid()) - { - texhitlist[smf->skinIDs[i].GetIndex()] |= FTexture::TEX_Flat; - } - else if (smf->modelIDs[i] != -1) - { - Models[smf->modelIDs[i]]->PushSpriteMDLFrame(smf, i); - Models[smf->modelIDs[i]]->AddSkins(texhitlist); - } - if (smf->modelIDs[i] != -1) - { - modellist[smf->modelIDs[i]] = 1; - } - } - } - } - } - - // mark all sprite textures belonging to the marked sprites. - for (int i = (int)(sprites.Size() - 1); i >= 0; i--) - { - if (spritelist[i].CountUsed()) - { - int j, k; - for (j = 0; j < sprites[i].numframes; j++) - { - const spriteframe_t *frame = &SpriteFrames[sprites[i].spriteframes + j]; - - for (k = 0; k < 16; k++) - { - FTextureID pic = frame->Texture[k]; - if (pic.isValid()) - { - spritehitlist[pic.GetIndex()] = &spritelist[i]; - } - } - } - } - } - - // delete everything unused before creating any new resources to avoid memory usage peaks. - - // delete unused models - for (unsigned i = 0; i < Models.Size(); i++) - { - if (!modellist[i]) Models[i]->DestroyVertexBuffer(); - } - - // delete unused textures - int cnt = TexMan.NumTextures(); - for (int i = cnt - 1; i >= 0; i--) - { - FTexture *tex = TexMan.ByIndex(i); - if (tex != nullptr) - { - if (!texhitlist[i]) - { - if (tex->gl_info.Material[0]) tex->gl_info.Material[0]->Clean(true); - } - if (spritehitlist[i] == nullptr || (*spritehitlist[i]).CountUsed() == 0) - { - if (tex->gl_info.Material[1]) tex->gl_info.Material[1]->Clean(true); - } - } - } - - if (gl_precache) - { - // cache all used textures - for (int i = cnt - 1; i >= 0; i--) - { - FTexture *tex = TexMan.ByIndex(i); - if (tex != nullptr) - { - PrecacheTexture(tex, texhitlist[i]); - if (spritehitlist[i] != nullptr && (*spritehitlist[i]).CountUsed() > 0) - { - PrecacheSprite(tex, *spritehitlist[i]); - } - } - } - - // cache all used models - for (unsigned i = 0; i < Models.Size(); i++) - { - if (modellist[i]) - Models[i]->BuildVertexBuffer(); - } - } - - delete[] spritehitlist; - delete[] spritelist; - delete[] modellist; -} - - -//========================================================================== -// -// DFrameBuffer :: StateChanged -// -//========================================================================== - -void FGLInterface::StateChanged(AActor *actor) -{ - gl_SetActorLights(actor); +void FGLInterface::Precache(uint8_t *texhitlist, TMap &actorhitlist) +{ + gl_PrecacheTexture(texhitlist, actorhitlist); } //=========================================================================== @@ -1255,7 +1048,6 @@ void FGLInterface::EndSerialize(FSerializer &arc) { if (arc.isReading()) { - gl_RecreateAllAttachedLights(); gl_InitPortals(); } } @@ -1279,7 +1071,7 @@ int FGLInterface::GetMaxViewPitch(bool down) // //=========================================================================== -void FGLInterface::ClearBuffer(int color) +void FGLInterface::SetClearColor(int color) { PalEntry pe = GPalette.BaseColors[color]; GLRenderer->mSceneClearColor[0] = pe.r / 255.f; @@ -1295,7 +1087,8 @@ void FGLInterface::ClearBuffer(int color) void FGLInterface::WriteSavePic (player_t *player, FileWriter *file, int width, int height) { - GLRenderer->WriteSavePic(player, file, width, height); + GLSceneDrawer drawer; + drawer.WriteSavePic(player, file, width, height); } //=========================================================================== @@ -1306,7 +1099,8 @@ void FGLInterface::WriteSavePic (player_t *player, FileWriter *file, int width, void FGLInterface::RenderView(player_t *player) { - GLRenderer->RenderView(player); + GLSceneDrawer drawer; + drawer.RenderView(player); } //=========================================================================== @@ -1317,7 +1111,6 @@ void FGLInterface::RenderView(player_t *player) void FGLInterface::Init() { - gl_ParseDefs(); gl_InitData(); } @@ -1336,9 +1129,6 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, in int width = gltex->TextureWidth(); int height = gltex->TextureHeight(); - gl_fixedcolormap=CM_DEFAULT; - gl_RenderState.SetFixedColormap(CM_DEFAULT); - if (gl.legacyMode) { // In legacy mode, fail if the requested texture is too large. @@ -1356,7 +1146,10 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, in bounds.width=FHardwareTexture::GetTexDimension(gltex->GetWidth()); bounds.height=FHardwareTexture::GetTexDimension(gltex->GetHeight()); - GLRenderer->RenderViewpoint(Viewpoint, &bounds, FOV, (float)width/height, (float)width/height, false, false); + GLSceneDrawer drawer; + gl_fixedcolormap = CM_DEFAULT; + gl_RenderState.SetFixedColormap(CM_DEFAULT); + drawer.RenderViewpoint(Viewpoint, &bounds, FOV, (float)width/height, (float)width/height, false, false); if (gl.legacyMode) { @@ -1379,7 +1172,7 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, in // //========================================================================== -sector_t *FGLInterface::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) +sector_t *FGLInterface::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) { if (floorlightlevel != NULL) { @@ -1389,7 +1182,7 @@ sector_t *FGLInterface::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlig { *ceilinglightlevel = sec->GetCeilingLight (); } - return gl_FakeFlat(sec, tempsec, back); + return gl_FakeFlat(sec, tempsec, false); } //=========================================================================== diff --git a/src/gl/scene/gl_scenedrawer.h b/src/gl/scene/gl_scenedrawer.h new file mode 100644 index 0000000000..fda91e6866 --- /dev/null +++ b/src/gl/scene/gl_scenedrawer.h @@ -0,0 +1,69 @@ +#pragma once + +#include "r_defs.h" +#include "m_fixed.h" +#include "gl_clipper.h" +#include "gl_portal.h" + +class GLSceneDrawer +{ + fixed_t viewx, viewy; // since the nodes are still fixed point, keeping the view position also fixed point for node traversal is faster. + + subsector_t *currentsubsector; // used by the line processing code. + sector_t *currentsector; + + void RenderMultipassStuff(); + + void UnclipSubsector(subsector_t *sub); + void AddLine (seg_t *seg, bool portalclip); + void PolySubsector(subsector_t * sub); + void RenderPolyBSPNode (void *node); + void AddPolyobjs(subsector_t *sub); + void AddLines(subsector_t * sub, sector_t * sector); + void AddSpecialPortalLines(subsector_t * sub, sector_t * sector, line_t *line); + void RenderThings(subsector_t * sub, sector_t * sector); + void DoSubsector(subsector_t * sub); + void RenderBSPNode(void *node); + + void RenderScene(int recursion); + void RenderTranslucent(); + + void CreateScene(); + +public: + GLSceneDrawer() + { + GLPortal::drawer = this; + } + + Clipper clipper; + + angle_t FrustumAngle(); + void SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror); + void SetViewArea(); + void SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror); + void SetViewAngle(DAngle viewangle); + void SetProjection(VSMatrix matrix); + void Set3DViewport(bool mainview); + void Reset3DViewport(); + void SetFixedColormap(player_t *player); + void DrawScene(int drawmode); + void ProcessScene(bool toscreen = false); + void DrawBlend(sector_t * viewsector); + void EndDrawScene(sector_t * viewsector); + + sector_t *RenderViewpoint(AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); + void RenderView(player_t *player); + void WriteSavePic(player_t *player, FileWriter *file, int width, int height); + + void InitClipper(angle_t a1, angle_t a2) + { + clipper.SafeAddClipRangeRealAngles(a1, a2); + } + + void SetView() + { + viewx = FLOAT2FIXED(r_viewpoint.Pos.X); + viewy = FLOAT2FIXED(r_viewpoint.Pos.Y); + } +}; diff --git a/src/gl/scene/gl_sky.cpp b/src/gl/scene/gl_sky.cpp index 7fe92b8905..70e2686a7d 100644 --- a/src/gl/scene/gl_sky.cpp +++ b/src/gl/scene/gl_sky.cpp @@ -159,8 +159,8 @@ void GLWall::SkyPlane(sector_t *sector, int plane, bool allowreflect) } else if (allowreflect && sector->GetReflect(plane) > 0) { - if ((plane == sector_t::ceiling && ViewPos.Z > sector->ceilingplane.fD()) || - (plane == sector_t::floor && ViewPos.Z < -sector->floorplane.fD())) return; + if ((plane == sector_t::ceiling && r_viewpoint.Pos.Z > sector->ceilingplane.fD()) || + (plane == sector_t::floor && r_viewpoint.Pos.Z < -sector->floorplane.fD())) return; ptype = PORTALTYPE_PLANEMIRROR; planemirror = plane == sector_t::ceiling ? §or->ceilingplane : §or->floorplane; } @@ -345,7 +345,7 @@ void GLWall::SkyBottom(seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,ver else { // Special hack for Vrack2b - if (bs->floorplane.ZatPoint(ViewPos) > ViewPos.Z) return; + if (bs->floorplane.ZatPoint(r_viewpoint.Pos) > r_viewpoint.Pos.Z) return; } } zbottom[0]=zbottom[1]=-32768.0f; diff --git a/src/gl/scene/gl_skydome.cpp b/src/gl/scene/gl_skydome.cpp index 1d9cf9b3b9..ea19c8ca38 100644 --- a/src/gl/scene/gl_skydome.cpp +++ b/src/gl/scene/gl_skydome.cpp @@ -68,6 +68,7 @@ #include "gl/renderer/gl_lightdata.h" #include "gl/renderer/gl_renderstate.h" #include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_scenedrawer.h" #include "gl/scene/gl_portal.h" #include "gl/shaders/gl_shader.h" #include "gl/textures/gl_bitmap.h" @@ -519,7 +520,7 @@ void GLSkyPortal::DrawContents() bool oldClamp = gl_RenderState.SetDepthClamp(true); gl_MatrixStack.Push(gl_RenderState.mViewMatrix); - GLRenderer->SetupView(0, 0, 0, ViewAngle, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + drawer->SetupView(0, 0, 0, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO); if (origin->texture[0] && origin->texture[0]->tex->gl_info.bSkybox) diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 030daf91c8..ff2d5189fe 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -38,6 +38,7 @@ #include "d_player.h" #include "g_levellocals.h" #include "events.h" +#include "actorinlines.h" #include "gl/system/gl_interface.h" #include "gl/system/gl_framebuffer.h" @@ -77,7 +78,6 @@ EXTERN_CVAR (Float, transsouls) extern TArray sprites; extern TArray SpriteFrames; extern TArray BloodTranslationColors; -extern bool r_showviewer; enum HWRenderStyle { @@ -162,7 +162,7 @@ void GLSprite::CalculateVertices(FVector3 *v) // [fgsfds] check sprite type mask - DWORD spritetype = (DWORD)-1; + uint32_t spritetype = (uint32_t)-1; if (actor != nullptr) spritetype = actor->renderflags & RF_SPRITETYPEMASK; // [Nash] is a flat sprite @@ -188,8 +188,8 @@ void GLSprite::CalculateVertices(FVector3 *v) { // [CMB] Rotate relative to camera XY position, not just camera direction, // which is nicer in VR - float xrel = xcenter - ViewPos.X; - float yrel = ycenter - ViewPos.Y; + float xrel = xcenter - r_viewpoint.Pos.X; + float yrel = ycenter - r_viewpoint.Pos.Y; float absAngleDeg = RAD2DEG(atan2(-yrel, xrel)); float counterRotationDeg = 270. - GLRenderer->mAngles.Yaw.Degrees; // counteracts existing sprite rotation float relAngleDeg = counterRotationDeg + absAngleDeg; @@ -297,7 +297,7 @@ void GLSprite::Draw(int pass) // fog + fuzz don't work well without some fiddling with the alpha value! if (!gl_isBlack(Colormap.FadeColor)) { - float dist=Dist2(ViewPos.X, ViewPos.Y, x,y); + float dist=Dist2(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, x,y); int fogd = gl_GetFogDensity(lightlevel, Colormap.FadeColor, Colormap.fogdensity); // this value was determined by trial and error and is scale dependent! @@ -395,7 +395,7 @@ void GLSprite::Draw(int pass) secplane_t *lowplane = i == (*lightlist).Size() - 1 ? &bottomp : &(*lightlist)[i + 1].plane; int thislight = (*lightlist)[i].caster != NULL ? gl_ClampLight(*(*lightlist)[i].p_lightlevel) : lightlevel; - int thisll = actor == nullptr? thislight : (uint8_t)gl_CheckSpriteGlow(actor->Sector, thislight, actor->InterpolatedPosition(r_TicFracF)); + int thisll = actor == nullptr? thislight : (uint8_t)gl_CheckSpriteGlow(actor->Sector, thislight, actor->InterpolatedPosition(r_viewpoint.TicFrac)); FColormap thiscm; thiscm.FadeColor = Colormap.FadeColor; @@ -670,6 +670,8 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) return; } + AActor *camera = r_viewpoint.camera; + if (thing->renderflags & RF_INVISIBLE || !thing->RenderStyle.IsVisible(thing->Alpha)) { if (!(thing->flags & MF_STEALTH) || !gl_fixedcolormap || !gl_enhanced_nightvision || thing == camera) @@ -686,26 +688,26 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) if (!thruportal && !(currentmapsection[thing->subsector->mapsection >> 3] & (1 << (thing->subsector->mapsection & 7)))) return; // [RH] Interpolate the sprite's position to make it look smooth - DVector3 thingpos = thing->InterpolatedPosition(r_TicFracF); + DVector3 thingpos = thing->InterpolatedPosition(r_viewpoint.TicFrac); if (thruportal == 1) thingpos += Displacements.getOffset(thing->Sector->PortalGroup, sector->PortalGroup); // Some added checks if the camera actor is not supposed to be seen. It can happen that some portal setup has this actor in view in which case it may not be skipped here - if (thing == camera && !r_showviewer) + if (thing == camera && !r_viewpoint.showviewer) { DVector3 thingorigin = thing->Pos(); if (thruportal == 1) thingorigin += Displacements.getOffset(thing->Sector->PortalGroup, sector->PortalGroup); - if (fabs(thingorigin.X - ViewActorPos.X) < 2 && fabs(thingorigin.Y - ViewActorPos.Y) < 2) return; + if (fabs(thingorigin.X - r_viewpoint.ActorPos.X) < 2 && fabs(thingorigin.Y - r_viewpoint.ActorPos.Y) < 2) return; } // Thing is invisible if close to the camera. if (thing->renderflags & RF_MAYBEINVISIBLE) { - if (fabs(thingpos.X - ViewPos.X) < 32 && fabs(thingpos.Y - ViewPos.Y) < 32) return; + if (fabs(thingpos.X - r_viewpoint.Pos.X) < 32 && fabs(thingpos.Y - r_viewpoint.Pos.Y) < 32) return; } // Too close to the camera. This doesn't look good if it is a sprite. - if (fabs(thingpos.X - ViewPos.X) < 2 && fabs(thingpos.Y - ViewPos.Y) < 2) + if (fabs(thingpos.X - r_viewpoint.Pos.X) < 2 && fabs(thingpos.Y - r_viewpoint.Pos.Y) < 2) { - if (ViewPos.Z >= thingpos.Z - 2 && ViewPos.Z <= thingpos.Z + thing->Height + 2) + if (r_viewpoint.Pos.Z >= thingpos.Z - 2 && r_viewpoint.Pos.Z <= thingpos.Z + thing->Height + 2) { // exclude vertically moving objects from this check. if (!thing->Vel.isZero()) @@ -727,7 +729,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) if (speed >= thing->target->radius / 2) { double clipdist = clamp(thing->Speed, thing->target->radius, thing->target->radius * 2); - if ((thingpos - ViewPos).LengthSquared() < clipdist * clipdist) return; + if ((thingpos - r_viewpoint.Pos).LengthSquared() < clipdist * clipdist) return; } } thing->flags7 |= MF7_FLYCHEAT; // do this only once for the very first frame, but not if it gets into range again. @@ -740,7 +742,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) } // disabled because almost none of the actual game code is even remotely prepared for this. If desired, use the INTERPOLATE flag. if (thing->renderflags & RF_INTERPOLATEANGLES) - Angles = thing->InterpolatedAngles(r_TicFracF); + Angles = thing->InterpolatedAngles(r_viewpoint.TicFrac); else Angles = thing->Angles; @@ -758,7 +760,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) topclip = rendersector->PortalBlocksMovement(sector_t::ceiling) ? LARGE_VALUE : rendersector->GetPortalPlaneZ(sector_t::ceiling); bottomclip = rendersector->PortalBlocksMovement(sector_t::floor) ? -LARGE_VALUE : rendersector->GetPortalPlaneZ(sector_t::floor); - DWORD spritetype = (thing->renderflags & RF_SPRITETYPEMASK); + uint32_t spritetype = (thing->renderflags & RF_SPRITETYPEMASK); x = thingpos.X; z = thingpos.Z; y = thingpos.Y; @@ -767,7 +769,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) // [RH] Make floatbobbing a renderer-only effect. if (thing->flags2 & MF2_FLOATBOB) { - float fz = thing->GetBobOffset(r_TicFracF); + float fz = thing->GetBobOffset(r_viewpoint.TicFrac); z += fz; } @@ -775,7 +777,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) if (!modelframe) { bool mirror; - DAngle ang = (thingpos - ViewPos).Angle(); + DAngle ang = (thingpos - r_viewpoint.Pos).Angle(); FTextureID patch; // [ZZ] add direct picnum override if (isPicnumOverride) @@ -878,7 +880,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) gltexture=NULL; } - depth = FloatToFixed((x - ViewPos.X) * ViewTanCos + (y - ViewPos.Y) * ViewTanSin); + depth = FloatToFixed((x - r_viewpoint.Pos.X) * r_viewpoint.TanCos + (y - r_viewpoint.Pos.Y) * r_viewpoint.TanSin); // light calculation @@ -892,7 +894,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) lightlevel=fullbright? 255 : gl_ClampLight(rendersector->GetTexture(sector_t::ceiling) == skyflatnum ? rendersector->GetCeilingLight() : rendersector->GetFloorLight()); - foglevel = (BYTE)clamp(rendersector->lightlevel, 0, 255); + foglevel = (uint8_t)clamp(rendersector->lightlevel, 0, 255); lightlevel = gl_CheckSpriteGlow(rendersector, lightlevel, thingpos); @@ -1093,7 +1095,7 @@ void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int s lightlevel = gl_ClampLight(sector->GetTexture(sector_t::ceiling) == skyflatnum ? sector->GetCeilingLight() : sector->GetFloorLight()); - foglevel = (BYTE)clamp(sector->lightlevel, 0, 255); + foglevel = (uint8_t)clamp(sector->lightlevel, 0, 255); if (gl_fixedcolormap) { @@ -1187,7 +1189,7 @@ void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int s z1=z-scalefac; z2=z+scalefac; - depth = FloatToFixed((x - ViewPos.X) * ViewTanCos + (y - ViewPos.Y) * ViewTanSin); + depth = FloatToFixed((x - r_viewpoint.Pos.X) * r_viewpoint.TanCos + (y - r_viewpoint.Pos.Y) * r_viewpoint.TanSin); actor=NULL; this->particle=particle; @@ -1241,9 +1243,9 @@ void gl_RenderActorsInPortal(FGLLinePortal *glport) DVector3 newpos = savedpos; sector_t fakesector; - if (!r_showviewer && th == camera) + if (!r_viewpoint.showviewer && th == r_viewpoint.camera) { - if (fabs(savedpos.X - ViewActorPos.X) < 2 && fabs(savedpos.Y - ViewActorPos.Y) < 2) + if (fabs(savedpos.X - r_viewpoint.ActorPos.X) < 2 && fabs(savedpos.Y - r_viewpoint.ActorPos.Y) < 2) { continue; } diff --git a/src/gl/scene/gl_spritelight.cpp b/src/gl/scene/gl_spritelight.cpp index 6efa078ca9..3283f00862 100644 --- a/src/gl/scene/gl_spritelight.cpp +++ b/src/gl/scene/gl_spritelight.cpp @@ -91,7 +91,7 @@ void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * frac = 1.0f - (dist / radius); - if (frac > 0) + if (frac > 0 && GLRenderer->mShadowMap.ShadowTest(light, { x, y, z })) { lr = light->GetRed() / 255.0f; lg = light->GetGreen() / 255.0f; diff --git a/src/gl/scene/gl_wall.h b/src/gl/scene/gl_wall.h index 7337c4d3f4..83b6ac66fb 100644 --- a/src/gl/scene/gl_wall.h +++ b/src/gl/scene/gl_wall.h @@ -6,6 +6,7 @@ // //========================================================================== #include "r_defs.h" +#include "r_data/renderstyle.h" #include "textures/textures.h" #include "gl/renderer/gl_colormap.h" @@ -63,11 +64,18 @@ struct GLSeg FVector3 Normal() const { - // we do not use the vector math inlines here because they are not optimized for speed but accuracy in the playsim + // we do not use the vector math inlines here because they are not optimized for speed but accuracy in the playsim and this is called quite frequently. float x = y2 - y1; float y = x1 - x2; +#if defined(__amd64__) || defined(_M_X64) + __m128 v; + v.m128_f32[0] = x*x + y*y; + float ilength = _mm_rsqrt_ss(v).m128_f32[0]; + return FVector3(x * ilength, 0, y * ilength); +#else float length = sqrtf(x*x + y*y); return FVector3(x / length, 0, y / length); +#endif } }; @@ -153,8 +161,8 @@ public: TArray *lightlist; int lightlevel; - BYTE type; - BYTE flags; + uint8_t type; + uint8_t flags; short rellight; float topglowcolor[4]; @@ -309,7 +317,7 @@ public: int lightlevel; bool stack; bool ceiling; - BYTE renderflags; + uint8_t renderflags; int vboindex; //int vboheight; @@ -348,8 +356,8 @@ public: friend void Mod_RenderModel(GLSprite * spr, model_t * mdl, int framenumber); int lightlevel; - BYTE foglevel; - BYTE hw_styleflags; + uint8_t foglevel; + uint8_t hw_styleflags; bool fullbright; PalEntry ThingColor; // thing's own color FColormap Colormap; diff --git a/src/gl/scene/gl_walls.cpp b/src/gl/scene/gl_walls.cpp index fc5606b47f..647c1b7e73 100644 --- a/src/gl/scene/gl_walls.cpp +++ b/src/gl/scene/gl_walls.cpp @@ -87,7 +87,7 @@ void GLWall::PutWall(bool translucent) if (translucent) // translucent walls { - ViewDistance = (ViewPos - (seg->linedef->v1->fPos() + seg->linedef->Delta() / 2)).XY().LengthSquared(); + ViewDistance = (r_viewpoint.Pos - (seg->linedef->v1->fPos() + seg->linedef->Delta() / 2)).XY().LengthSquared(); if (gl.buffermethod == BM_DEFERRED) MakeVertices(true); gl_drawinfo->drawlists[GLDL_TRANSLUCENT].AddWall(this); } @@ -430,10 +430,10 @@ bool GLWall::DoHorizon(seg_t * seg,sector_t * fs, vertex_t * v1,vertex_t * v2) ztop[1] = ztop[0] = fs->GetPlaneTexZ(sector_t::ceiling); zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor); - if (ViewPos.Z < fs->GetPlaneTexZ(sector_t::ceiling)) + if (r_viewpoint.Pos.Z < fs->GetPlaneTexZ(sector_t::ceiling)) { - if (ViewPos.Z > fs->GetPlaneTexZ(sector_t::floor)) - zbottom[1] = zbottom[0] = ViewPos.Z; + if (r_viewpoint.Pos.Z > fs->GetPlaneTexZ(sector_t::floor)) + zbottom[1] = zbottom[0] = r_viewpoint.Pos.Z; if (fs->GetTexture(sector_t::ceiling) == skyflatnum) { @@ -460,7 +460,7 @@ bool GLWall::DoHorizon(seg_t * seg,sector_t * fs, vertex_t * v1,vertex_t * v2) ztop[1] = ztop[0] = zbottom[0]; } - if (ViewPos.Z > fs->GetPlaneTexZ(sector_t::floor)) + if (r_viewpoint.Pos.Z > fs->GetPlaneTexZ(sector_t::floor)) { zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor); if (fs->GetTexture(sector_t::floor) == skyflatnum) @@ -711,7 +711,7 @@ void GLWall::DoTexture(int _type,seg_t * seg, int peg, GLSeg glsave=glseg; float flh=ceilingrefheight-floorrefheight; int texpos; - BYTE savedflags = flags; + uint8_t savedflags = flags; switch (_type) { diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 34a4cc2825..31c88e46e8 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -25,6 +25,8 @@ #include "p_lnspec.h" #include "a_sharedglobal.h" #include "g_levellocals.h" +#include "actor.h" +#include "actorinlines.h" #include "gl/gl_functions.h" #include "gl/system/gl_interface.h" @@ -72,12 +74,14 @@ void GLWall::SetupLights() Plane p; lightdata.Clear(); - p.Init(vtx,4); + p.Set(&glseg); + /* if (!p.ValidNormal()) { return; } + */ FLightNode *node; if (seg->sidedef == NULL) { @@ -101,8 +105,6 @@ void GLWall::SetupLights() { iter_dlight++; - Vector fn, pos; - DVector3 posrel = node->lightsource->PosRelative(seg->frontsector); float x = posrel.X; float y = posrel.Y; @@ -110,29 +112,31 @@ void GLWall::SetupLights() float dist = fabsf(p.DistToPoint(x, z, y)); float radius = node->lightsource->GetRadius(); float scale = 1.0f / ((2.f * radius) - dist); + FVector3 fn, pos; if (radius > 0.f && dist < radius) { - Vector nearPt, up, right; + FVector3 nearPt, up, right; + + pos = { x, z, y }; + fn = p.Normal(); - pos.Set(x,z,y); - fn=p.Normal(); fn.GetRightUp(right, up); - Vector tmpVec = fn * dist; + FVector3 tmpVec = fn * dist; nearPt = pos + tmpVec; - Vector t1; + FVector3 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; + t1 = FVector3(&vtx[i*3]); + FVector3 nearToVert = t1 - nearPt; + tcs[i].u = ((nearToVert | right) * scale) + 0.5f; + tcs[i].v = ((nearToVert | up) * scale) + 0.5f; if (tcs[i].u<0) outcnt[0]++; if (tcs[i].u>1) outcnt[1]++; diff --git a/src/gl/scene/gl_weapon.cpp b/src/gl/scene/gl_weapon.cpp index 62a051e582..0734c580a0 100644 --- a/src/gl/scene/gl_weapon.cpp +++ b/src/gl/scene/gl_weapon.cpp @@ -96,7 +96,7 @@ void FGLRenderer::DrawPSprite (player_t * player,DPSprite *psp, float sx, float tex->GetSpriteRect(&r); // calculate edges of the shape - scalex = (320.0f / (240.0f * WidescreenRatio)) * vw / 320; + scalex = (320.0f / (240.0f * r_viewwindow.WidescreenRatio)) * vw / 320; tx = sx - (160 - r.left); x1 = tx * scalex + vw/2; @@ -201,6 +201,8 @@ void FGLRenderer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) s3d::Stereo3DMode::getCurrentMode().AdjustPlayerSprites(); + AActor *camera = r_viewpoint.camera; + // this is the same as the software renderer if (!player || !r_drawplayersprites || @@ -212,7 +214,7 @@ void FGLRenderer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) float bobx, boby, wx, wy; DPSprite *weapon; - P_BobWeapon(camera->player, &bobx, &boby, r_TicFracF); + P_BobWeapon(camera->player, &bobx, &boby, r_viewpoint.TicFrac); // Interpolate the main weapon layer once so as to be able to add it to other layers. if ((weapon = camera->player->FindPSprite(PSP_WEAPON)) != nullptr) @@ -224,8 +226,8 @@ void FGLRenderer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) } else { - wx = weapon->oldx + (weapon->x - weapon->oldx) * r_TicFracF; - wy = weapon->oldy + (weapon->y - weapon->oldy) * r_TicFracF; + wx = weapon->oldx + (weapon->x - weapon->oldx) * r_viewpoint.TicFrac; + wy = weapon->oldy + (weapon->y - weapon->oldy) * r_viewpoint.TicFrac; } } else @@ -257,11 +259,11 @@ void FGLRenderer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) if (ifloorplane.ZatPoint(ViewPos); + lightbottom=viewsector->floorplane.ZatPoint(r_viewpoint.Pos); } if (lightbottomviewz) @@ -427,8 +429,8 @@ void FGLRenderer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) psp->oldy = psp->y; } - float sx = psp->oldx + (psp->x - psp->oldx) * r_TicFracF; - float sy = psp->oldy + (psp->y - psp->oldy) * r_TicFracF; + float sx = psp->oldx + (psp->x - psp->oldx) * r_viewpoint.TicFrac; + float sy = psp->oldy + (psp->y - psp->oldy) * r_viewpoint.TicFrac; if (psp->Flags & PSPF_ADDBOB) { diff --git a/src/gl/shaders/gl_ambientshader.cpp b/src/gl/shaders/gl_ambientshader.cpp index effde24964..02090cf61c 100644 --- a/src/gl/shaders/gl_ambientshader.cpp +++ b/src/gl/shaders/gl_ambientshader.cpp @@ -21,7 +21,6 @@ // #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" diff --git a/src/gl/shaders/gl_bloomshader.cpp b/src/gl/shaders/gl_bloomshader.cpp index 44253ae79c..bc7b80b01c 100644 --- a/src/gl/shaders/gl_bloomshader.cpp +++ b/src/gl/shaders/gl_bloomshader.cpp @@ -26,7 +26,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" diff --git a/src/gl/shaders/gl_blurshader.cpp b/src/gl/shaders/gl_blurshader.cpp index cd4532cc48..56b637f6de 100644 --- a/src/gl/shaders/gl_blurshader.cpp +++ b/src/gl/shaders/gl_blurshader.cpp @@ -26,7 +26,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" diff --git a/src/gl/shaders/gl_colormapshader.cpp b/src/gl/shaders/gl_colormapshader.cpp index 06c72160a6..e29b270136 100644 --- a/src/gl/shaders/gl_colormapshader.cpp +++ b/src/gl/shaders/gl_colormapshader.cpp @@ -26,7 +26,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" diff --git a/src/gl/shaders/gl_lensshader.cpp b/src/gl/shaders/gl_lensshader.cpp index 9d242aa877..c5ec679f3a 100644 --- a/src/gl/shaders/gl_lensshader.cpp +++ b/src/gl/shaders/gl_lensshader.cpp @@ -26,7 +26,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" diff --git a/src/gl/shaders/gl_present3dRowshader.cpp b/src/gl/shaders/gl_present3dRowshader.cpp index d8369a3b6e..94dd7dc796 100644 --- a/src/gl/shaders/gl_present3dRowshader.cpp +++ b/src/gl/shaders/gl_present3dRowshader.cpp @@ -28,7 +28,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" diff --git a/src/gl/shaders/gl_presentshader.cpp b/src/gl/shaders/gl_presentshader.cpp index 15acc74fba..5fe040db9a 100644 --- a/src/gl/shaders/gl_presentshader.cpp +++ b/src/gl/shaders/gl_presentshader.cpp @@ -26,7 +26,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 421139050c..44be65ebe6 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -97,7 +97,11 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * } else { - vp_comb = "#version 400 core\n#extension GL_ARB_shader_storage_buffer_object : require\n#define SHADER_STORAGE_LIGHTS\n"; + // This differentiation is for Intel which do not seem to expose the full extension, even if marked as required. + if (gl.glslversion < 4.3f) + vp_comb = "#version 400 core\n#extension GL_ARB_shader_storage_buffer_object : require\n#define SHADER_STORAGE_LIGHTS\n"; + else + vp_comb = "#version 430 core\n#define SHADER_STORAGE_LIGHTS\n"; } if (gl.buffermethod == BM_DEFERRED) @@ -105,6 +109,11 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * vp_comb << "#define USE_QUAD_DRAWER\n"; } + if (!!(gl.flags & RFL_SHADER_STORAGE_BUFFER)) + { + vp_comb << "#define SUPPORTS_SHADOWMAPS\n"; + } + vp_comb << defines << i_data.GetString().GetChars(); FString fp_comb = vp_comb; @@ -270,6 +279,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * if (tempindex > 0) glUniform1i(tempindex, i - 1); } + int shadowmapindex = glGetUniformLocation(hShader, "ShadowMap"); + if (shadowmapindex > 0) glUniform1i(shadowmapindex, 16); + glUseProgram(0); return !!linked; } diff --git a/src/gl/shaders/gl_shaderprogram.cpp b/src/gl/shaders/gl_shaderprogram.cpp index 7a4690874c..5327eeac09 100644 --- a/src/gl/shaders/gl_shaderprogram.cpp +++ b/src/gl/shaders/gl_shaderprogram.cpp @@ -26,7 +26,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" diff --git a/src/gl/shaders/gl_shadowmapshader.cpp b/src/gl/shaders/gl_shadowmapshader.cpp new file mode 100644 index 0000000000..674d0a04f2 --- /dev/null +++ b/src/gl/shaders/gl_shadowmapshader.cpp @@ -0,0 +1,45 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Magnus Norddahl +// All rights reserved. +// +// 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 3 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, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.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/shaders/gl_shadowmapshader.h" + +void FShadowMapShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 430); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/shadowmap.fp", "", 430); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/shadowmap"); + mShader.SetAttribLocation(0, "PositionInProjection"); + } + mShader.Bind(); +} diff --git a/src/gl/shaders/gl_shadowmapshader.h b/src/gl/shaders/gl_shadowmapshader.h new file mode 100644 index 0000000000..7d01f9974a --- /dev/null +++ b/src/gl/shaders/gl_shadowmapshader.h @@ -0,0 +1,15 @@ +#ifndef __GL_SHADOWMAPSHADER_H +#define __GL_SHADOWMAPSHADER_H + +#include "gl_shaderprogram.h" + +class FShadowMapShader +{ +public: + void Bind(); + +private: + FShaderProgram mShader; +}; + +#endif \ No newline at end of file diff --git a/src/gl/shaders/gl_texshader.h b/src/gl/shaders/gl_texshader.h index b3e90bf56e..2de45c3c4b 100644 --- a/src/gl/shaders/gl_texshader.h +++ b/src/gl/shaders/gl_texshader.h @@ -5,7 +5,7 @@ #include "tarray.h" #include "zstring.h" -#include "gl/utility/gl_cycler.h" +#include "cycler.h" enum diff --git a/src/gl/shaders/gl_tonemapshader.cpp b/src/gl/shaders/gl_tonemapshader.cpp index 3db6db8ba8..5045618beb 100644 --- a/src/gl/shaders/gl_tonemapshader.cpp +++ b/src/gl/shaders/gl_tonemapshader.cpp @@ -26,7 +26,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "gl/gl_functions.h" diff --git a/src/gl/stereo3d/scoped_view_shifter.cpp b/src/gl/stereo3d/scoped_view_shifter.cpp index b0eb13ef90..ac2b89a278 100644 --- a/src/gl/stereo3d/scoped_view_shifter.cpp +++ b/src/gl/stereo3d/scoped_view_shifter.cpp @@ -34,15 +34,15 @@ namespace s3d { ScopedViewShifter::ScopedViewShifter(float dxyz[3]) // in meters { // save original values - cachedView = ViewPos; + cachedView = r_viewpoint.Pos; // modify values - ViewPos += DVector3(dxyz[0], dxyz[1], dxyz[2]); + r_viewpoint.Pos += DVector3(dxyz[0], dxyz[1], dxyz[2]); } ScopedViewShifter::~ScopedViewShifter() { // restore original values - ViewPos = cachedView; + r_viewpoint.Pos = cachedView; } } \ No newline at end of file diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h index 3b425d2613..b7122b01c3 100644 --- a/src/gl/system/gl_cvars.h +++ b/src/gl/system/gl_cvars.h @@ -26,6 +26,7 @@ EXTERN_CVAR (Bool, gl_attachedlights); EXTERN_CVAR (Bool, gl_lights_checkside); EXTERN_CVAR (Bool, gl_light_sprites); EXTERN_CVAR (Bool, gl_light_particles); +EXTERN_CVAR (Bool, gl_light_shadowmap); EXTERN_CVAR(Int, gl_fogmode) EXTERN_CVAR(Int, gl_lightmode) diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 19c062b095..4aedd176ad 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -27,7 +27,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "m_swap.h" #include "v_video.h" #include "doomstat.h" @@ -79,7 +78,7 @@ CUSTOM_CVAR(Int, vid_hwgamma, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITC //========================================================================== OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) : - Super(hMonitor, width, height, bits, refreshHz, fullscreen) + Super(hMonitor, width, height, bits, refreshHz, fullscreen, false) { // SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver. // If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed! @@ -94,7 +93,6 @@ OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, int width, int height, int memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); UpdatePalette (); ScreenshotBuffer = NULL; - LastCamera = NULL; InitializeState(); mDebug = std::make_shared(); @@ -102,8 +100,6 @@ OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, int width, int height, int gl_SetupMenu(); gl_GenerateGlobalBrightmapFromColormap(); DoSetGamma(); - needsetgamma = true; - swapped = false; Accel2D = true; } @@ -179,7 +175,6 @@ void OpenGLFrameBuffer::Update() GLRenderer->Flush(); Swap(); - swapped = false; Unlock(); CheckBench(); @@ -216,15 +211,9 @@ void OpenGLFrameBuffer::Swap() Finish.Reset(); Finish.Clock(); if (swapbefore) glFinish(); - if (needsetgamma) - { - //DoSetGamma(); - needsetgamma = false; - } SwapBuffers(); if (!swapbefore) glFinish(); Finish.Unclock(); - swapped = true; camtexcount = 0; FHardwareTexture::UnbindAll(); mDebug->Update(); @@ -423,7 +412,7 @@ void OpenGLFrameBuffer::DrawTextureParms(FTexture *img, DrawParms &parms) // // //========================================================================== -void OpenGLFrameBuffer::DrawLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color) +void OpenGLFrameBuffer::DrawLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color) { if (GLRenderer != nullptr && GLRenderer->m2DDrawer != nullptr) GLRenderer->m2DDrawer->AddLine(x1, y1, x2, y2, palcolor, color); @@ -434,7 +423,7 @@ void OpenGLFrameBuffer::DrawLine(int x1, int y1, int x2, int y2, int palcolor, u // // //========================================================================== -void OpenGLFrameBuffer::DrawPixel(int x1, int y1, int palcolor, uint32 color) +void OpenGLFrameBuffer::DrawPixel(int x1, int y1, int palcolor, uint32_t color) { if (GLRenderer != nullptr && GLRenderer->m2DDrawer != nullptr) GLRenderer->m2DDrawer->AddPixel(x1, y1, palcolor, color); @@ -475,7 +464,7 @@ void OpenGLFrameBuffer::FlatFill (int left, int top, int right, int bottom, FTex // // //========================================================================== -void OpenGLFrameBuffer::Clear(int left, int top, int right, int bottom, int palcolor, uint32 color) +void OpenGLFrameBuffer::Clear(int left, int top, int right, int bottom, int palcolor, uint32_t color) { if (GLRenderer != nullptr && GLRenderer->m2DDrawer != nullptr) GLRenderer->m2DDrawer->AddClear(left, top, right, bottom, palcolor, color); @@ -506,7 +495,7 @@ void OpenGLFrameBuffer::FillSimplePoly(FTexture *texture, FVector2 *points, int // //=========================================================================== -void OpenGLFrameBuffer::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type) +void OpenGLFrameBuffer::GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) { const auto &viewport = GLRenderer->mOutputLetterbox; @@ -523,7 +512,7 @@ void OpenGLFrameBuffer::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESS int h = SCREENHEIGHT; ReleaseScreenshotBuffer(); - ScreenshotBuffer = new BYTE[w * h * 3]; + ScreenshotBuffer = new uint8_t[w * h * 3]; float rcpWidth = 1.0f / w; float rcpHeight = 1.0f / h; @@ -566,7 +555,6 @@ void OpenGLFrameBuffer::GameRestart() memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); UpdatePalette (); ScreenshotBuffer = NULL; - LastCamera = NULL; gl_GenerateGlobalBrightmapFromColormap(); } diff --git a/src/gl/system/gl_framebuffer.h b/src/gl/system/gl_framebuffer.h index d6306fcdf8..cdf96775ec 100644 --- a/src/gl/system/gl_framebuffer.h +++ b/src/gl/system/gl_framebuffer.h @@ -55,16 +55,16 @@ public: // 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); + virtual void GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type); // Releases the screenshot buffer. virtual void ReleaseScreenshotBuffer(); // 2D drawing void DrawTextureParms(FTexture *img, 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 Clear(int left, int top, int right, int bottom, int palcolor, uint32 color); + void DrawLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color); + void DrawPixel(int x1, int y1, int palcolor, uint32_t color); + void Clear(int left, int top, int right, int bottom, int palcolor, uint32_t 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); @@ -85,18 +85,13 @@ public: void SetVSync(bool vsync); + int palette_brightness; // brightness of the active palette - this is used for screen blends + bool HWGammaActive = false; // Are we using hardware or software gamma? + std::shared_ptr mDebug; // Debug API private: - PalEntry Flash; - - // Texture creation info - int cm; - int translation; - bool iscomplex; - bool needsetgamma; - bool swapped; - - PalEntry SourcePalette[256]; - BYTE *ScreenshotBuffer; + PalEntry Flash; // Only needed to support some cruft in the interface that only makes sense for the software renderer + PalEntry SourcePalette[256]; // This is where unpaletted textures get their palette from + uint8_t *ScreenshotBuffer; // What the name says. This must be maintained because the software renderer can return a locked canvas surface which the caller cannot release. class Wiper { @@ -120,13 +115,8 @@ private: FHardwareTexture *wipestartscreen; FHardwareTexture *wipeendscreen; - bool HWGammaActive = false; - - std::shared_ptr mDebug; public: - AActor * LastCamera; - int palette_brightness; }; diff --git a/src/gl/system/gl_interface.cpp b/src/gl/system/gl_interface.cpp index 7f98c7f3c5..a0d16597f7 100644 --- a/src/gl/system/gl_interface.cpp +++ b/src/gl/system/gl_interface.cpp @@ -130,8 +130,16 @@ void gl_LoadExtensions() InitContext(); CollectExtensions(); - const char *version = Args->CheckValue("-glversion"); const char *glversion = (const char*)glGetString(GL_VERSION); + gl.es = false; + + if (glversion && strlen(glversion) > 10 && memcmp(glversion, "OpenGL ES ", 10) == 0) + { + glversion += 10; + gl.es = true; + } + + const char *version = Args->CheckValue("-glversion"); if (version == NULL) { @@ -147,90 +155,117 @@ void gl_LoadExtensions() float gl_version = (float)strtod(version, NULL) + 0.01f; - // Don't even start if it's lower than 2.0 or no framebuffers are available (The framebuffer extension is needed for glGenerateMipmapsEXT!) - if ((gl_version < 2.0f || !CheckExtension("GL_EXT_framebuffer_object")) && gl_version < 3.0f) + if (gl.es) { - I_FatalError("Unsupported OpenGL version.\nAt least OpenGL 2.0 with framebuffer support is required to run " GAMENAME ".\n"); - } + if (gl_version < 2.0f) + { + I_FatalError("Unsupported OpenGL ES version.\nAt least OpenGL ES 2.0 is required to run " GAMENAME ".\n"); + } + + const char *glslversion = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION); + if (glslversion && strlen(glslversion) > 18 && memcmp(glslversion, "OpenGL ES GLSL ES ", 10) == 0) + { + glslversion += 18; + } + + // add 0.01 to account for roundoff errors making the number a tad smaller than the actual version + gl.glslversion = strtod(glslversion, NULL) + 0.01f; + gl.vendorstring = (char*)glGetString(GL_VENDOR); - // add 0.01 to account for roundoff errors making the number a tad smaller than the actual version - gl.glslversion = strtod((char*)glGetString(GL_SHADING_LANGUAGE_VERSION), NULL) + 0.01f; - - gl.vendorstring = (char*)glGetString(GL_VENDOR); - - // first test for optional features - 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 ((gl_version >= 3.3f || CheckExtension("GL_ARB_sampler_objects")) && !Args->CheckParm("-nosampler")) - { - gl.flags |= RFL_SAMPLER_OBJECTS; - } - - // The minimum requirement for the modern render path are GL 3.0 + uniform buffers. Also exclude the Linux Mesa driver at GL 3.0 because it errors out on shader compilation. - if (gl_version < 3.0f || (gl_version < 3.1f && (!CheckExtension("GL_ARB_uniform_buffer_object") || strstr(gl.vendorstring, "X.Org") != nullptr))) - { - gl.legacyMode = true; - gl.lightmethod = LM_LEGACY; - gl.buffermethod = BM_LEGACY; - gl.glslversion = 0; - gl.flags |= RFL_NO_CLIP_PLANES; - } - else - { + // Use the slowest/oldest modern path for now gl.legacyMode = false; gl.lightmethod = LM_DEFERRED; gl.buffermethod = BM_DEFERRED; - if (gl_version < 4.f) + } + else + { + // Don't even start if it's lower than 2.0 or no framebuffers are available (The framebuffer extension is needed for glGenerateMipmapsEXT!) + if ((gl_version < 2.0f || !CheckExtension("GL_EXT_framebuffer_object")) && gl_version < 3.0f) { -#ifdef _WIN32 - if (strstr(gl.vendorstring, "ATI Tech")) - { - gl.flags |= RFL_NO_CLIP_PLANES; // gl_ClipDistance is horribly broken on ATI GL3 drivers for Windows. - } -#endif + I_FatalError("Unsupported OpenGL version.\nAt least OpenGL 2.0 with framebuffer support is required to run " GAMENAME ".\n"); } - else if (gl_version < 4.5f) + + gl.es = false; + + // add 0.01 to account for roundoff errors making the number a tad smaller than the actual version + gl.glslversion = strtod((char*)glGetString(GL_SHADING_LANGUAGE_VERSION), NULL) + 0.01f; + + gl.vendorstring = (char*)glGetString(GL_VENDOR); + + // first test for optional features + 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 ((gl_version >= 3.3f || CheckExtension("GL_ARB_sampler_objects")) && !Args->CheckParm("-nosampler")) { - // don't use GL 4.x features when running a GL 3.x context. - 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; - gl.lightmethod = LM_DIRECT; - gl.buffermethod = BM_PERSISTENT; - } + gl.flags |= RFL_SAMPLER_OBJECTS; + } + + // The minimum requirement for the modern render path are GL 3.0 + uniform buffers. Also exclude the Linux Mesa driver at GL 3.0 because it errors out on shader compilation. + if (gl_version < 3.0f || (gl_version < 3.1f && (!CheckExtension("GL_ARB_uniform_buffer_object") || strstr(gl.vendorstring, "X.Org") != nullptr))) + { + gl.legacyMode = true; + gl.lightmethod = LM_LEGACY; + gl.buffermethod = BM_LEGACY; + gl.glslversion = 0; + gl.flags |= RFL_NO_CLIP_PLANES; } else { - // Assume that everything works without problems on GL 4.5 drivers where these things are core features. - gl.flags |= RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE; - gl.lightmethod = LM_DIRECT; - gl.buffermethod = BM_PERSISTENT; - } + gl.legacyMode = false; + gl.lightmethod = LM_DEFERRED; + gl.buffermethod = BM_DEFERRED; + if (gl_version < 4.f) + { +#ifdef _WIN32 + if (strstr(gl.vendorstring, "ATI Tech")) + { + gl.flags |= RFL_NO_CLIP_PLANES; // gl_ClipDistance is horribly broken on ATI GL3 drivers for Windows. + } +#endif + } + else if (gl_version < 4.5f) + { + // don't use GL 4.x features when running a GL 3.x context. + 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")) + { + // Intel's GLSL compiler is a bit broken with extensions, so unlock the feature only if not on Intel or having GL 4.3. + if (strstr(gl.vendorstring, "Intel") == NULL || gl_version >= 4.3f) + { + gl.flags |= RFL_SHADER_STORAGE_BUFFER; + } + } + gl.flags |= RFL_BUFFER_STORAGE; + gl.lightmethod = LM_DIRECT; + gl.buffermethod = BM_PERSISTENT; + } + } + else + { + // Assume that everything works without problems on GL 4.5 drivers where these things are core features. + gl.flags |= RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE; + gl.lightmethod = LM_DIRECT; + gl.buffermethod = BM_PERSISTENT; + } - if (gl_version >= 4.3f || CheckExtension("GL_ARB_invalidate_subdata")) gl.flags |= RFL_INVALIDATE_BUFFER; - if (gl_version >= 4.3f || CheckExtension("GL_KHR_debug")) gl.flags |= RFL_DEBUG; + if (gl_version >= 4.3f || CheckExtension("GL_ARB_invalidate_subdata")) gl.flags |= RFL_INVALIDATE_BUFFER; + if (gl_version >= 4.3f || CheckExtension("GL_KHR_debug")) gl.flags |= RFL_DEBUG; - const char *lm = Args->CheckValue("-lightmethod"); - if (lm != NULL) - { - if (!stricmp(lm, "deferred") && gl.lightmethod == LM_DIRECT) gl.lightmethod = LM_DEFERRED; - } + const char *lm = Args->CheckValue("-lightmethod"); + if (lm != NULL) + { + if (!stricmp(lm, "deferred") && gl.lightmethod == LM_DIRECT) gl.lightmethod = LM_DEFERRED; + } - lm = Args->CheckValue("-buffermethod"); - if (lm != NULL) - { - if (!stricmp(lm, "deferred") && gl.buffermethod == BM_PERSISTENT) gl.buffermethod = BM_DEFERRED; + lm = Args->CheckValue("-buffermethod"); + if (lm != NULL) + { + if (!stricmp(lm, "deferred") && gl.buffermethod == BM_PERSISTENT) gl.buffermethod = BM_DEFERRED; + } } } diff --git a/src/gl/system/gl_interface.h b/src/gl/system/gl_interface.h index edaec58c05..a839e13b46 100644 --- a/src/gl/system/gl_interface.h +++ b/src/gl/system/gl_interface.h @@ -66,6 +66,7 @@ struct RenderContext int max_texturesize; char * vendorstring; bool legacyMode; + bool es; int MaxLights() const { diff --git a/src/gl/system/gl_load.c b/src/gl/system/gl_load.c index 3e494f95a6..7075d69698 100644 --- a/src/gl/system/gl_load.c +++ b/src/gl/system/gl_load.c @@ -42,8 +42,12 @@ static void* PosixGetProcAddress (const GLubyte* name) #if defined(_WIN32) +#ifdef APIENTRY +#undef APIENTRY +#endif #include + #ifdef _MSC_VER // disable inlining here because it creates an incredible amount of bloat in this file. #pragma inline_depth(0) diff --git a/src/gl/system/gl_swframebuffer.cpp b/src/gl/system/gl_swframebuffer.cpp new file mode 100644 index 0000000000..1b7c6d43fc --- /dev/null +++ b/src/gl/system/gl_swframebuffer.cpp @@ -0,0 +1,3742 @@ +/* +** gl_swframebuffer.cpp +** Code to let ZDoom use OpenGL as a simple framebuffer +** +**--------------------------------------------------------------------------- +** Copyright 1998-2011 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +** This file does _not_ implement hardware-acclerated 3D rendering. It is +** just a means of getting the pixel data to the screen in a more reliable +** method on modern hardware by copying the entire frame to a texture, +** drawing that to the screen, and presenting. +** +** That said, it does implement hardware-accelerated 2D rendering. +*/ + +#include "gl/system/gl_system.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 "c_dispatch.h" +#include "templates.h" +#include "i_system.h" +#include "i_video.h" +#include "v_pfx.h" +#include "stats.h" +#include "doomerrors.h" +#include "r_data/r_translate.h" +#include "f_wipe.h" +#include "sbar.h" +#include "w_wad.h" +#include "r_data/colormaps.h" + +#include "gl/system/gl_interface.h" +#include "gl/system/gl_swframebuffer.h" +#include "gl/data/gl_data.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/gl_functions.h" +#include "gl_debug.h" + +#include "swrenderer/scene/r_light.h" + +CVAR(Int, gl_showpacks, 0, 0) +#ifndef WIN32 // Defined in fb_d3d9 for Windows +CVAR(Bool, vid_hwaalines, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Bool, vid_hw2d, true, CVAR_NOINITCALL) +{ + V_SetBorderNeedRefresh(); + ST_SetNeedRefresh(); +} +#else +EXTERN_CVAR(Bool, vid_hwaalines) +EXTERN_CVAR(Bool, vid_hw2d) +#endif + +EXTERN_CVAR(Bool, fullscreen) +EXTERN_CVAR(Float, Gamma) +EXTERN_CVAR(Bool, vid_vsync) +EXTERN_CVAR(Float, transsouls) +EXTERN_CVAR(Int, vid_refreshrate) + +CVAR(Int, vid_max_width, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, vid_max_height, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +namespace +{ + int ClampWidth(int width) { return (vid_max_width == 0 || width < vid_max_width) ? width : vid_max_width; } + int ClampHeight(int height) { return (vid_max_height == 0 || height < vid_max_height) ? height : vid_max_height; } +} + +#ifdef WIN32 +extern cycle_t BlitCycles; +#endif + +void gl_LoadExtensions(); +void gl_PrintStartupLog(); + +#ifndef WIN32 +// This has to be in this file because system headers conflict Doom headers +DFrameBuffer *CreateGLSWFrameBuffer(int width, int height, bool bgra, bool fullscreen) +{ + return new OpenGLSWFrameBuffer(NULL, width, height, 32, 60, fullscreen, bgra); +} +#endif + +IMPLEMENT_CLASS(OpenGLSWFrameBuffer, false, false) + +const char *const OpenGLSWFrameBuffer::ShaderDefines[OpenGLSWFrameBuffer::NUM_SHADERS] = +{ + "#define ENORMALCOLOR", // NormalColor + "#define ENORMALCOLOR\n#define PALTEX", // NormalColorPal + "#define ENORMALCOLOR\n#define INVERT", // NormalColorInv + "#define ENORMALCOLOR\n#define PALTEX\n#define INVERT", // NormalColorPalInv + + "#define EREDTOALPHA", // RedToAlpha + "#define EREDTOALPHA\n#define INVERT", // RedToAlphaInv + + "#define EVERTEXCOLOR", // VertexColor + + "#define ESPECIALCOLORMAP\n", // SpecialColormap + "#define ESPECIALCOLORMAP\n#define PALTEX", // SpecialColorMapPal + + "#define EINGAMECOLORMAP", // InGameColormap + "#define EINGAMECOLORMAP\n#define DESAT", // InGameColormapDesat + "#define EINGAMECOLORMAP\n#define INVERT", // InGameColormapInv + "#define EINGAMECOLORMAP\n#define INVERT\n#define DESAT", // InGameColormapInvDesat + "#define EINGAMECOLORMAP\n#define PALTEX\n", // InGameColormapPal + "#define EINGAMECOLORMAP\n#define PALTEX\n#define DESAT", // InGameColormapPalDesat + "#define EINGAMECOLORMAP\n#define PALTEX\n#define INVERT", // InGameColormapPalInv + "#define EINGAMECOLORMAP\n#define PALTEX\n#define INVERT\n#define DESAT", // InGameColormapPalInvDesat + + "#define EBURNWIPE", // BurnWipe + "#define EGAMMACORRECTION", // GammaCorrection +}; + +OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen, bool bgra) : + Super(hMonitor, ClampWidth(width), ClampHeight(height), bits, refreshHz, fullscreen, bgra) +{ + VertexBuffer = nullptr; + IndexBuffer = nullptr; + FBTexture = nullptr; + InitialWipeScreen = nullptr; + ScreenshotTexture = nullptr; + FinalWipeScreen = nullptr; + PaletteTexture = nullptr; + for (int i = 0; i < NUM_SHADERS; ++i) + { + Shaders[i] = nullptr; + } + + BlendingRect.left = 0; + BlendingRect.top = 0; + BlendingRect.right = Width; + BlendingRect.bottom = Height; + In2D = 0; + Palettes = nullptr; + Textures = nullptr; + Accel2D = true; + GatheringWipeScreen = false; + ScreenWipe = nullptr; + InScene = false; + QuadExtra = new BufferedTris[MAX_QUAD_BATCH]; + memset(QuadExtra, 0, sizeof(BufferedTris) * MAX_QUAD_BATCH); + Atlases = nullptr; + PixelDoubling = 0; + + Gamma = 1.0; + FlashColor0 = 0; + FlashColor1 = 0xFFFFFFFF; + FlashColor = 0; + FlashAmount = 0; + + NeedGammaUpdate = false; + NeedPalUpdate = false; + + if (MemBuffer == nullptr) + { + return; + } + + memcpy(SourcePalette, GPalette.BaseColors, sizeof(PalEntry) * 256); + + // To do: this needs to cooperate with the same static in OpenGLFrameBuffer::InitializeState + static bool first = true; + if (first) + { + ogl_LoadFunctions(); + } + gl_LoadExtensions(); + InitializeState(); + if (first) + { + gl_PrintStartupLog(); + first = false; + } + + if (!glGetString) + return; + + // SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver. + // If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed! + Super::SetVSync(vid_vsync); + + Debug = std::make_shared(); + Debug->Update(); + + //Windowed = !(static_cast(Video)->GoFullscreen(fullscreen)); + + TrueHeight = height; + + Valid = CreateResources(); + if (Valid) + SetInitialState(); +} + +OpenGLSWFrameBuffer::~OpenGLSWFrameBuffer() +{ + ReleaseResources(); + delete[] QuadExtra; +} + +void *OpenGLSWFrameBuffer::MapBuffer(int target, int size) +{ + if (glMapBufferRange) + { + return (FBVERTEX*)glMapBufferRange(target, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); + } + else + { + glBufferData(target, size, nullptr, GL_STREAM_DRAW); + return glMapBuffer(target, GL_WRITE_ONLY); + } +} + +OpenGLSWFrameBuffer::HWFrameBuffer::~HWFrameBuffer() +{ + if (Framebuffer != 0) glDeleteFramebuffers(1, (GLuint*)&Framebuffer); + delete Texture; +} + +OpenGLSWFrameBuffer::HWTexture::~HWTexture() +{ + if (Texture != 0) glDeleteTextures(1, (GLuint*)&Texture); + if (Buffers[0] != 0) glDeleteBuffers(2, (GLuint*)Buffers); +} + +OpenGLSWFrameBuffer::HWVertexBuffer::~HWVertexBuffer() +{ + if (VertexArray != 0) glDeleteVertexArrays(1, (GLuint*)&VertexArray); + if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); +} + +OpenGLSWFrameBuffer::FBVERTEX *OpenGLSWFrameBuffer::HWVertexBuffer::Lock() +{ + glBindBuffer(GL_ARRAY_BUFFER, Buffer); + return (FBVERTEX*)MapBuffer(GL_ARRAY_BUFFER, Size); +} + +void OpenGLSWFrameBuffer::HWVertexBuffer::Unlock() +{ + glUnmapBuffer(GL_ARRAY_BUFFER); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +OpenGLSWFrameBuffer::HWIndexBuffer::~HWIndexBuffer() +{ + if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); +} + +uint16_t *OpenGLSWFrameBuffer::HWIndexBuffer::Lock() +{ + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &LockedOldBinding); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Buffer); + return (uint16_t*)MapBuffer(GL_ELEMENT_ARRAY_BUFFER, Size); +} + +void OpenGLSWFrameBuffer::HWIndexBuffer::Unlock() +{ + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, LockedOldBinding); +} + +OpenGLSWFrameBuffer::HWPixelShader::~HWPixelShader() +{ + if (Program != 0) glDeleteProgram(Program); + if (VertexShader != 0) glDeleteShader(VertexShader); + if (FragmentShader != 0) glDeleteShader(FragmentShader); +} + +bool OpenGLSWFrameBuffer::CreateFrameBuffer(const FString &name, int width, int height, HWFrameBuffer **outFramebuffer) +{ + auto fb = std::make_unique(); + + GLint format = GL_RGBA16F; + if (gl.es) format = GL_RGB; + + if (!CreateTexture(name, width, height, 1, format, &fb->Texture)) + { + outFramebuffer = nullptr; + return false; + } + + glGenFramebuffers(1, (GLuint*)&fb->Framebuffer); + + GLint oldFramebufferBinding = 0, oldTextureBinding = 0; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFramebufferBinding); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureBinding); + + glBindFramebuffer(GL_FRAMEBUFFER, fb->Framebuffer); + FGLDebug::LabelObject(GL_FRAMEBUFFER, fb->Framebuffer, name); + + glBindTexture(GL_TEXTURE_2D, fb->Texture->Texture); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->Texture->Texture, 0); + + GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + glBindFramebuffer(GL_FRAMEBUFFER, oldFramebufferBinding); + glBindTexture(GL_TEXTURE_2D, oldTextureBinding); + + if (result != GL_FRAMEBUFFER_COMPLETE) + { + Printf("Framebuffer is not complete\n"); + outFramebuffer = nullptr; + return false; + } + + *outFramebuffer = fb.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreatePixelShader(FString vertexsrc, FString fragmentsrc, const FString &defines, HWPixelShader **outShader) +{ + auto shader = std::make_unique(); + + shader->Program = glCreateProgram(); + shader->VertexShader = glCreateShader(GL_VERTEX_SHADER); + shader->FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + int maxGlslVersion = 330; + int shaderVersion = MIN((int)round(gl.glslversion * 10) * 10, maxGlslVersion); + + FString prefix; + prefix.AppendFormat("#version %d\n%s\n#line 0\n", shaderVersion, defines.GetChars()); + //Printf("Shader prefix: %s", prefix.GetChars()); + + vertexsrc = prefix + vertexsrc; + fragmentsrc = prefix + fragmentsrc; + + { + int lengths[1] = { (int)vertexsrc.Len() }; + const char *sources[1] = { vertexsrc.GetChars() }; + glShaderSource(shader->VertexShader, 1, sources, lengths); + glCompileShader(shader->VertexShader); + } + + { + int lengths[1] = { (int)fragmentsrc.Len() }; + const char *sources[1] = { fragmentsrc.GetChars() }; + glShaderSource(shader->FragmentShader, 1, sources, lengths); + glCompileShader(shader->FragmentShader); + } + + GLint status = 0; + int errorShader = shader->VertexShader; + glGetShaderiv(shader->VertexShader, GL_COMPILE_STATUS, &status); + if (status != GL_FALSE) { errorShader = shader->FragmentShader; glGetShaderiv(shader->FragmentShader, GL_COMPILE_STATUS, &status); } + if (status == GL_FALSE) + { + static char buffer[10000]; + GLsizei length = 0; + buffer[0] = 0; + glGetShaderInfoLog(errorShader, 10000, &length, buffer); + //Printf("Shader compile failed: %s", buffer); + + *outShader = nullptr; + return false; + } + + glAttachShader(shader->Program, shader->VertexShader); + glAttachShader(shader->Program, shader->FragmentShader); + glBindFragDataLocation(shader->Program, 0, "FragColor"); + glBindAttribLocation(shader->Program, 0, "AttrPosition"); + glBindAttribLocation(shader->Program, 1, "AttrColor0"); + glBindAttribLocation(shader->Program, 2, "AttrColor1"); + glBindAttribLocation(shader->Program, 3, "AttrTexCoord0"); + glLinkProgram(shader->Program); + glGetProgramiv(shader->Program, GL_LINK_STATUS, &status); + if (status == GL_FALSE) + { + static char buffer[10000]; + GLsizei length = 0; + buffer[0] = 0; + glGetProgramInfoLog(shader->Program, 10000, &length, buffer); + //Printf("Shader link failed: %s", buffer); + + *outShader = nullptr; + return false; + } + + shader->ConstantLocations[PSCONST_Desaturation] = glGetUniformLocation(shader->Program, "Desaturation"); + shader->ConstantLocations[PSCONST_PaletteMod] = glGetUniformLocation(shader->Program, "PaletteMod"); + shader->ConstantLocations[PSCONST_Weights] = glGetUniformLocation(shader->Program, "Weights"); + shader->ConstantLocations[PSCONST_Gamma] = glGetUniformLocation(shader->Program, "Gamma"); + shader->ConstantLocations[PSCONST_ScreenSize] = glGetUniformLocation(shader->Program, "ScreenSize"); + shader->ImageLocation = glGetUniformLocation(shader->Program, "Image"); + shader->PaletteLocation = glGetUniformLocation(shader->Program, "Palette"); + shader->NewScreenLocation = glGetUniformLocation(shader->Program, "NewScreen"); + shader->BurnLocation = glGetUniformLocation(shader->Program, "Burn"); + + *outShader = shader.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer) +{ + auto obj = std::make_unique(); + + obj->Size = size; + + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + glGenVertexArrays(1, (GLuint*)&obj->VertexArray); + glGenBuffers(1, (GLuint*)&obj->Buffer); + glBindVertexArray(obj->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, obj->Buffer); + FGLDebug::LabelObject(GL_BUFFER, obj->Buffer, "VertexBuffer"); + glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, x)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color0)); + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color1)); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, tu)); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); + + *outVertexBuffer = obj.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer) +{ + auto obj = std::make_unique(); + + obj->Size = size; + + GLint oldBinding = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &oldBinding); + + glGenBuffers(1, (GLuint*)&obj->Buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, obj->Buffer); + FGLDebug::LabelObject(GL_BUFFER, obj->Buffer, "IndexBuffer"); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oldBinding); + + *outIndexBuffer = obj.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateTexture(const FString &name, int width, int height, int levels, int format, HWTexture **outTexture) +{ + auto obj = std::make_unique(); + + obj->Format = format; + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + + glGenTextures(1, (GLuint*)&obj->Texture); + glBindTexture(GL_TEXTURE_2D, obj->Texture); + GLenum srcformat; + switch (format) + { + case GL_RGB: srcformat = GL_RGB; break; + case GL_R8: srcformat = GL_RED; break; + case GL_RGBA8: srcformat = gl.es ? GL_RGBA : GL_BGRA; break; + case GL_RGBA16F: srcformat = GL_RGBA; break; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: srcformat = GL_RGB; break; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: srcformat = GL_RGBA; break; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: srcformat = GL_RGBA; break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: srcformat = GL_RGBA; break; + default: + I_FatalError("Unknown format passed to CreateTexture"); + return false; + } + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, srcformat, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + FGLDebug::LabelObject(GL_TEXTURE, obj->Texture, name); + + glBindTexture(GL_TEXTURE_2D, oldBinding); + + *outTexture = obj.release(); + return true; +} + +OpenGLSWFrameBuffer::HWTexture *OpenGLSWFrameBuffer::CopyCurrentScreen() +{ + auto obj = std::make_unique(); + obj->Format = GL_RGBA16F; + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + + glGenTextures(1, (GLuint*)&obj->Texture); + glBindTexture(GL_TEXTURE_2D, obj->Texture); + + glCopyTexImage2D(GL_TEXTURE_2D, 0, obj->Format, 0, 0, Width, Height, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + FGLDebug::LabelObject(GL_TEXTURE, obj->Texture, "CopyCurrentScreen"); + + glBindTexture(GL_TEXTURE_2D, oldBinding); + + return obj.release(); +} + +void OpenGLSWFrameBuffer::SetGammaRamp(const GammaRamp *ramp) +{ +} + +void OpenGLSWFrameBuffer::SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount) +{ + assert(uniformIndex < NumPSCONST && vec4fcount == 1); // This emulation of d3d9 only works for very simple stuff + for (int i = 0; i < 4; i++) + ShaderConstants[uniformIndex * 4 + i] = data[i]; + if (CurrentShader && CurrentShader->ConstantLocations[uniformIndex] != -1) + glUniform4fv(CurrentShader->ConstantLocations[uniformIndex], vec4fcount, data); +} + +void OpenGLSWFrameBuffer::SetHWPixelShader(HWPixelShader *shader) +{ + if (shader != CurrentShader) + { + if (shader) + { + glUseProgram(shader->Program); + for (int i = 0; i < NumPSCONST; i++) + { + if (shader->ConstantLocations[i] != -1) + glUniform4fv(shader->ConstantLocations[i], 1, &ShaderConstants[i * 4]); + } + } + else + { + glUseProgram(0); + } + } + CurrentShader = shader; +} + +void OpenGLSWFrameBuffer::SetStreamSource(HWVertexBuffer *vertexBuffer) +{ + if (vertexBuffer) + glBindVertexArray(vertexBuffer->VertexArray); + else + glBindVertexArray(0); +} + +void OpenGLSWFrameBuffer::SetIndices(HWIndexBuffer *indexBuffer) +{ + if (indexBuffer) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer->Buffer); + else + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +void OpenGLSWFrameBuffer::DrawTriangleFans(int count, const FBVERTEX *vertices) +{ + count = 2 + count; + + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + if (!StreamVertexBuffer) + { + StreamVertexBuffer = std::make_unique(); + glGenVertexArrays(1, (GLuint*)&StreamVertexBuffer->VertexArray); + glGenBuffers(1, (GLuint*)&StreamVertexBuffer->Buffer); + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, x)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color0)); + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color1)); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, tu)); + } + else + { + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + } + + glDrawArrays(GL_TRIANGLE_FAN, 0, count); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); +} + +void OpenGLSWFrameBuffer::DrawTriangleFans(int count, const BURNVERTEX *vertices) +{ + count = 2 + count; + + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + if (!StreamVertexBufferBurn) + { + StreamVertexBufferBurn = std::make_unique(); + glGenVertexArrays(1, (GLuint*)&StreamVertexBufferBurn->VertexArray); + glGenBuffers(1, (GLuint*)&StreamVertexBufferBurn->Buffer); + glBindVertexArray(StreamVertexBufferBurn->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBufferBurn->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(BURNVERTEX), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(BURNVERTEX), (const GLvoid*)offsetof(BURNVERTEX, x)); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(BURNVERTEX), (const GLvoid*)offsetof(BURNVERTEX, tu0)); + } + else + { + glBindVertexArray(StreamVertexBufferBurn->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBufferBurn->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(BURNVERTEX), vertices, GL_STREAM_DRAW); + } + + glDrawArrays(GL_TRIANGLE_FAN, 0, count); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); +} + +void OpenGLSWFrameBuffer::DrawPoints(int count, const FBVERTEX *vertices) +{ + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + if (!StreamVertexBuffer) + { + StreamVertexBuffer = std::make_unique(); + glGenVertexArrays(1, (GLuint*)&StreamVertexBuffer->VertexArray); + glGenBuffers(1, (GLuint*)&StreamVertexBuffer->Buffer); + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, x)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color0)); + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color1)); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, tu)); + } + else + { + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + } + + glDrawArrays(GL_POINTS, 0, count); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); +} + +void OpenGLSWFrameBuffer::DrawLineList(int count) +{ + glDrawArrays(GL_LINES, 0, count * 2); +} + +void OpenGLSWFrameBuffer::DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount) +{ + glDrawRangeElements(GL_TRIANGLES, minIndex, minIndex + numVertices - 1, primitiveCount * 3, GL_UNSIGNED_SHORT, (const void*)(startIndex * sizeof(uint16_t))); +} + +void OpenGLSWFrameBuffer::Present() +{ + int clientWidth = GetClientWidth(); + int clientHeight = GetClientHeight(); + if (clientWidth > 0 && clientHeight > 0) + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, clientWidth, clientHeight); + + float scale = MIN(clientWidth / (float)Width, clientHeight / (float)Height); + int letterboxWidth = (int)round(Width * scale); + int letterboxHeight = (int)round(Height * scale); + int letterboxX = (clientWidth - letterboxWidth) / 2; + int letterboxY = (clientHeight - letterboxHeight) / 2; + + DrawLetterbox(letterboxX, letterboxY, letterboxWidth, letterboxHeight); + glViewport(letterboxX, letterboxY, letterboxWidth, letterboxHeight); + + FBVERTEX verts[4]; + CalcFullscreenCoords(verts, false, 0, 0xFFFFFFFF); + SetTexture(0, OutputFB->Texture); + SetPixelShader(Shaders[SHADER_GammaCorrection]); + SetAlphaBlend(0); + EnableAlphaTest(false); + DrawTriangleFans(2, verts); + } + + SwapBuffers(); + Debug->Update(); + + float screensize[4] = { (float)Width, (float)Height, 1.0f, 1.0f }; + SetPixelShaderConstantF(PSCONST_ScreenSize, screensize, 1); + + glBindFramebuffer(GL_FRAMEBUFFER, OutputFB->Framebuffer); + glViewport(0, 0, Width, Height); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: SetInitialState +// +// Called after initial device creation and reset, when everything is set +// to OpenGL's defaults. +// +//========================================================================== + +void OpenGLSWFrameBuffer::SetInitialState() +{ + if (gl.es) UseMappedMemBuffer = false; + + AlphaBlendEnabled = false; + AlphaBlendOp = GL_FUNC_ADD; + AlphaSrcBlend = 0; + AlphaDestBlend = 0; + + CurPixelShader = nullptr; + memset(Constant, 0, sizeof(Constant)); + + for (unsigned i = 0; i < countof(Texture); ++i) + { + Texture[i] = nullptr; + SamplerWrapS[i] = GL_CLAMP_TO_EDGE; + SamplerWrapT[i] = GL_CLAMP_TO_EDGE; + } + + NeedGammaUpdate = true; + NeedPalUpdate = true; + + // This constant is used for grayscaling weights (.xyz) and color inversion (.w) + float weights[4] = { 77 / 256.f, 143 / 256.f, 37 / 256.f, 1 }; + SetPixelShaderConstantF(PSCONST_Weights, weights, 1); + + float screensize[4] = { (float)Width, (float)Height, 1.0f, 1.0f }; + SetPixelShaderConstantF(PSCONST_ScreenSize, screensize, 1); + + AlphaTestEnabled = false; + + CurBorderColor = 0; + + // Clear to black, just in case it wasn't done already. + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateResources +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreateResources() +{ + Atlases = nullptr; + if (!LoadShaders()) + return false; + + if (!CreateFrameBuffer("OutputFB", Width, Height, &OutputFB)) + return false; + + glBindFramebuffer(GL_FRAMEBUFFER, OutputFB->Framebuffer); + + if (!CreateFBTexture() || + !CreatePaletteTexture()) + { + return false; + } + if (!CreateVertexes()) + { + return false; + } + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: LoadShaders +// +// Returns true if all required shaders were loaded. (Gamma and burn wipe +// are the only ones not considered "required".) +// +//========================================================================== + +bool OpenGLSWFrameBuffer::LoadShaders() +{ + int lumpvert = Wads.CheckNumForFullName("shaders/glsl/swshader.vp"); + int lumpfrag = Wads.CheckNumForFullName("shaders/glsl/swshader.fp"); + if (lumpvert < 0 || lumpfrag < 0) + return false; + + FString vertsource = Wads.ReadLump(lumpvert).GetString(); + FString fragsource = Wads.ReadLump(lumpfrag).GetString(); + + FString shaderdir, shaderpath; + unsigned int i; + + for (i = 0; i < NUM_SHADERS; ++i) + { + shaderpath = shaderdir; + if (!CreatePixelShader(vertsource, fragsource, ShaderDefines[i], &Shaders[i]) && i < SHADER_BurnWipe) + { + break; + } + + glUseProgram(Shaders[i]->Program); + if (Shaders[i]->ImageLocation != -1) glUniform1i(Shaders[i]->ImageLocation, 0); + if (Shaders[i]->PaletteLocation != -1) glUniform1i(Shaders[i]->PaletteLocation, 1); + if (Shaders[i]->NewScreenLocation != -1) glUniform1i(Shaders[i]->NewScreenLocation, 0); + if (Shaders[i]->BurnLocation != -1) glUniform1i(Shaders[i]->BurnLocation, 1); + glUseProgram(0); + } + if (i == NUM_SHADERS) + { // Success! + return true; + } + // Failure. Release whatever managed to load (which is probably nothing.) + for (i = 0; i < NUM_SHADERS; ++i) + { + SafeRelease(Shaders[i]); + } + return false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: ReleaseResources +// +//========================================================================== + +void OpenGLSWFrameBuffer::ReleaseResources() +{ +#ifdef WIN32 + I_SaveWindowedPos(); +#endif + KillNativeTexs(); + KillNativePals(); + ReleaseDefaultPoolItems(); + SafeRelease(ScreenshotTexture); + SafeRelease(PaletteTexture); + for (int i = 0; i < NUM_SHADERS; ++i) + { + SafeRelease(Shaders[i]); + } + if (ScreenWipe != nullptr) + { + delete ScreenWipe; + ScreenWipe = nullptr; + } + Atlas *pack, *next; + for (pack = Atlases; pack != nullptr; pack = next) + { + next = pack->Next; + delete pack; + } + GatheringWipeScreen = false; +} + +void OpenGLSWFrameBuffer::ReleaseDefaultPoolItems() +{ + SafeRelease(FBTexture); + SafeRelease(FinalWipeScreen); + SafeRelease(InitialWipeScreen); + SafeRelease(VertexBuffer); + SafeRelease(IndexBuffer); + SafeRelease(OutputFB); +} + +bool OpenGLSWFrameBuffer::Reset() +{ + ReleaseDefaultPoolItems(); + + if (!CreateFrameBuffer("OutputFB", Width, Height, &OutputFB) || !CreateFBTexture() || !CreateVertexes()) + { + return false; + } + + glBindFramebuffer(GL_FRAMEBUFFER, OutputFB->Framebuffer); + glViewport(0, 0, Width, Height); + + SetInitialState(); + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: KillNativePals +// +// Frees all native palettes. +// +//========================================================================== + +void OpenGLSWFrameBuffer::KillNativePals() +{ + while (Palettes != nullptr) + { + delete Palettes; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: KillNativeTexs +// +// Frees all native textures. +// +//========================================================================== + +void OpenGLSWFrameBuffer::KillNativeTexs() +{ + while (Textures != nullptr) + { + delete Textures; + } +} + +bool OpenGLSWFrameBuffer::CreateFBTexture() +{ + return CreateTexture("FBTexture", Width, Height, 1, IsBgra() ? GL_RGBA8 : GL_R8, &FBTexture); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreatePaletteTexture +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreatePaletteTexture() +{ + return CreateTexture("PaletteTexture", 256, 1, 1, GL_RGBA8, &PaletteTexture); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateVertexes +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreateVertexes() +{ + VertexPos = -1; + IndexPos = -1; + QuadBatchPos = -1; + BatchType = BATCH_None; + if (!CreateVertexBuffer(sizeof(FBVERTEX)*NUM_VERTS, &VertexBuffer)) + { + return false; + } + if (!CreateIndexBuffer(sizeof(uint16_t)*NUM_INDEXES, &IndexBuffer)) + { + return false; + } + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CalcFullscreenCoords +// +//========================================================================== + +void OpenGLSWFrameBuffer::CalcFullscreenCoords(FBVERTEX verts[4], bool viewarea_only, uint32_t color0, uint32_t color1) const +{ + float mxl, mxr, myt, myb, tmxl, tmxr, tmyt, tmyb; + + if (viewarea_only) + { // Just calculate vertices for the viewarea/BlendingRect + mxl = float(BlendingRect.left); + mxr = float(BlendingRect.right); + myt = float(BlendingRect.top); + myb = float(BlendingRect.bottom); + tmxl = float(BlendingRect.left) / float(Width); + tmxr = float(BlendingRect.right) / float(Width); + tmyt = float(BlendingRect.top) / float(Height); + tmyb = float(BlendingRect.bottom) / float(Height); + } + else + { // Calculate vertices for the whole screen + mxl = 0.0f; + mxr = float(Width); + myt = 0.0f; + myb = float(Height); + tmxl = 0; + tmxr = 1.0f; + tmyt = 0; + tmyb = 1.0f; + } + + //{ mxl, myt, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyt }, + //{ mxr, myt, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyt }, + //{ mxr, myb, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyb }, + //{ mxl, myb, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyb }, + + verts[0].x = mxl; + verts[0].y = myt; + verts[0].z = 0; + verts[0].rhw = 1; + verts[0].color0 = color0; + verts[0].color1 = color1; + verts[0].tu = tmxl; + verts[0].tv = tmyt; + + verts[1].x = mxr; + verts[1].y = myt; + verts[1].z = 0; + verts[1].rhw = 1; + verts[1].color0 = color0; + verts[1].color1 = color1; + verts[1].tu = tmxr; + verts[1].tv = tmyt; + + verts[2].x = mxr; + verts[2].y = myb; + verts[2].z = 0; + verts[2].rhw = 1; + verts[2].color0 = color0; + verts[2].color1 = color1; + verts[2].tu = tmxr; + verts[2].tv = tmyb; + + verts[3].x = mxl; + verts[3].y = myb; + verts[3].z = 0; + verts[3].rhw = 1; + verts[3].color0 = color0; + verts[3].color1 = color1; + verts[3].tu = tmxl; + verts[3].tv = tmyb; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: GetPageCount +// +//========================================================================== + +int OpenGLSWFrameBuffer::GetPageCount() +{ + return 2; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: IsValid +// +//========================================================================== + +bool OpenGLSWFrameBuffer::IsValid() +{ + return Valid; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Lock +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Lock(bool buffered) +{ + if (LockCount++ > 0) + { + return false; + } + assert(!In2D); + Accel2D = vid_hw2d; + if (UseMappedMemBuffer) + { + if (!MappedMemBuffer) + { + BindFBBuffer(); + + MappedMemBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE); + Pitch = Width; + if (MappedMemBuffer == nullptr) + return true; + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + Buffer = (uint8_t*)MappedMemBuffer; + } + else + { + Buffer = MemBuffer; + } + return false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Unlock +// +//========================================================================== + +void OpenGLSWFrameBuffer::Unlock() +{ + if (LockCount == 0) + { + return; + } + + if (UpdatePending && LockCount == 1) + { + Update(); + } + else if (--LockCount == 0) + { + Buffer = nullptr; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Update +// +// When In2D == 0: Copy buffer to screen and present +// When In2D == 1: Copy buffer to screen but do not present +// When In2D == 2: Set up for 2D drawing but do not draw anything +// When In2D == 3: Present and set In2D to 0 +// +//========================================================================== + +void OpenGLSWFrameBuffer::Update() +{ + if (In2D == 3) + { + if (InScene) + { + DrawRateStuff(); + DrawPackedTextures(gl_showpacks); + EndBatch(); // Make sure all batched primitives are drawn. + Flip(); + } + In2D = 0; + return; + } + + if (LockCount != 1) + { + I_FatalError("Framebuffer must have exactly 1 lock to be updated"); + if (LockCount > 0) + { + UpdatePending = true; + --LockCount; + } + return; + } + + if (In2D == 0) + { + DrawRateStuff(); + } + + if (NeedGammaUpdate) + { + float psgamma[4]; + float igamma; + + NeedGammaUpdate = false; + igamma = 1 / Gamma; + if (IsFullscreen()) + { + GammaRamp ramp; + + for (int i = 0; i < 256; ++i) + { + ramp.blue[i] = ramp.green[i] = ramp.red[i] = uint16_t(65535.f * powf(i / 255.f, igamma)); + } + SetGammaRamp(&ramp); + } + psgamma[2] = psgamma[1] = psgamma[0] = igamma; + psgamma[3] = 0.5; // For SM14 version + SetPixelShaderConstantF(PSCONST_Gamma, psgamma, 1); + } + + if (NeedPalUpdate) + { + UploadPalette(); + NeedPalUpdate = false; + } + +#ifdef WIN32 + BlitCycles.Reset(); + BlitCycles.Clock(); +#endif + + LockCount = 0; + Draw3DPart(In2D <= 1); + if (In2D == 0) + { + Flip(); + } + +#ifdef WIN32 + BlitCycles.Unclock(); + //LOG1 ("cycles = %d\n", BlitCycles); +#endif + + Buffer = nullptr; + UpdatePending = false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Flip +// +//========================================================================== + +void OpenGLSWFrameBuffer::Flip() +{ + assert(InScene); + + Present(); + InScene = false; + + if (!IsFullscreen()) + { + int clientWidth = ClampWidth(GetClientWidth()); + int clientHeight = ClampHeight(GetClientHeight()); + if (clientWidth > 0 && clientHeight > 0 && (Width != clientWidth || Height != clientHeight)) + { + Resize(clientWidth, clientHeight); + + TrueHeight = Height; + PixelDoubling = 0; + Reset(); + + V_OutputResized(Width, Height); + } + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: PaintToWindow +// +//========================================================================== + +#ifdef WIN32 + +bool OpenGLSWFrameBuffer::PaintToWindow() +{ + if (LockCount != 0) + { + return false; + } + Draw3DPart(true); + return true; +} + +#endif + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Draw3DPart +// +// The software 3D part, to be exact. +// +//========================================================================== + +void OpenGLSWFrameBuffer::BindFBBuffer() +{ + int usage = UseMappedMemBuffer ? GL_DYNAMIC_DRAW : GL_STREAM_DRAW; + + int pixelsize = IsBgra() ? 4 : 1; + int size = Width * Height * pixelsize; + + if (FBTexture->Buffers[0] == 0) + { + glGenBuffers(2, (GLuint*)FBTexture->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffers[1]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, size, nullptr, usage); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, size, nullptr, usage); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffers[FBTexture->CurrentBuffer]); + } +} + +void OpenGLSWFrameBuffer::BgraToRgba(uint32_t *dest, const uint32_t *src, int width, int height, int srcpitch) +{ + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + uint32_t r = RPART(src[x]); + uint32_t g = GPART(src[x]); + uint32_t b = BPART(src[x]); + uint32_t a = APART(src[x]); + dest[x] = r | (g << 8) | (b << 16) | (a << 24); + } + dest += width; + src += srcpitch; + } +} + +void OpenGLSWFrameBuffer::Draw3DPart(bool copy3d) +{ + if (copy3d) + { + BindFBBuffer(); + FBTexture->CurrentBuffer = (FBTexture->CurrentBuffer + 1) & 1; + + if (!UseMappedMemBuffer) + { + int pixelsize = IsBgra() ? 4 : 1; + int size = Width * Height * pixelsize; + + uint8_t *dest = (uint8_t*)MapBuffer(GL_PIXEL_UNPACK_BUFFER, size); + if (dest) + { + if (gl.es && pixelsize == 4) + { + BgraToRgba((uint32_t*)dest, (const uint32_t *)MemBuffer, Width, Height, Pitch); + } + else if (Pitch == Width) + { + memcpy(dest, MemBuffer, Width * Height * pixelsize); + } + else + { + uint8_t *src = MemBuffer; + for (int y = 0; y < Height; y++) + { + memcpy(dest, src, Width * pixelsize); + dest += Width * pixelsize; + src += Pitch * pixelsize; + } + } + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + } + } + else if (MappedMemBuffer) + { + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + MappedMemBuffer = nullptr; + } + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, FBTexture->Texture); + if (IsBgra()) + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Width, Height, gl.es ? GL_RGBA : GL_BGRA, GL_UNSIGNED_BYTE, 0); + else + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Width, Height, GL_RED, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + InScene = true; + if (vid_hwaalines) + glEnable(GL_LINE_SMOOTH); + else + glDisable(GL_LINE_SMOOTH); + + SetTexture(0, FBTexture); + SetPaletteTexture(PaletteTexture, 256, BorderColor); + memset(Constant, 0, sizeof(Constant)); + SetAlphaBlend(0); + EnableAlphaTest(false); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_NormalColor]); + else + SetPixelShader(Shaders[SHADER_NormalColorPal]); + if (copy3d) + { + FBVERTEX verts[4]; + uint32_t color0, color1; + if (Accel2D) + { + auto map = swrenderer::CameraLight::Instance()->ShaderColormap(); + if (map == nullptr) + { + color0 = 0; + color1 = 0xFFFFFFF; + } + else + { + color0 = ColorValue(map->ColorizeStart[0] / 2, map->ColorizeStart[1] / 2, map->ColorizeStart[2] / 2, 0); + color1 = ColorValue(map->ColorizeEnd[0] / 2, map->ColorizeEnd[1] / 2, map->ColorizeEnd[2] / 2, 1); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_SpecialColormap]); + else + SetPixelShader(Shaders[SHADER_SpecialColormapPal]); + } + } + else + { + color0 = FlashColor0; + color1 = FlashColor1; + } + CalcFullscreenCoords(verts, Accel2D, color0, color1); + DrawTriangleFans(2, verts); + } + if (IsBgra()) + SetPixelShader(Shaders[SHADER_NormalColor]); + else + SetPixelShader(Shaders[SHADER_NormalColorPal]); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawLetterbox +// +// Draws the black bars at the top and bottom of the screen for letterboxed +// modes. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawLetterbox(int x, int y, int width, int height) +{ + int clientWidth = GetClientWidth(); + int clientHeight = GetClientHeight(); + if (clientWidth == 0 || clientHeight == 0) + return; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glEnable(GL_SCISSOR_TEST); + if (y > 0) + { + glScissor(0, 0, clientWidth, y); + glClear(GL_COLOR_BUFFER_BIT); + } + if (clientHeight - y - height > 0) + { + glScissor(0, y + height, clientWidth, clientHeight - y - height); + glClear(GL_COLOR_BUFFER_BIT); + } + if (x > 0) + { + glScissor(0, y, x, height); + glClear(GL_COLOR_BUFFER_BIT); + } + if (clientWidth - x - width > 0) + { + glScissor(x + width, y, clientWidth - x - width, height); + glClear(GL_COLOR_BUFFER_BIT); + } + glDisable(GL_SCISSOR_TEST); +} + +void OpenGLSWFrameBuffer::UploadPalette() +{ + if (PaletteTexture->Buffers[0] == 0) + { + glGenBuffers(2, (GLuint*)PaletteTexture->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 4, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffers[1]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 4, nullptr, GL_STREAM_DRAW); + + if (gl.es) PaletteTexture->MapBuffer.resize(256 * 4); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffers[PaletteTexture->CurrentBuffer]); + PaletteTexture->CurrentBuffer = (PaletteTexture->CurrentBuffer + 1) & 1; + } + + uint8_t *pix = gl.es ? PaletteTexture->MapBuffer.data() : (uint8_t*)MapBuffer(GL_PIXEL_UNPACK_BUFFER, 256 * 4); + if (pix) + { + int i; + + for (i = 0; i < 256; ++i, pix += 4) + { + pix[0] = SourcePalette[i].b; + pix[1] = SourcePalette[i].g; + pix[2] = SourcePalette[i].r; + pix[3] = (i == 0 ? 0 : 255); + // To let masked textures work, the first palette entry's alpha is 0. + } + pix += 4; + for (; i < 255; ++i, pix += 4) + { + pix[0] = SourcePalette[i].b; + pix[1] = SourcePalette[i].g; + pix[2] = SourcePalette[i].r; + pix[3] = 255; + } + if (gl.es) + { + uint8_t *tempbuffer = PaletteTexture->MapBuffer.data(); + BgraToRgba((uint32_t*)tempbuffer, (const uint32_t *)tempbuffer, 256, 1, 256); + glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, 256 * 4, tempbuffer); + } + else + { + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + } + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, PaletteTexture->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 1, gl.es ? GL_RGBA : GL_BGRA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + BorderColor = ColorXRGB(SourcePalette[255].r, SourcePalette[255].g, SourcePalette[255].b); + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +PalEntry *OpenGLSWFrameBuffer::GetPalette() +{ + return SourcePalette; +} + +void OpenGLSWFrameBuffer::UpdatePalette() +{ + NeedPalUpdate = true; +} + +bool OpenGLSWFrameBuffer::SetGamma(float gamma) +{ + Gamma = gamma; + NeedGammaUpdate = true; + return true; +} + +bool OpenGLSWFrameBuffer::SetFlash(PalEntry rgb, int amount) +{ + FlashColor = rgb; + FlashAmount = amount; + + // Fill in the constants for the pixel shader to do linear interpolation between the palette and the flash: + float r = rgb.r / 255.f, g = rgb.g / 255.f, b = rgb.b / 255.f, a = amount / 256.f; + FlashColor0 = ColorValue(r * a, g * a, b * a, 0); + a = 1 - a; + FlashColor1 = ColorValue(a, a, a, 1); + return true; +} + +void OpenGLSWFrameBuffer::GetFlash(PalEntry &rgb, int &amount) +{ + rgb = FlashColor; + amount = FlashAmount; +} + +void OpenGLSWFrameBuffer::GetFlashedPalette(PalEntry pal[256]) +{ + memcpy(pal, SourcePalette, 256 * sizeof(PalEntry)); + if (FlashAmount) + { + DoBlending(pal, pal, 256, FlashColor.r, FlashColor.g, FlashColor.b, FlashAmount); + } +} + +void OpenGLSWFrameBuffer::SetVSync(bool vsync) +{ + // Switch to the default frame buffer because Nvidia's driver associates the vsync state with the bound FB object. + GLint oldDrawFramebufferBinding = 0, oldReadFramebufferBinding = 0; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDrawFramebufferBinding); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldReadFramebufferBinding); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + Super::SetVSync(vsync); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDrawFramebufferBinding); + glBindFramebuffer(GL_READ_FRAMEBUFFER, oldReadFramebufferBinding); +} + +void OpenGLSWFrameBuffer::NewRefreshRate() +{ +} + +void OpenGLSWFrameBuffer::SetBlendingRect(int x1, int y1, int x2, int y2) +{ + BlendingRect.left = x1; + BlendingRect.top = y1; + BlendingRect.right = x2; + BlendingRect.bottom = y2; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: GetScreenshotBuffer +// +// Returns a pointer into a surface holding the current screen data. +// +//========================================================================== + +void OpenGLSWFrameBuffer::GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) +{ + Super::GetScreenshotBuffer(buffer, pitch, color_type); + /* + LockedRect lrect; + + if (!Accel2D) + { + Super::GetScreenshotBuffer(buffer, pitch, color_type); + return; + } + buffer = nullptr; + if ((ScreenshotTexture = GetCurrentScreen()) != nullptr) + { + if (!ScreenshotTexture->GetSurfaceLevel(0, &ScreenshotSurface)) + { + delete ScreenshotTexture; + ScreenshotTexture = nullptr; + } + else if (!ScreenshotSurface->LockRect(&lrect, nullptr, false)) + { + delete ScreenshotSurface; + ScreenshotSurface = nullptr; + delete ScreenshotTexture; + ScreenshotTexture = nullptr; + } + else + { + buffer = (const uint8_t *)lrect.pBits; + pitch = lrect.Pitch; + color_type = SS_BGRA; + } + } + */ +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: ReleaseScreenshotBuffer +// +//========================================================================== + +void OpenGLSWFrameBuffer::ReleaseScreenshotBuffer() +{ + if (LockCount > 0) + { + Super::ReleaseScreenshotBuffer(); + } + SafeRelease(ScreenshotTexture); +} + +/**************************************************************************/ +/* 2D Stuff */ +/**************************************************************************/ + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawPackedTextures +// +// DEBUG: Draws the texture atlases to the screen, starting with the +// 1-based packnum. Ignores atlases that are flagged for use by one +// texture only. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawPackedTextures(int packnum) +{ + uint32_t empty_colors[8] = + { + 0x50FF0000, 0x5000FF00, 0x500000FF, 0x50FFFF00, + 0x50FF00FF, 0x5000FFFF, 0x50FF8000, 0x500080FF + }; + Atlas *pack; + int x = 8, y = 8; + + if (packnum <= 0) + { + return; + } + pack = Atlases; + // Find the first texture atlas that is an actual atlas. + while (pack != nullptr && pack->OneUse) + { // Skip textures that aren't used as atlases + pack = pack->Next; + } + // Skip however many atlases we would have otherwise drawn + // until we've skipped of them. + while (pack != nullptr && packnum != 1) + { + if (!pack->OneUse) + { // Skip textures that aren't used as atlases + packnum--; + } + pack = pack->Next; + } + // Draw atlases until we run out of room on the screen. + while (pack != nullptr) + { + if (pack->OneUse) + { // Skip textures that aren't used as atlases + pack = pack->Next; + continue; + } + + AddColorOnlyRect(x - 1, y - 1, 258, 258, ColorXRGB(255, 255, 0)); + int back = 0; + for (PackedTexture *box = pack->UsedList; box != nullptr; box = box->Next) + { + AddColorOnlyQuad( + x + box->Area.left * 256 / pack->Width, + y + box->Area.top * 256 / pack->Height, + (box->Area.right - box->Area.left) * 256 / pack->Width, + (box->Area.bottom - box->Area.top) * 256 / pack->Height, empty_colors[back]); + back = (back + 1) & 7; + } + // AddColorOnlyQuad(x, y-LBOffsetI, 256, 256, ColorARGB(180,0,0,0)); + + CheckQuadBatch(); + + BufferedTris *quad = &QuadExtra[QuadBatchPos]; + FBVERTEX *vert = &VertexData[VertexPos]; + + quad->ClearSetup(); + if (pack->Format == GL_R8/* && !tex->IsGray*/) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette/* | BQF_DisableAlphaTest*/; + quad->ShaderNum = BQS_PalTex; + } + else + { + quad->Flags = BQF_WrapUV/* | BQF_DisableAlphaTest*/; + quad->ShaderNum = BQS_Plain; + } + quad->Palette = nullptr; + quad->Texture = pack->Tex; + quad->NumVerts = 4; + quad->NumTris = 2; + + float x0 = float(x); + float y0 = float(y); + float x1 = x0 + 256.f; + float y1 = y0 + 256.f; + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFFF; + vert[0].tu = 0; + vert[0].tv = 0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFFF; + vert[1].tu = 1; + vert[1].tv = 0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFFF; + vert[2].tu = 1; + vert[2].tv = 1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFFF; + vert[3].tu = 0; + vert[3].tv = 1; + + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; + + x += 256 + 8; + if (x > Width - 256) + { + x = 8; + y += 256 + 8; + if (y > Height - 256) + { + return; + } + } + pack = pack->Next; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: AllocPackedTexture +// +// Finds space to pack an image inside a texture atlas and returns it. +// Large images and those that need to wrap always get their own textures. +// +//========================================================================== + +OpenGLSWFrameBuffer::PackedTexture *OpenGLSWFrameBuffer::AllocPackedTexture(int w, int h, bool wrapping, int format) +{ + Atlas *pack; + Rect box; + bool padded; + + // The - 2 to account for padding + if (w > 256 - 2 || h > 256 - 2 || wrapping) + { // Create a new texture atlas. + pack = new Atlas(this, w, h, format); + pack->OneUse = true; + box = pack->Packer.Insert(w, h); + padded = false; + } + else + { // Try to find space in an existing texture atlas. + w += 2; // Add padding + h += 2; + for (pack = Atlases; pack != nullptr; pack = pack->Next) + { + // Use the first atlas it fits in. + if (pack->Format == format) + { + box = pack->Packer.Insert(w, h); + if (box.width != 0) + { + break; + } + } + } + if (pack == nullptr) + { // Create a new texture atlas. + pack = new Atlas(this, DEF_ATLAS_WIDTH, DEF_ATLAS_HEIGHT, format); + box = pack->Packer.Insert(w, h); + } + padded = true; + } + assert(box.width != 0 && box.height != 0); + return pack->AllocateImage(box, padded); +} + +//========================================================================== +// +// Atlas Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Atlas::Atlas(OpenGLSWFrameBuffer *fb, int w, int h, int format) + : Packer(w, h, true) +{ + Tex = nullptr; + Format = format; + UsedList = nullptr; + OneUse = false; + Width = 0; + Height = 0; + Next = nullptr; + + // Attach to the end of the atlas list + Atlas **prev = &fb->Atlases; + while (*prev != nullptr) + { + prev = &((*prev)->Next); + } + *prev = this; + + fb->CreateTexture("Atlas", w, h, 1, format, &Tex); + Width = w; + Height = h; +} + +//========================================================================== +// +// Atlas Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Atlas::~Atlas() +{ + PackedTexture *box, *next; + + SafeRelease(Tex); + for (box = UsedList; box != nullptr; box = next) + { + next = box->Next; + delete box; + } +} + +//========================================================================== +// +// Atlas :: AllocateImage +// +// Moves the box from the empty list to the used list, sizing it to the +// requested dimensions and adding additional boxes to the empty list if +// needed. +// +// The passed box *MUST* be in this texture atlas's empty list. +// +//========================================================================== + +OpenGLSWFrameBuffer::PackedTexture *OpenGLSWFrameBuffer::Atlas::AllocateImage(const Rect &rect, bool padded) +{ + PackedTexture *box = new PackedTexture; + + box->Owner = this; + box->Area.left = rect.x; + box->Area.top = rect.y; + box->Area.right = rect.x + rect.width; + box->Area.bottom = rect.y + rect.height; + + box->Left = float(box->Area.left + padded) / Width; + box->Right = float(box->Area.right - padded) / Width; + box->Top = float(box->Area.top + padded) / Height; + box->Bottom = float(box->Area.bottom - padded) / Height; + + box->Padded = padded; + + // Add it to the used list. + box->Next = UsedList; + if (box->Next != nullptr) + { + box->Next->Prev = &box->Next; + } + UsedList = box; + box->Prev = &UsedList; + + return box; +} + +//========================================================================== +// +// Atlas :: FreeBox +// +// Removes a box from the used list and deletes it. Space is returned to the +// waste list. Once all boxes for this atlas are freed, the entire bin +// packer is reinitialized for maximum efficiency. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Atlas::FreeBox(OpenGLSWFrameBuffer::PackedTexture *box) +{ + *(box->Prev) = box->Next; + if (box->Next != nullptr) + { + box->Next->Prev = box->Prev; + } + Rect waste; + waste.x = box->Area.left; + waste.y = box->Area.top; + waste.width = box->Area.right - box->Area.left; + waste.height = box->Area.bottom - box->Area.top; + box->Owner->Packer.AddWaste(waste); + delete box; + if (UsedList == nullptr) + { + Packer.Init(Width, Height, true); + } +} + +//========================================================================== +// +// OpenGLTex Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLTex::OpenGLTex(FTexture *tex, OpenGLSWFrameBuffer *fb, bool wrapping) +{ + // Attach to the texture list for the OpenGLSWFrameBuffer + Next = fb->Textures; + if (Next != nullptr) + { + Next->Prev = &Next; + } + Prev = &fb->Textures; + fb->Textures = this; + + GameTex = tex; + Box = nullptr; + IsGray = false; + + Create(fb, wrapping); +} + +//========================================================================== +// +// OpenGLTex Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLTex::~OpenGLTex() +{ + if (Box != nullptr) + { + Box->Owner->FreeBox(Box); + Box = nullptr; + } + // Detach from the texture list + *Prev = Next; + if (Next != nullptr) + { + Next->Prev = Prev; + } + // Remove link from the game texture + if (GameTex != nullptr) + { + GameTex->Native = nullptr; + } +} + +//========================================================================== +// +// OpenGLTex :: CheckWrapping +// +// Returns true if the texture is compatible with the specified wrapping +// mode. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLTex::CheckWrapping(bool wrapping) +{ + // If it doesn't need to wrap, then it works. + if (!wrapping) + { + return true; + } + // If it needs to wrap, then it can't be packed inside another texture. + return Box->Owner->OneUse; +} + +//========================================================================== +// +// OpenGLTex :: Create +// +// Creates an HWTexture for the texture and copies the image data +// to it. Note that unlike FTexture, this image is row-major. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLTex::Create(OpenGLSWFrameBuffer *fb, bool wrapping) +{ + assert(Box == nullptr); + if (Box != nullptr) + { + Box->Owner->FreeBox(Box); + } + + Box = fb->AllocPackedTexture(GameTex->GetWidth(), GameTex->GetHeight(), wrapping, GetTexFormat()); + + if (Box == nullptr) + { + return false; + } + if (!Update()) + { + Box->Owner->FreeBox(Box); + Box = nullptr; + return false; + } + return true; +} + +//========================================================================== +// +// OpenGLTex :: Update +// +// Copies image data from the underlying FTexture to the OpenGL texture. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLTex::Update() +{ + LTRBRect rect; + uint8_t *dest; + + assert(Box != nullptr); + assert(Box->Owner != nullptr); + assert(Box->Owner->Tex != nullptr); + assert(GameTex != nullptr); + + int format = Box->Owner->Tex->Format; + + rect = Box->Area; + + if (Box->Owner->Tex->Buffers[0] == 0) + glGenBuffers(2, (GLuint*)Box->Owner->Tex->Buffers); + + int bytesPerPixel = 4; + switch (format) + { + case GL_R8: bytesPerPixel = 1; break; + case GL_RGBA8: bytesPerPixel = 4; break; + default: return false; + } + + int buffersize = (rect.right - rect.left) * (rect.bottom - rect.top) * bytesPerPixel; + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Box->Owner->Tex->Buffers[Box->Owner->Tex->CurrentBuffer]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, buffersize, nullptr, GL_STREAM_DRAW); + Box->Owner->Tex->CurrentBuffer = (Box->Owner->Tex->CurrentBuffer + 1) & 1; + + static std::vector tempbuffer; + if (gl.es) + tempbuffer.resize(buffersize); + + int pitch = (rect.right - rect.left) * bytesPerPixel; + uint8_t *bits = gl.es ? tempbuffer.data() : (uint8_t *)MapBuffer(GL_PIXEL_UNPACK_BUFFER, buffersize); + dest = bits; + if (!dest) + { + return false; + } + if (Box->Padded) + { + dest += pitch + (format == GL_R8 ? 1 : 4); + } + GameTex->FillBuffer(dest, pitch, GameTex->GetHeight(), ToTexFmt(format)); + if (Box->Padded) + { + // Clear top padding row. + dest = bits; + int numbytes = GameTex->GetWidth() + 2; + if (format != GL_R8) + { + numbytes <<= 2; + } + memset(dest, 0, numbytes); + dest += pitch; + // Clear left and right padding columns. + if (format == GL_R8) + { + for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y) + { + dest[0] = 0; + dest[numbytes - 1] = 0; + dest += pitch; + } + } + else + { + for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y) + { + *(uint32_t *)dest = 0; + *(uint32_t *)(dest + numbytes - 4) = 0; + dest += pitch; + } + } + // Clear bottom padding row. + memset(dest, 0, numbytes); + } + + if (gl.es && format == GL_RGBA8) + { + BgraToRgba((uint32_t*)bits, (const uint32_t *)bits, rect.right - rect.left, rect.bottom - rect.top, rect.right - rect.left); + } + + if (gl.es) + glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, buffersize, bits); + else + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, Box->Owner->Tex->Texture); + if (format == GL_RGBA8) + glTexSubImage2D(GL_TEXTURE_2D, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, gl.es ? GL_RGBA : GL_BGRA, GL_UNSIGNED_BYTE, 0); + else + glTexSubImage2D(GL_TEXTURE_2D, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, GL_RED, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + return true; +} + +//========================================================================== +// +// OpenGLTex :: GetTexFormat +// +// Returns the texture format that would best fit this texture. +// +//========================================================================== + +int OpenGLSWFrameBuffer::OpenGLTex::GetTexFormat() +{ + FTextureFormat fmt = GameTex->GetFormat(); + + IsGray = false; + + switch (fmt) + { + case TEX_Pal: return GL_R8; + case TEX_Gray: IsGray = true; return GL_R8; + case TEX_RGB: return GL_RGBA8; + case TEX_DXT1: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + case TEX_DXT2: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case TEX_DXT3: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case TEX_DXT4: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; // Doesn't exist in OpenGL. Closest match is DXT5. + case TEX_DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + default: I_FatalError("GameTex->GetFormat() returned invalid format."); + } + return GL_R8; +} + +//========================================================================== +// +// OpenGLTex :: ToTexFmt +// +// Converts an OpenGL internal format constant to something the FTexture system +// understands. +// +//========================================================================== + +FTextureFormat OpenGLSWFrameBuffer::OpenGLTex::ToTexFmt(int fmt) +{ + switch (fmt) + { + case GL_R8: return IsGray ? TEX_Gray : TEX_Pal; + case GL_RGBA8: return TEX_RGB; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return TEX_DXT1; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return TEX_DXT2; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return TEX_DXT3; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return TEX_DXT5; + default: + assert(0); // LOL WUT? + return TEX_Pal; + } +} + +//========================================================================== +// +// OpenGLPal Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLPal::OpenGLPal(FRemapTable *remap, OpenGLSWFrameBuffer *fb) + : Tex(nullptr), Remap(remap) +{ + int count; + + // Attach to the palette list for the OpenGLSWFrameBuffer + Next = fb->Palettes; + if (Next != nullptr) + { + Next->Prev = &Next; + } + Prev = &fb->Palettes; + fb->Palettes = this; + + int pow2count; + + // Round up to the nearest power of 2. + for (pow2count = 1; pow2count < remap->NumEntries; pow2count <<= 1) + { + } + count = pow2count; + DoColorSkip = false; + + BorderColor = 0; + RoundedPaletteSize = count; + if (fb->CreateTexture("Pal", count, 1, 1, GL_RGBA8, &Tex)) + { + if (!Update()) + { + delete Tex; + Tex = nullptr; + } + } +} + +//========================================================================== +// +// OpenGLPal Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLPal::~OpenGLPal() +{ + SafeRelease(Tex); + // Detach from the palette list + *Prev = Next; + if (Next != nullptr) + { + Next->Prev = Prev; + } + // Remove link from the remap table + if (Remap != nullptr) + { + Remap->Native = nullptr; + } +} + +//========================================================================== +// +// OpenGLPal :: Update +// +// Copies the palette to the texture. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLPal::Update() +{ + uint32_t *buff; + const PalEntry *pal; + int skipat, i; + + assert(Tex != nullptr); + + if (Tex->Buffers[0] == 0) + { + glGenBuffers(2, (GLuint*)Tex->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, RoundedPaletteSize * 4, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffers[1]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, RoundedPaletteSize * 4, nullptr, GL_STREAM_DRAW); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffers[Tex->CurrentBuffer]); + Tex->CurrentBuffer = (Tex->CurrentBuffer + 1) & 1; + } + + int numEntries = MIN(Remap->NumEntries, RoundedPaletteSize); + + std::vector &tempbuffer = Tex->MapBuffer; + if (gl.es) + tempbuffer.resize(numEntries * 4); + + buff = gl.es ? (uint32_t*)tempbuffer.data() : (uint32_t *)MapBuffer(GL_PIXEL_UNPACK_BUFFER, numEntries * 4); + if (buff == nullptr) + { + return false; + } + pal = Remap->Palette; + + // See explanation in UploadPalette() for skipat rationale. + skipat = MIN(numEntries, DoColorSkip ? 256 - 8 : 256); + + for (i = 0; i < skipat; ++i) + { + buff[i] = ColorARGB(pal[i].a, pal[i].r, pal[i].g, pal[i].b); + } + for (++i; i < numEntries; ++i) + { + buff[i] = ColorARGB(pal[i].a, pal[i - 1].r, pal[i - 1].g, pal[i - 1].b); + } + if (numEntries > 1) + { + i = numEntries - 1; + BorderColor = ColorARGB(pal[i].a, pal[i - 1].r, pal[i - 1].g, pal[i - 1].b); + } + + if (gl.es) + { + BgraToRgba((uint32_t*)buff, (const uint32_t *)buff, numEntries, 1, numEntries); + glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, numEntries * 4, buff); + } + else + { + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + } + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, Tex->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, numEntries, 1, gl.es ? GL_RGBA : GL_BGRA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Begin2D +// +// Begins 2D mode drawing operations. In particular, DrawTexture is +// rerouted to use Direct3D instead of the software renderer. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Begin2D(bool copy3d) +{ + if (!Accel2D) + { + return false; + } + if (In2D) + { + return true; + } + In2D = 2 - copy3d; + Update(); + In2D = 3; + + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawBlendingRect +// +// Call after Begin2D to blend the 3D view. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawBlendingRect() +{ + if (!In2D || !Accel2D) + { + return; + } + Dim(FlashColor, FlashAmount / 256.f, viewwindowx, viewwindowy, viewwidth, viewheight); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateTexture +// +// Returns a native texture that wraps a FTexture. +// +//========================================================================== + +FNativeTexture *OpenGLSWFrameBuffer::CreateTexture(FTexture *gametex, bool wrapping) +{ + OpenGLTex *tex = new OpenGLTex(gametex, this, wrapping); + if (tex->Box == nullptr) + { + delete tex; + return nullptr; + } + return tex; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreatePalette +// +// Returns a native texture that contains a palette. +// +//========================================================================== + +FNativePalette *OpenGLSWFrameBuffer::CreatePalette(FRemapTable *remap) +{ + OpenGLPal *tex = new OpenGLPal(remap, this); + if (tex->Tex == nullptr) + { + delete tex; + return nullptr; + } + return tex; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Clear +// +// Fills the specified region with a color. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Clear(int left, int top, int right, int bottom, int palcolor, uint32_t color) +{ + if (In2D < 2) + { + Super::Clear(left, top, right, bottom, palcolor, color); + return; + } + if (!InScene) + { + return; + } + if (palcolor >= 0 && color == 0) + { + color = GPalette.BaseColors[palcolor]; + } + else if (APART(color) < 255) + { + Dim(color, APART(color) / 255.f, left, top, right - left, bottom - top); + return; + } + AddColorOnlyQuad(left, top, right - left, bottom - top, color | 0xFF000000); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Dim +// +//========================================================================== + +void OpenGLSWFrameBuffer::Dim(PalEntry color, float amount, int x1, int y1, int w, int h) +{ + if (amount <= 0) + { + return; + } + if (In2D < 2) + { + Super::Dim(color, amount, x1, y1, w, h); + return; + } + if (!InScene) + { + return; + } + if (amount > 1) + { + amount = 1; + } + AddColorOnlyQuad(x1, y1, w, h, color | (int(amount * 255) << 24)); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: BeginLineBatch +// +//========================================================================== + +void OpenGLSWFrameBuffer::BeginLineBatch() +{ + if (In2D < 2 || !InScene || BatchType == BATCH_Lines) + { + return; + } + EndQuadBatch(); // Make sure all quads have been drawn first. + VertexData = VertexBuffer->Lock(); + VertexPos = 0; + BatchType = BATCH_Lines; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: EndLineBatch +// +//========================================================================== + +void OpenGLSWFrameBuffer::EndLineBatch() +{ + if (In2D < 2 || !InScene || BatchType != BATCH_Lines) + { + return; + } + VertexBuffer->Unlock(); + if (VertexPos > 0) + { + SetPixelShader(Shaders[SHADER_VertexColor]); + SetAlphaBlend(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + SetStreamSource(VertexBuffer); + DrawLineList(VertexPos / 2); + } + VertexPos = -1; + BatchType = BATCH_None; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawLine +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawLine(int x0, int y0, int x1, int y1, int palcolor, uint32_t color) +{ + if (In2D < 2) + { + Super::DrawLine(x0, y0, x1, y1, palcolor, color); + return; + } + if (!InScene) + { + return; + } + if (BatchType != BATCH_Lines) + { + BeginLineBatch(); + } + if (VertexPos == NUM_VERTS) + { // Flush the buffer and refill it. + EndLineBatch(); + BeginLineBatch(); + } + // Add the endpoints to the vertex buffer. + VertexData[VertexPos].x = float(x0); + VertexData[VertexPos].y = float(y0); + VertexData[VertexPos].z = 0; + VertexData[VertexPos].rhw = 1; + VertexData[VertexPos].color0 = color; + VertexData[VertexPos].color1 = 0; + VertexData[VertexPos].tu = 0; + VertexData[VertexPos].tv = 0; + + VertexData[VertexPos + 1].x = float(x1); + VertexData[VertexPos + 1].y = float(y1); + VertexData[VertexPos + 1].z = 0; + VertexData[VertexPos + 1].rhw = 1; + VertexData[VertexPos + 1].color0 = color; + VertexData[VertexPos + 1].color1 = 0; + VertexData[VertexPos + 1].tu = 0; + VertexData[VertexPos + 1].tv = 0; + + VertexPos += 2; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawPixel +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawPixel(int x, int y, int palcolor, uint32_t color) +{ + if (In2D < 2) + { + Super::DrawPixel(x, y, palcolor, color); + return; + } + if (!InScene) + { + return; + } + FBVERTEX pt = + { + float(x), float(y), 0, 1, color + }; + EndBatch(); // Draw out any batched operations. + SetPixelShader(Shaders[SHADER_VertexColor]); + SetAlphaBlend(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + DrawPoints(1, &pt); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawTextureV +// +// If not in 2D mode, just call the normal software version. +// If in 2D mode, then use Direct3D calls to perform the drawing. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawTextureParms(FTexture *img, DrawParms &parms) +{ + if (In2D < 2) + { + Super::DrawTextureParms(img, parms); + return; + } + if (!InScene) + { + return; + } + + OpenGLTex *tex = static_cast(img->GetNative(false)); + + if (tex == nullptr) + { + assert(tex != nullptr); + return; + } + + CheckQuadBatch(); + + double xscale = parms.destwidth / parms.texwidth; + double yscale = parms.destheight / parms.texheight; + double x0 = parms.x - parms.left * xscale; + double y0 = parms.y - parms.top * yscale; + double x1 = x0 + parms.destwidth; + double y1 = y0 + parms.destheight; + float u0 = tex->Box->Left; + float v0 = tex->Box->Top; + float u1 = tex->Box->Right; + float v1 = tex->Box->Bottom; + double uscale = 1.f / tex->Box->Owner->Width; + bool scissoring = false; + FBVERTEX *vert; + + if (parms.flipX) + { + swapvalues(u0, u1); + } + if (parms.windowleft > 0 || parms.windowright < parms.texwidth) + { + double wi = MIN(parms.windowright, parms.texwidth); + x0 += parms.windowleft * xscale; + u0 = float(u0 + parms.windowleft * uscale); + x1 -= (parms.texwidth - wi) * xscale; + u1 = float(u1 - (parms.texwidth - wi) * uscale); + } + +#if 0 + float vscale = 1.f / tex->Box->Owner->Height / yscale; + if (y0 < parms.uclip) + { + v0 += (float(parms.uclip) - y0) * vscale; + y0 = float(parms.uclip); + } + if (y1 > parms.dclip) + { + v1 -= (y1 - float(parms.dclip)) * vscale; + y1 = float(parms.dclip); + } + if (x0 < parms.lclip) + { + u0 += float(parms.lclip - x0) * uscale / xscale * 2; + x0 = float(parms.lclip); + } + if (x1 > parms.rclip) + { + u1 -= (x1 - parms.rclip) * uscale / xscale * 2; + x1 = float(parms.rclip); + } +#else + // Use a scissor test because the math above introduces some jitter + // that is noticeable at low resolutions. Unfortunately, this means this + // quad has to be in a batch by itself. + if (y0 < parms.uclip || y1 > parms.dclip || x0 < parms.lclip || x1 > parms.rclip) + { + scissoring = true; + if (QuadBatchPos > 0) + { + EndQuadBatch(); + BeginQuadBatch(); + } + glEnable(GL_SCISSOR_TEST); + glScissor(parms.lclip, parms.uclip, parms.rclip - parms.lclip, parms.dclip - parms.uclip); + } +#endif + parms.bilinear = false; + + uint32_t color0, color1; + BufferedTris *quad = &QuadExtra[QuadBatchPos]; + + if (!SetStyle(tex, parms, color0, color1, *quad)) + { + goto done; + } + + quad->Texture = tex->Box->Owner->Tex; + if (parms.bilinear) + { + quad->Flags |= BQF_Bilinear; + } + quad->NumTris = 2; + quad->NumVerts = 4; + + vert = &VertexData[VertexPos]; + + // Fill the vertex buffer. + vert[0].x = float(x0); + vert[0].y = float(y0); + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = color0; + vert[0].color1 = color1; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = float(x1); + vert[1].y = float(y0); + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = color0; + vert[1].color1 = color1; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = float(x1); + vert[2].y = float(y1); + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = color0; + vert[2].color1 = color1; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = float(x0); + vert[3].y = float(y1); + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = color0; + vert[3].color1 = color1; + vert[3].tu = u0; + vert[3].tv = v1; + + // Fill the vertex index buffer. + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + // Batch the quad. + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; +done: + if (scissoring) + { + EndQuadBatch(); + glDisable(GL_SCISSOR_TEST); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: FlatFill +// +// Fills an area with a repeating copy of the texture. +// +//========================================================================== + +void OpenGLSWFrameBuffer::FlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin) +{ + if (In2D < 2) + { + Super::FlatFill(left, top, right, bottom, src, local_origin); + return; + } + if (!InScene) + { + return; + } + OpenGLTex *tex = static_cast(src->GetNative(true)); + if (tex == nullptr) + { + return; + } + float x0 = float(left); + float y0 = float(top); + float x1 = float(right); + float y1 = float(bottom); + float itw = 1.f / float(src->GetWidth()); + float ith = 1.f / float(src->GetHeight()); + float xo = local_origin ? x0 : 0; + float yo = local_origin ? y0 : 0; + float u0 = (x0 - xo) * itw; + float v0 = (y0 - yo) * ith; + float u1 = (x1 - xo) * itw; + float v1 = (y1 - yo) * ith; + + CheckQuadBatch(); + + BufferedTris *quad = &QuadExtra[QuadBatchPos]; + FBVERTEX *vert = &VertexData[VertexPos]; + + quad->ClearSetup(); + if (tex->GetTexFormat() == GL_R8 && !tex->IsGray) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette; // | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_PalTex; + } + else + { + quad->Flags = BQF_WrapUV; // | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + } + quad->Palette = nullptr; + quad->Texture = tex->Box->Owner->Tex; + quad->NumVerts = 4; + quad->NumTris = 2; + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFFF; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFFF; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFFF; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFFF; + vert[3].tu = u0; + vert[3].tv = v1; + + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: FillSimplePoly +// +// Here, "simple" means that a simple triangle fan can draw it. +// +//========================================================================== + +void OpenGLSWFrameBuffer::FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + DAngle rotation, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) +{ + // Use an equation similar to player sprites to determine shade + double fadelevel = clamp((swrenderer::LightVisibility::LightLevelToShade(lightlevel, true) / 65536. - 12) / NUMCOLORMAPS, 0.0, 1.0); + + BufferedTris *quad; + FBVERTEX *verts; + OpenGLTex *tex; + float uscale, vscale; + int i, ipos; + uint32_t color0, color1; + float ox, oy; + float cosrot, sinrot; + bool dorotate = rotation != 0; + + if (npoints < 3) + { // This is no polygon. + return; + } + if (In2D < 2) + { + Super::FillSimplePoly(texture, points, npoints, originx, originy, scalex, scaley, rotation, colormap, lightlevel, flatcolor, bottomclip); + return; + } + if (!InScene) + { + return; + } + tex = static_cast(texture->GetNative(true)); + if (tex == nullptr) + { + return; + } + + cosrot = (float)cos(rotation.Radians()); + sinrot = (float)sin(rotation.Radians()); + + CheckQuadBatch(npoints - 2, npoints); + quad = &QuadExtra[QuadBatchPos]; + verts = &VertexData[VertexPos]; + + color0 = 0; + color1 = 0xFFFFFFFF; + + quad->ClearSetup(); + if (tex->GetTexFormat() == GL_R8 && !tex->IsGray) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_PalTex; + if (colormap != nullptr) + { + if (colormap->Desaturate != 0) + { + quad->Flags |= BQF_Desaturated; + } + quad->ShaderNum = BQS_InGameColormap; + quad->Desat = colormap->Desaturate; + color0 = ColorARGB(255, colormap->Color.r, colormap->Color.g, colormap->Color.b); + color1 = ColorARGB(uint32_t((1 - fadelevel) * 255), + uint32_t(colormap->Fade.r * fadelevel), + uint32_t(colormap->Fade.g * fadelevel), + uint32_t(colormap->Fade.b * fadelevel)); + } + } + else + { + quad->Flags = BQF_WrapUV | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + } + quad->Palette = nullptr; + quad->Texture = tex->Box->Owner->Tex; + quad->NumVerts = npoints; + quad->NumTris = npoints - 2; + + uscale = float(1.f / (texture->GetScaledWidth() * scalex)); + vscale = float(1.f / (texture->GetScaledHeight() * scaley)); + ox = float(originx); + oy = float(originy); + + for (i = 0; i < npoints; ++i) + { + verts[i].x = points[i].X; + verts[i].y = points[i].Y; + verts[i].z = 0; + verts[i].rhw = 1; + verts[i].color0 = color0; + verts[i].color1 = color1; + float u = points[i].X - ox; + float v = points[i].Y - oy; + if (dorotate) + { + float t = u; + u = t * cosrot - v * sinrot; + v = v * cosrot + t * sinrot; + } + verts[i].tu = u * uscale; + verts[i].tv = v * vscale; + } + for (ipos = IndexPos, i = 2; i < npoints; ++i, ipos += 3) + { + IndexData[ipos] = VertexPos; + IndexData[ipos + 1] = VertexPos + i - 1; + IndexData[ipos + 2] = VertexPos + i; + } + + QuadBatchPos++; + VertexPos += npoints; + IndexPos = ipos; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: AddColorOnlyQuad +// +// Adds a single-color, untextured quad to the batch. +// +//========================================================================== + +void OpenGLSWFrameBuffer::AddColorOnlyQuad(int left, int top, int width, int height, uint32_t color) +{ + BufferedTris *quad; + FBVERTEX *verts; + + CheckQuadBatch(); + quad = &QuadExtra[QuadBatchPos]; + verts = &VertexData[VertexPos]; + + float x = float(left); + float y = float(top); + + quad->ClearSetup(); + quad->ShaderNum = BQS_ColorOnly; + if ((color & 0xFF000000) != 0xFF000000) + { + quad->BlendOp = GL_FUNC_ADD; + quad->SrcBlend = GL_SRC_ALPHA; + quad->DestBlend = GL_ONE_MINUS_SRC_ALPHA; + } + quad->Palette = nullptr; + quad->Texture = nullptr; + quad->NumVerts = 4; + quad->NumTris = 2; + + verts[0].x = x; + verts[0].y = y; + verts[0].z = 0; + verts[0].rhw = 1; + verts[0].color0 = color; + verts[0].color1 = 0; + verts[0].tu = 0; + verts[0].tv = 0; + + verts[1].x = x + width; + verts[1].y = y; + verts[1].z = 0; + verts[1].rhw = 1; + verts[1].color0 = color; + verts[1].color1 = 0; + verts[1].tu = 0; + verts[1].tv = 0; + + verts[2].x = x + width; + verts[2].y = y + height; + verts[2].z = 0; + verts[2].rhw = 1; + verts[2].color0 = color; + verts[2].color1 = 0; + verts[2].tu = 0; + verts[2].tv = 0; + + verts[3].x = x; + verts[3].y = y + height; + verts[3].z = 0; + verts[3].rhw = 1; + verts[3].color0 = color; + verts[3].color1 = 0; + verts[3].tu = 0; + verts[3].tv = 0; + + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: AddColorOnlyRect +// +// Like AddColorOnlyQuad, except it's hollow. +// +//========================================================================== + +void OpenGLSWFrameBuffer::AddColorOnlyRect(int left, int top, int width, int height, uint32_t color) +{ + AddColorOnlyQuad(left, top, width - 1, 1, color); // top + AddColorOnlyQuad(left + width - 1, top, 1, height - 1, color); // right + AddColorOnlyQuad(left + 1, top + height - 1, width - 1, 1, color); // bottom + AddColorOnlyQuad(left, top + 1, 1, height - 1, color); // left +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CheckQuadBatch +// +// Make sure there's enough room in the batch for one more set of triangles. +// +//========================================================================== + +void OpenGLSWFrameBuffer::CheckQuadBatch(int numtris, int numverts) +{ + if (BatchType == BATCH_Lines) + { + EndLineBatch(); + } + else if (QuadBatchPos == MAX_QUAD_BATCH || + VertexPos + numverts > NUM_VERTS || + IndexPos + numtris * 3 > NUM_INDEXES) + { + EndQuadBatch(); + } + if (QuadBatchPos < 0) + { + BeginQuadBatch(); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: BeginQuadBatch +// +// Locks the vertex buffer for quads and sets the cursor to 0. +// +//========================================================================== + +void OpenGLSWFrameBuffer::BeginQuadBatch() +{ + if (In2D < 2 || !InScene || QuadBatchPos >= 0) + { + return; + } + EndLineBatch(); // Make sure all lines have been drawn first. + VertexData = VertexBuffer->Lock(); + IndexData = IndexBuffer->Lock(); + VertexPos = 0; + IndexPos = 0; + QuadBatchPos = 0; + BatchType = BATCH_Quads; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: EndQuadBatch +// +// Draws all the quads that have been batched up. +// +//========================================================================== + +void OpenGLSWFrameBuffer::EndQuadBatch() +{ + if (In2D < 2 || !InScene || BatchType != BATCH_Quads) + { + return; + } + BatchType = BATCH_None; + VertexBuffer->Unlock(); + IndexBuffer->Unlock(); + if (QuadBatchPos == 0) + { + QuadBatchPos = -1; + VertexPos = -1; + IndexPos = -1; + return; + } + SetStreamSource(VertexBuffer); + SetIndices(IndexBuffer); + bool uv_wrapped = false; + bool uv_should_wrap; + int indexpos, vertpos; + + indexpos = vertpos = 0; + for (int i = 0; i < QuadBatchPos; ) + { + const BufferedTris *quad = &QuadExtra[i]; + int j; + + int startindex = indexpos; + int startvertex = vertpos; + + indexpos += quad->NumTris * 3; + vertpos += quad->NumVerts; + + // Quads with matching parameters should be done with a single + // DrawPrimitive call. + for (j = i + 1; j < QuadBatchPos; ++j) + { + const BufferedTris *q2 = &QuadExtra[j]; + if (quad->Texture != q2->Texture || + !quad->IsSameSetup(*q2) || + quad->Palette != q2->Palette) + { + break; + } + if (quad->ShaderNum == BQS_InGameColormap && (quad->Flags & BQF_Desaturated) && quad->Desat != q2->Desat) + { + break; + } + indexpos += q2->NumTris * 3; + vertpos += q2->NumVerts; + } + + // Set the palette (if one) + if ((quad->Flags & BQF_Paletted) == BQF_GamePalette) + { + SetPaletteTexture(PaletteTexture, 256, BorderColor); + } + else if ((quad->Flags & BQF_Paletted) == BQF_CustomPalette) + { + assert(quad->Palette != nullptr); + SetPaletteTexture(quad->Palette->Tex, quad->Palette->RoundedPaletteSize, quad->Palette->BorderColor); + } + + // Set the alpha blending + SetAlphaBlend(quad->BlendOp, quad->SrcBlend, quad->DestBlend); + + // Set the alpha test + EnableAlphaTest(!(quad->Flags & BQF_DisableAlphaTest)); + + // Set the pixel shader + if (quad->ShaderNum == BQS_PalTex) + { + SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ? + SHADER_NormalColorPalInv : SHADER_NormalColorPal]); + } + else if (quad->ShaderNum == BQS_Plain) + { + SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ? + SHADER_NormalColorInv : SHADER_NormalColor]); + } + else if (quad->ShaderNum == BQS_RedToAlpha) + { + SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ? + SHADER_RedToAlphaInv : SHADER_RedToAlpha]); + } + else if (quad->ShaderNum == BQS_ColorOnly) + { + SetPixelShader(Shaders[SHADER_VertexColor]); + } + else if (quad->ShaderNum == BQS_SpecialColormap) + { + int select; + + select = !!(quad->Flags & BQF_Paletted); + SetPixelShader(Shaders[SHADER_SpecialColormap + select]); + } + else if (quad->ShaderNum == BQS_InGameColormap) + { + int select; + + select = !!(quad->Flags & BQF_Desaturated); + select |= !!(quad->Flags & BQF_InvertSource) << 1; + select |= !!(quad->Flags & BQF_Paletted) << 2; + if (quad->Flags & BQF_Desaturated) + { + SetConstant(PSCONST_Desaturation, quad->Desat / 255.f, (255 - quad->Desat) / 255.f, 0, 0); + } + SetPixelShader(Shaders[SHADER_InGameColormap + select]); + } + + // Set the texture clamp addressing mode + uv_should_wrap = !!(quad->Flags & BQF_WrapUV); + if (uv_wrapped != uv_should_wrap) + { + uint32_t mode = uv_should_wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE; + uv_wrapped = uv_should_wrap; + SetSamplerWrapS(0, mode); + SetSamplerWrapT(0, mode); + } + + // Set the texture + if (quad->Texture != nullptr) + { + SetTexture(0, quad->Texture); + } + + // Draw the quad + DrawTriangleList( + startvertex, // MinIndex + vertpos - startvertex, // NumVertices + startindex, // StartIndex + (indexpos - startindex) / 3 // PrimitiveCount + /*4 * i, 4 * (j - i), 6 * i, 2 * (j - i)*/); + i = j; + } + if (uv_wrapped) + { + SetSamplerWrapS(0, GL_CLAMP_TO_EDGE); + SetSamplerWrapT(0, GL_CLAMP_TO_EDGE); + } + QuadBatchPos = -1; + VertexPos = -1; + IndexPos = -1; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: EndBatch +// +// Draws whichever type of primitive is currently being batched. +// +//========================================================================== + +void OpenGLSWFrameBuffer::EndBatch() +{ + if (BatchType == BATCH_Quads) + { + EndQuadBatch(); + } + else if (BatchType == BATCH_Lines) + { + EndLineBatch(); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: SetStyle +// +// Patterned after R_SetPatchStyle. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::SetStyle(OpenGLTex *tex, DrawParms &parms, uint32_t &color0, uint32_t &color1, BufferedTris &quad) +{ + int fmt = tex->GetTexFormat(); + FRenderStyle style = parms.style; + float alpha; + bool stencilling; + + if (style.Flags & STYLEF_TransSoulsAlpha) + { + alpha = transsouls; + } + else if (style.Flags & STYLEF_Alpha1) + { + alpha = 1; + } + else + { + alpha = clamp(parms.Alpha, 0.f, 1.f); + } + + style.CheckFuzz(); + if (style.BlendOp == STYLEOP_Shadow) + { + style = LegacyRenderStyles[STYLE_TranslucentStencil]; + alpha = 0.3f; + parms.fillcolor = 0; + } + + // FIXME: Fuzz effect is not written + if (style.BlendOp == STYLEOP_FuzzOrAdd || style.BlendOp == STYLEOP_Fuzz) + { + style.BlendOp = STYLEOP_Add; + } + else if (style.BlendOp == STYLEOP_FuzzOrSub) + { + style.BlendOp = STYLEOP_Sub; + } + else if (style.BlendOp == STYLEOP_FuzzOrRevSub) + { + style.BlendOp = STYLEOP_RevSub; + } + + stencilling = false; + quad.Palette = nullptr; + quad.Flags = 0; + quad.Desat = 0; + + switch (style.BlendOp) + { + default: + case STYLEOP_Add: quad.BlendOp = GL_FUNC_ADD; break; + case STYLEOP_Sub: quad.BlendOp = GL_FUNC_SUBTRACT; break; + case STYLEOP_RevSub: quad.BlendOp = GL_FUNC_REVERSE_SUBTRACT; break; + case STYLEOP_None: return false; + } + quad.SrcBlend = GetStyleAlpha(style.SrcAlpha); + quad.DestBlend = GetStyleAlpha(style.DestAlpha); + + if (style.Flags & STYLEF_InvertOverlay) + { + // Only the overlay color is inverted, not the overlay alpha. + parms.colorOverlay = ColorARGB(APART(parms.colorOverlay), + 255 - RPART(parms.colorOverlay), 255 - GPART(parms.colorOverlay), + 255 - BPART(parms.colorOverlay)); + } + + SetColorOverlay(parms.colorOverlay, alpha, color0, color1); + + if (style.Flags & STYLEF_ColorIsFixed) + { + if (style.Flags & STYLEF_InvertSource) + { // Since the source color is a constant, we can invert it now + // without spending time doing it in the shader. + parms.fillcolor = ColorXRGB(255 - RPART(parms.fillcolor), + 255 - GPART(parms.fillcolor), 255 - BPART(parms.fillcolor)); + } + // Set up the color mod to replace the color from the image data. + color0 = (color0 & ColorRGBA(0, 0, 0, 255)) | (parms.fillcolor & ColorRGBA(255, 255, 255, 0)); + color1 &= ColorRGBA(0, 0, 0, 255); + + if (style.Flags & STYLEF_RedIsAlpha) + { + // Note that if the source texture is paletted, the palette is ignored. + quad.Flags = 0; + quad.ShaderNum = BQS_RedToAlpha; + } + else if (fmt == GL_R8) + { + quad.Flags = BQF_GamePalette; + quad.ShaderNum = BQS_PalTex; + } + else + { + quad.Flags = 0; + quad.ShaderNum = BQS_Plain; + } + } + else + { + if (style.Flags & STYLEF_RedIsAlpha) + { + quad.Flags = 0; + quad.ShaderNum = BQS_RedToAlpha; + } + else if (fmt == GL_R8) + { + if (parms.remap != nullptr) + { + quad.Flags = BQF_CustomPalette; + quad.Palette = reinterpret_cast(parms.remap->GetNative()); + quad.ShaderNum = BQS_PalTex; + } + else if (tex->IsGray) + { + quad.Flags = 0; + quad.ShaderNum = BQS_Plain; + } + else + { + quad.Flags = BQF_GamePalette; + quad.ShaderNum = BQS_PalTex; + } + } + else + { + quad.Flags = 0; + quad.ShaderNum = BQS_Plain; + } + if (style.Flags & STYLEF_InvertSource) + { + quad.Flags |= BQF_InvertSource; + } + + if (parms.specialcolormap != nullptr) + { // Emulate an invulnerability or similar colormap. + float *start, *end; + start = parms.specialcolormap->ColorizeStart; + end = parms.specialcolormap->ColorizeEnd; + if (quad.Flags & BQF_InvertSource) + { + quad.Flags &= ~BQF_InvertSource; + swapvalues(start, end); + } + quad.ShaderNum = BQS_SpecialColormap; + color0 = ColorRGBA(uint32_t(start[0] / 2 * 255), uint32_t(start[1] / 2 * 255), uint32_t(start[2] / 2 * 255), color0 >> 24); + color1 = ColorRGBA(uint32_t(end[0] / 2 * 255), uint32_t(end[1] / 2 * 255), uint32_t(end[2] / 2 * 255), color1 >> 24); + } + else if (parms.colormapstyle != nullptr) + { // Emulate the fading from an in-game colormap (colorized, faded, and desaturated) + if (parms.colormapstyle->Desaturate != 0) + { + quad.Flags |= BQF_Desaturated; + } + quad.ShaderNum = BQS_InGameColormap; + quad.Desat = parms.colormapstyle->Desaturate; + color0 = ColorARGB(color1 >> 24, + parms.colormapstyle->Color.r, + parms.colormapstyle->Color.g, + parms.colormapstyle->Color.b); + double fadelevel = parms.colormapstyle->FadeLevel; + color1 = ColorARGB(uint32_t((1 - fadelevel) * 255), + uint32_t(parms.colormapstyle->Fade.r * fadelevel), + uint32_t(parms.colormapstyle->Fade.g * fadelevel), + uint32_t(parms.colormapstyle->Fade.b * fadelevel)); + } + } + + // For unmasked images, force the alpha from the image data to be ignored. + if (!parms.masked && quad.ShaderNum != BQS_InGameColormap) + { + color0 = (color0 & ColorRGBA(255, 255, 255, 0)) | ColorValue(0, 0, 0, alpha); + color1 &= ColorRGBA(255, 255, 255, 0); + + // If our alpha is one and we are doing normal adding, then we can turn the blend off completely. + if (quad.BlendOp == GL_FUNC_ADD && + ((alpha == 1 && quad.SrcBlend == GL_SRC_ALPHA) || quad.SrcBlend == GL_ONE) && + ((alpha == 1 && quad.DestBlend == GL_ONE_MINUS_SRC_ALPHA) || quad.DestBlend == GL_ZERO)) + { + quad.BlendOp = 0; + } + quad.Flags |= BQF_DisableAlphaTest; + } + return true; +} + +int OpenGLSWFrameBuffer::GetStyleAlpha(int type) +{ + switch (type) + { + case STYLEALPHA_Zero: return GL_ZERO; + case STYLEALPHA_One: return GL_ONE; + case STYLEALPHA_Src: return GL_SRC_ALPHA; + case STYLEALPHA_InvSrc: return GL_ONE_MINUS_SRC_ALPHA; + default: return GL_ZERO; + } +} + + +void OpenGLSWFrameBuffer::SetColorOverlay(uint32_t color, float alpha, uint32_t &color0, uint32_t &color1) +{ + if (APART(color) != 0) + { + int a = APART(color) * 256 / 255; + color0 = ColorRGBA( + (RPART(color) * a) >> 8, + (GPART(color) * a) >> 8, + (BPART(color) * a) >> 8, + 0); + a = 256 - a; + color1 = ColorRGBA(a, a, a, int(alpha * 255)); + } + else + { + color0 = 0; + color1 = ColorValue(1, 1, 1, alpha); + } +} + +void OpenGLSWFrameBuffer::EnableAlphaTest(bool enabled) +{ + if (enabled != AlphaTestEnabled) + { + AlphaTestEnabled = enabled; + //glEnable(GL_ALPHA_TEST); // To do: move to shader as this is only in the compatibility profile + } +} + +void OpenGLSWFrameBuffer::SetAlphaBlend(int op, int srcblend, int destblend) +{ + if (op == 0) + { // Disable alpha blend + if (AlphaBlendEnabled) + { + AlphaBlendEnabled = false; + glDisable(GL_BLEND); + } + } + else + { // Enable alpha blend + assert(srcblend != 0); + assert(destblend != 0); + + if (!AlphaBlendEnabled) + { + AlphaBlendEnabled = true; + glEnable(GL_BLEND); + } + if (AlphaBlendOp != op) + { + AlphaBlendOp = op; + glBlendEquation(op); + } + if (AlphaSrcBlend != srcblend || AlphaDestBlend != destblend) + { + AlphaSrcBlend = srcblend; + AlphaDestBlend = destblend; + glBlendFunc(srcblend, destblend); + } + } +} + +void OpenGLSWFrameBuffer::SetConstant(int cnum, float r, float g, float b, float a) +{ + if (Constant[cnum][0] != r || + Constant[cnum][1] != g || + Constant[cnum][2] != b || + Constant[cnum][3] != a) + { + Constant[cnum][0] = r; + Constant[cnum][1] = g; + Constant[cnum][2] = b; + Constant[cnum][3] = a; + SetPixelShaderConstantF(cnum, Constant[cnum], 1); + } +} + +void OpenGLSWFrameBuffer::SetPixelShader(HWPixelShader *shader) +{ + if (CurPixelShader != shader) + { + CurPixelShader = shader; + SetHWPixelShader(shader); + } +} + +void OpenGLSWFrameBuffer::SetTexture(int tnum, HWTexture *texture) +{ + assert(unsigned(tnum) < countof(Texture)); + if (texture) + { + if (Texture[tnum] != texture || SamplerWrapS[tnum] != texture->WrapS || SamplerWrapT[tnum] != texture->WrapT) + { + Texture[tnum] = texture; + glActiveTexture(GL_TEXTURE0 + tnum); + glBindTexture(GL_TEXTURE_2D, texture->Texture); + if (Texture[tnum]->WrapS != SamplerWrapS[tnum]) + { + Texture[tnum]->WrapS = SamplerWrapS[tnum]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SamplerWrapS[tnum]); + } + if (Texture[tnum]->WrapT != SamplerWrapT[tnum]) + { + Texture[tnum]->WrapT = SamplerWrapT[tnum]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SamplerWrapT[tnum]); + } + } + } + else if (Texture[tnum] != texture) + { + Texture[tnum] = texture; + glActiveTexture(GL_TEXTURE0 + tnum); + glBindTexture(GL_TEXTURE_2D, 0); + } +} + +void OpenGLSWFrameBuffer::SetSamplerWrapS(int tnum, int mode) +{ + assert(unsigned(tnum) < countof(Texture)); + if (Texture[tnum] && SamplerWrapS[tnum] != mode) + { + SamplerWrapS[tnum] = mode; + Texture[tnum]->WrapS = mode; + glActiveTexture(GL_TEXTURE0 + tnum); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SamplerWrapS[tnum]); + } +} + +void OpenGLSWFrameBuffer::SetSamplerWrapT(int tnum, int mode) +{ + assert(unsigned(tnum) < countof(Texture)); + if (Texture[tnum] && SamplerWrapT[tnum] != mode) + { + SamplerWrapT[tnum] = mode; + Texture[tnum]->WrapT = mode; + glActiveTexture(GL_TEXTURE0 + tnum); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SamplerWrapT[tnum]); + } +} + +void OpenGLSWFrameBuffer::SetPaletteTexture(HWTexture *texture, int count, uint32_t border_color) +{ + // The pixel shader receives color indexes in the range [0.0,1.0]. + // The palette texture is also addressed in the range [0.0,1.0], + // HOWEVER the coordinate 1.0 is the right edge of the texture and + // not actually the texture itself. We need to scale and shift + // the palette indexes so they lie exactly in the center of each + // texel. For a normal palette with 256 entries, that means the + // range we use should be [0.5,255.5], adjusted so the coordinate + // is still within [0.0,1.0]. + // + // The constant register c2 is used to hold the multiplier in the + // x part and the adder in the y part. + float fcount = 1 / float(count); + SetConstant(PSCONST_PaletteMod, 255 * fcount, 0.5f * fcount, 0, 0); + SetTexture(1, texture); +} diff --git a/src/gl/system/gl_swframebuffer.h b/src/gl/system/gl_swframebuffer.h new file mode 100644 index 0000000000..b5631074c4 --- /dev/null +++ b/src/gl/system/gl_swframebuffer.h @@ -0,0 +1,506 @@ +#ifndef __GL_SWFRAMEBUFFER +#define __GL_SWFRAMEBUFFER + +#ifdef _WIN32 +#include "win32iface.h" +#include "win32gliface.h" +#endif + +#include "SkylineBinPack.h" +#include "textures.h" + +#include + +class FGLDebug; + +#ifdef _WIN32 +class OpenGLSWFrameBuffer : public Win32GLFrameBuffer +{ + typedef Win32GLFrameBuffer Super; + DECLARE_CLASS(OpenGLSWFrameBuffer, Win32GLFrameBuffer) +#else +#include "sdlglvideo.h" +class OpenGLSWFrameBuffer : public SDLGLFB +{ +// typedef SDLGLFB Super; //[C]commented, DECLARE_CLASS defines this in linux + DECLARE_CLASS(OpenGLSWFrameBuffer, SDLGLFB) +#endif + + +public: + + explicit OpenGLSWFrameBuffer() {} + OpenGLSWFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen, bool bgra); + ~OpenGLSWFrameBuffer(); + + + bool IsValid() override; + bool Lock(bool buffered) override; + void Unlock() override; + void Update() override; + PalEntry *GetPalette() override; + void GetFlashedPalette(PalEntry palette[256]) override; + void UpdatePalette() override; + bool SetGamma(float gamma) override; + bool SetFlash(PalEntry rgb, int amount) override; + void GetFlash(PalEntry &rgb, int &amount) override; + int GetPageCount() override; + void SetVSync(bool vsync) override; + void NewRefreshRate() override; + void GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) override; + void ReleaseScreenshotBuffer() override; + void SetBlendingRect(int x1, int y1, int x2, int y2) override; + bool Begin2D(bool copy3d) override; + void DrawBlendingRect() override; + FNativeTexture *CreateTexture(FTexture *gametex, bool wrapping) override; + FNativePalette *CreatePalette(FRemapTable *remap) override; + void DrawTextureParms(FTexture *img, DrawParms &parms) override; + void Clear(int left, int top, int right, int bottom, int palcolor, uint32_t color) override; + void Dim(PalEntry color, float amount, int x1, int y1, int w, int h) override; + void FlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin) override; + void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32_t realcolor) override; + void DrawPixel(int x, int y, int palcolor, uint32_t rgbcolor) override; + void FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) override; + bool WipeStartScreen(int type) override; + void WipeEndScreen() override; + bool WipeDo(int ticks) override; + void WipeCleanup() override; + +#ifdef WIN32 + void PaletteChanged() override { } + int QueryNewPalette() override { return 0; } + void Blank() override { } + bool PaintToWindow() override; + bool Is8BitMode() override { return false; } + int GetTrueHeight() override { return TrueHeight; } +#endif + +private: + struct FBVERTEX + { + float x, y, z, rhw; + uint32_t color0, color1; + float tu, tv; + }; + + struct BURNVERTEX + { + float x, y, z, rhw; + float tu0, tv0; + float tu1, tv1; + }; + + enum + { + PSCONST_Desaturation, + PSCONST_PaletteMod, + PSCONST_Weights, + PSCONST_Gamma, + PSCONST_ScreenSize, + NumPSCONST + }; + + struct GammaRamp + { + uint16_t red[256], green[256], blue[256]; + }; + + struct LTRBRect + { + int left, top, right, bottom; + }; + + class HWTexture + { + public: + HWTexture() { Buffers[0] = 0; Buffers[1] = 0; } + ~HWTexture(); + + int Texture = 0; + int Buffers[2]; + int CurrentBuffer = 0; + int WrapS = 0; + int WrapT = 0; + int Format = 0; + + std::vector MapBuffer; + }; + + class HWFrameBuffer + { + public: + ~HWFrameBuffer(); + + int Framebuffer = 0; + HWTexture *Texture = nullptr; + }; + + + class HWVertexBuffer + { + public: + ~HWVertexBuffer(); + + FBVERTEX *Lock(); + void Unlock(); + + int VertexArray = 0; + int Buffer = 0; + int Size = 0; + }; + + class HWIndexBuffer + { + public: + ~HWIndexBuffer(); + + uint16_t *Lock(); + void Unlock(); + + int Buffer = 0; + int Size = 0; + + private: + int LockedOldBinding = 0; + }; + + class HWPixelShader + { + public: + ~HWPixelShader(); + + int Program = 0; + int VertexShader = 0; + int FragmentShader = 0; + + int ConstantLocations[NumPSCONST]; + int ImageLocation = -1; + int PaletteLocation = -1; + int NewScreenLocation = -1; + int BurnLocation = -1; + }; + + bool CreateFrameBuffer(const FString &name, int width, int height, HWFrameBuffer **outFramebuffer); + bool CreatePixelShader(FString vertexsrc, FString fragmentsrc, const FString &defines, HWPixelShader **outShader); + bool CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer); + bool CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer); + bool CreateTexture(const FString &name, int width, int height, int levels, int format, HWTexture **outTexture); + void SetGammaRamp(const GammaRamp *ramp); + void SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount); + void SetHWPixelShader(HWPixelShader *shader); + void SetStreamSource(HWVertexBuffer *vertexBuffer); + void SetIndices(HWIndexBuffer *indexBuffer); + void DrawTriangleFans(int count, const FBVERTEX *vertices); + void DrawTriangleFans(int count, const BURNVERTEX *vertices); + void DrawPoints(int count, const FBVERTEX *vertices); + void DrawLineList(int count); + void DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount); + void Present(); + + static void BgraToRgba(uint32_t *dest, const uint32_t *src, int width, int height, int srcpitch); + + void BindFBBuffer(); + void *MappedMemBuffer = nullptr; + bool UseMappedMemBuffer = true; + + static uint32_t ColorARGB(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | ((b) & 0xff); } + static uint32_t ColorRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { return ColorARGB(a, r, g, b); } + static uint32_t ColorXRGB(uint32_t r, uint32_t g, uint32_t b) { return ColorARGB(0xff, r, g, b); } + static uint32_t ColorValue(float r, float g, float b, float a) { return ColorRGBA((uint32_t)(r * 255.0f), (uint32_t)(g * 255.0f), (uint32_t)(b * 255.0f), (uint32_t)(a * 255.0f)); } + + static void *MapBuffer(int target, int size); + + // The number of points for the vertex buffer. + enum { NUM_VERTS = 10240 }; + + // The number of indices for the index buffer. + enum { NUM_INDEXES = ((NUM_VERTS * 6) / 4) }; + + // The number of quads we can batch together. + enum { MAX_QUAD_BATCH = (NUM_INDEXES / 6) }; + + // The default size for a texture atlas. + enum { DEF_ATLAS_WIDTH = 512 }; + enum { DEF_ATLAS_HEIGHT = 512 }; + + // TYPES ------------------------------------------------------------------- + + struct Atlas; + + struct PackedTexture + { + Atlas *Owner; + + PackedTexture **Prev, *Next; + + // Pixels this image covers + LTRBRect Area; + + // Texture coordinates for this image + float Left, Top, Right, Bottom; + + // Texture has extra space on the border? + bool Padded; + }; + + struct Atlas + { + Atlas(OpenGLSWFrameBuffer *fb, int width, int height, int format); + ~Atlas(); + + PackedTexture *AllocateImage(const Rect &rect, bool padded); + void FreeBox(PackedTexture *box); + + SkylineBinPack Packer; + Atlas *Next; + HWTexture *Tex; + int Format; + PackedTexture *UsedList; // Boxes that contain images + int Width, Height; + bool OneUse; + }; + + class OpenGLTex : public FNativeTexture + { + public: + OpenGLTex(FTexture *tex, OpenGLSWFrameBuffer *fb, bool wrapping); + ~OpenGLTex(); + + FTexture *GameTex; + PackedTexture *Box; + + OpenGLTex **Prev; + OpenGLTex *Next; + + bool IsGray; + + bool Create(OpenGLSWFrameBuffer *fb, bool wrapping); + bool Update(); + bool CheckWrapping(bool wrapping); + int GetTexFormat(); + FTextureFormat ToTexFmt(int fmt); + }; + + class OpenGLPal : public FNativePalette + { + public: + OpenGLPal(FRemapTable *remap, OpenGLSWFrameBuffer *fb); + ~OpenGLPal(); + + OpenGLPal **Prev; + OpenGLPal *Next; + + HWTexture *Tex; + uint32_t BorderColor; + bool DoColorSkip; + + bool Update(); + + FRemapTable *Remap; + int RoundedPaletteSize; + }; + + // Flags for a buffered quad + enum + { + BQF_GamePalette = 1, + BQF_CustomPalette = 7, + BQF_Paletted = 7, + BQF_Bilinear = 8, + BQF_WrapUV = 16, + BQF_InvertSource = 32, + BQF_DisableAlphaTest = 64, + BQF_Desaturated = 128, + }; + + // Shaders for a buffered quad + enum + { + BQS_PalTex, + BQS_Plain, + BQS_RedToAlpha, + BQS_ColorOnly, + BQS_SpecialColormap, + BQS_InGameColormap, + }; + + struct BufferedTris + { + uint8_t Flags; + uint8_t ShaderNum; + int BlendOp; + int SrcBlend; + int DestBlend; + + uint8_t Desat; + OpenGLPal *Palette; + HWTexture *Texture; + uint16_t NumVerts; // Number of _unique_ vertices used by this set. + uint16_t NumTris; // Number of triangles used by this set. + + void ClearSetup() + { + Flags = 0; + ShaderNum = 0; + BlendOp = 0; + SrcBlend = 0; + DestBlend = 0; + } + + bool IsSameSetup(const BufferedTris &other) const + { + return Flags == other.Flags && ShaderNum == other.ShaderNum && BlendOp == other.BlendOp && SrcBlend == other.SrcBlend && DestBlend == other.DestBlend; + } + }; + + enum + { + SHADER_NormalColor, + SHADER_NormalColorPal, + SHADER_NormalColorInv, + SHADER_NormalColorPalInv, + + SHADER_RedToAlpha, + SHADER_RedToAlphaInv, + + SHADER_VertexColor, + + SHADER_SpecialColormap, + SHADER_SpecialColormapPal, + + SHADER_InGameColormap, + SHADER_InGameColormapDesat, + SHADER_InGameColormapInv, + SHADER_InGameColormapInvDesat, + SHADER_InGameColormapPal, + SHADER_InGameColormapPalDesat, + SHADER_InGameColormapPalInv, + SHADER_InGameColormapPalInvDesat, + + SHADER_BurnWipe, + SHADER_GammaCorrection, + + NUM_SHADERS + }; + static const char *const ShaderDefines[NUM_SHADERS]; + + void Flip(); + void SetInitialState(); + bool CreateResources(); + void ReleaseResources(); + bool LoadShaders(); + bool CreateFBTexture(); + bool CreatePaletteTexture(); + bool CreateVertexes(); + void UploadPalette(); + void CalcFullscreenCoords(FBVERTEX verts[4], bool viewarea_only, uint32_t color0, uint32_t color1) const; + bool Reset(); + HWTexture *CopyCurrentScreen(); + void ReleaseDefaultPoolItems(); + void KillNativePals(); + void KillNativeTexs(); + PackedTexture *AllocPackedTexture(int width, int height, bool wrapping, int format); + void DrawPackedTextures(int packnum); + void DrawLetterbox(int x, int y, int width, int height); + void Draw3DPart(bool copy3d); + bool SetStyle(OpenGLTex *tex, DrawParms &parms, uint32_t &color0, uint32_t &color1, BufferedTris &quad); + static int GetStyleAlpha(int type); + static void SetColorOverlay(uint32_t color, float alpha, uint32_t &color0, uint32_t &color1); + void AddColorOnlyQuad(int left, int top, int width, int height, uint32_t color); + void AddColorOnlyRect(int left, int top, int width, int height, uint32_t color); + void CheckQuadBatch(int numtris = 2, int numverts = 4); + void BeginQuadBatch(); + void EndQuadBatch(); + void BeginLineBatch(); + void EndLineBatch(); + void EndBatch(); + + // State + void EnableAlphaTest(bool enabled); + void SetAlphaBlend(int op, int srcblend = 0, int destblend = 0); + void SetConstant(int cnum, float r, float g, float b, float a); + void SetPixelShader(HWPixelShader *shader); + void SetTexture(int tnum, HWTexture *texture); + void SetSamplerWrapS(int tnum, int mode); + void SetSamplerWrapT(int tnum, int mode); + void SetPaletteTexture(HWTexture *texture, int count, uint32_t border_color); + + template static void SafeRelease(T &x) { if (x != nullptr) { delete x; x = nullptr; } } + + bool Valid = false; + std::shared_ptr Debug; + + std::unique_ptr StreamVertexBuffer, StreamVertexBufferBurn; + float ShaderConstants[NumPSCONST * 4]; + HWPixelShader *CurrentShader = nullptr; + + HWFrameBuffer *OutputFB = nullptr; + + bool AlphaTestEnabled = false; + bool AlphaBlendEnabled = false; + int AlphaBlendOp = 0; + int AlphaSrcBlend = 0; + int AlphaDestBlend = 0; + float Constant[3][4]; + uint32_t CurBorderColor; + HWPixelShader *CurPixelShader; + HWTexture *Texture[5]; + int SamplerWrapS[5], SamplerWrapT[5]; + + PalEntry SourcePalette[256]; + uint32_t BorderColor; + uint32_t FlashColor0, FlashColor1; + PalEntry FlashColor; + int FlashAmount; + int TrueHeight; + int PixelDoubling; + float Gamma; + bool UpdatePending; + bool NeedPalUpdate; + bool NeedGammaUpdate; + LTRBRect BlendingRect; + int In2D; + bool InScene; + bool GatheringWipeScreen; + bool AALines; + uint8_t BlockNum; + OpenGLPal *Palettes = nullptr; + OpenGLTex *Textures = nullptr; + Atlas *Atlases = nullptr; + + HWTexture *FBTexture = nullptr; + HWTexture *PaletteTexture = nullptr; + HWTexture *ScreenshotTexture = nullptr; + + HWVertexBuffer *VertexBuffer = nullptr; + FBVERTEX *VertexData = nullptr; + HWIndexBuffer *IndexBuffer = nullptr; + uint16_t *IndexData = nullptr; + BufferedTris *QuadExtra = nullptr; + int VertexPos; + int IndexPos; + int QuadBatchPos; + enum { BATCH_None, BATCH_Quads, BATCH_Lines } BatchType; + + HWPixelShader *Shaders[NUM_SHADERS]; + + HWTexture *InitialWipeScreen = nullptr, *FinalWipeScreen = nullptr; + + class Wiper + { + public: + virtual ~Wiper(); + virtual bool Run(int ticks, OpenGLSWFrameBuffer *fb) = 0; + + void DrawScreen(OpenGLSWFrameBuffer *fb, HWTexture *tex, int blendop = 0, uint32_t color0 = 0, uint32_t color1 = 0xFFFFFFF); + }; + + class Wiper_Melt; friend class Wiper_Melt; + class Wiper_Burn; friend class Wiper_Burn; + class Wiper_Crossfade; friend class Wiper_Crossfade; + + Wiper *ScreenWipe; +}; + + +#endif //__GL_SWFRAMEBUFFER diff --git a/src/gl/system/gl_swwipe.cpp b/src/gl/system/gl_swwipe.cpp new file mode 100644 index 0000000000..930278ac91 --- /dev/null +++ b/src/gl/system/gl_swwipe.cpp @@ -0,0 +1,585 @@ +/* +** gl_swwipe.cpp +** Implements the different screen wipes using OpenGL calls. +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "gl/system/gl_system.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 "c_dispatch.h" +#include "templates.h" +#include "i_system.h" +#include "i_video.h" +#include "v_pfx.h" +#include "stats.h" +#include "doomerrors.h" +#include "r_data/r_translate.h" +#include "f_wipe.h" +#include "sbar.h" +#include "w_wad.h" +#include "r_data/colormaps.h" + +#include "gl/system/gl_interface.h" +#include "gl/system/gl_swframebuffer.h" +#include "gl/data/gl_data.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/gl_functions.h" +#include "gl_debug.h" +#include "m_random.h" + +class OpenGLSWFrameBuffer::Wiper_Crossfade : public OpenGLSWFrameBuffer::Wiper +{ +public: + Wiper_Crossfade(); + bool Run(int ticks, OpenGLSWFrameBuffer *fb); + +private: + int Clock; +}; + +class OpenGLSWFrameBuffer::Wiper_Melt : public OpenGLSWFrameBuffer::Wiper +{ +public: + Wiper_Melt(); + bool Run(int ticks, OpenGLSWFrameBuffer *fb); + +private: + // Match the strip sizes that oldschool Doom used. + static const int WIDTH = 160, HEIGHT = 200; + int y[WIDTH]; +}; + +class OpenGLSWFrameBuffer::Wiper_Burn : public OpenGLSWFrameBuffer::Wiper +{ +public: + Wiper_Burn(OpenGLSWFrameBuffer *fb); + ~Wiper_Burn(); + bool Run(int ticks, OpenGLSWFrameBuffer *fb); + +private: + static const int WIDTH = 64, HEIGHT = 64; + uint8_t BurnArray[WIDTH * (HEIGHT + 5)]; + HWTexture *BurnTexture; + int Density; + int BurnTime; +}; + +//========================================================================== +// +// OpenGLSWFrameBuffer :: 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. +// +// In fullscreen mode, we use GetFrontBufferData() to grab the data that +// is visible on screen right now. +// +// In windowed mode, we can't do that because we'll get the whole desktop. +// Instead, we can conveniently use the TempRenderTexture, which is normally +// used for gamma-correcting copying the image to the back buffer. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::WipeStartScreen(int type) +{ + if (!Accel2D) + { + return Super::WipeStartScreen(type); + } + + switch (type) + { + case wipe_Melt: + ScreenWipe = new Wiper_Melt; + break; + + case wipe_Burn: + ScreenWipe = new Wiper_Burn(this); + break; + + case wipe_Fade: + ScreenWipe = new Wiper_Crossfade; + break; + + default: + return false; + } + + InitialWipeScreen = CopyCurrentScreen(); + + // Make even fullscreen model render to the TempRenderTexture, so + // we can have a copy of the new screen readily available. + GatheringWipeScreen = true; + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeEndScreen +// +// The screen we want to animate to has just been drawn. This function is +// called in place of Update(), so it has not been Presented yet. +// +//========================================================================== + +void OpenGLSWFrameBuffer::WipeEndScreen() +{ + if (!Accel2D) + { + Super::WipeEndScreen(); + return; + } + + // Don't do anything if there is no starting point. + if (InitialWipeScreen == NULL) + { + return; + } + + // If the whole screen was drawn without 2D accel, get it in to + // video memory now. + if (!In2D) + { + Begin2D(true); + } + + EndBatch(); // Make sure all batched primitives have been drawn. + + FinalWipeScreen = CopyCurrentScreen(); + + // At this point, InitialWipeScreen holds the screen we are wiping from. + // FinalWipeScreen holds the screen we are wiping to, which may be the + // same texture as TempRenderTexture. +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: 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 OpenGLSWFrameBuffer::WipeDo(int ticks) +{ + if (!Accel2D) + { + return Super::WipeDo(ticks); + } + + // Sanity checks. + if (InitialWipeScreen == NULL || FinalWipeScreen == NULL) + { + return true; + } + if (GatheringWipeScreen) + { // This is the first time we've been called for this wipe. + GatheringWipeScreen = false; + } + else + { // This is the second or later time we've been called for this wipe. + InScene = true; + } + + In2D = 3; + + EnableAlphaTest(false); + bool done = ScreenWipe->Run(ticks, this); + return done; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeCleanup +// +// Release any resources that were specifically created for the wipe. +// +//========================================================================== + +void OpenGLSWFrameBuffer::WipeCleanup() +{ + if (ScreenWipe != NULL) + { + delete ScreenWipe; + ScreenWipe = NULL; + } + SafeRelease( InitialWipeScreen ); + SafeRelease( FinalWipeScreen ); + GatheringWipeScreen = false; + if (!Accel2D) + { + Super::WipeCleanup(); + return; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper::~Wiper() +{ +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper :: DrawScreen +// +// Draw either the initial or target screen completely to the screen. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Wiper::DrawScreen(OpenGLSWFrameBuffer *fb, HWTexture *tex, + int blendop, uint32_t color0, uint32_t color1) +{ + FBVERTEX verts[4]; + + fb->CalcFullscreenCoords(verts, false, color0, color1); + fb->SetTexture(0, tex); + fb->SetAlphaBlend(blendop, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + fb->SetPixelShader(fb->Shaders[SHADER_NormalColor]); + fb->DrawTriangleFans(2, verts); +} + +// WIPE: CROSSFADE --------------------------------------------------------- + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Crossfade Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Crossfade::Wiper_Crossfade() +: Clock(0) +{ +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Crossfade :: Run +// +// Fades the old screen into the new one over 32 ticks. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Wiper_Crossfade::Run(int ticks, OpenGLSWFrameBuffer *fb) +{ + Clock += ticks; + + // Put the initial screen back to the buffer. + DrawScreen(fb, fb->InitialWipeScreen); + + // Draw the new screen on top of it. + DrawScreen(fb, fb->FinalWipeScreen, GL_FUNC_ADD, ColorValue(0,0,0,Clock / 32.f), ColorRGBA(255,255,255,0)); + + return Clock >= 32; +} + +// WIPE: MELT -------------------------------------------------------------- + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Melt Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::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); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Melt :: Run +// +// Fades the old screen into the new one over 32 ticks. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Wiper_Melt::Run(int ticks, OpenGLSWFrameBuffer *fb) +{ + // Draw the new screen on the bottom. + DrawScreen(fb, fb->FinalWipeScreen); + + int i, dy; + int fbwidth = fb->Width; + int fbheight = fb->Height; + bool done = true; + + // 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. + LTRBRect rect; + struct Point { int x, y; } dpt; + + dpt.x = i * fbwidth / WIDTH; + dpt.y = MAX(0, y[i] * fbheight / HEIGHT); + rect.left = dpt.x; + rect.top = 0; + rect.right = (i + 1) * fbwidth / WIDTH; + rect.bottom = fbheight - dpt.y; + if (rect.bottom > rect.top) + { + fb->CheckQuadBatch(); + + BufferedTris *quad = &fb->QuadExtra[fb->QuadBatchPos]; + FBVERTEX *vert = &fb->VertexData[fb->VertexPos]; + uint16_t *index = &fb->IndexData[fb->IndexPos]; + + quad->ClearSetup(); + quad->Flags = BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + quad->Palette = NULL; + quad->Texture = fb->InitialWipeScreen; + quad->NumVerts = 4; + quad->NumTris = 2; + + // Fill the vertex buffer. + float u0 = rect.left / float(fb->Width); + float v0 = 0; + float u1 = rect.right / float(fb->Width); + float v1 = (rect.bottom - rect.top) / float(fb->Height); + + float x0 = float(rect.left); + float x1 = float(rect.right); + float y0 = float(dpt.y); + float y1 = float(fbheight); + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFF; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFF; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFF; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFF; + vert[3].tu = u0; + vert[3].tv = v1; + + // Fill the vertex index buffer. + index[0] = fb->VertexPos; + index[1] = fb->VertexPos + 1; + index[2] = fb->VertexPos + 2; + index[3] = fb->VertexPos; + index[4] = fb->VertexPos + 2; + index[5] = fb->VertexPos + 3; + + // Batch the quad. + fb->QuadBatchPos++; + fb->VertexPos += 4; + fb->IndexPos += 6; + } + } + } + } + fb->EndQuadBatch(); + return done; +} + +// WIPE: BURN -------------------------------------------------------------- + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Burn Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Burn::Wiper_Burn(OpenGLSWFrameBuffer *fb) +{ + Density = 4; + BurnTime = 0; + memset(BurnArray, 0, sizeof(BurnArray)); + if (fb->Shaders[SHADER_BurnWipe] == NULL || !fb->CreateTexture("BurnWipe", WIDTH, HEIGHT, 1, GL_R8, &BurnTexture)) + { + BurnTexture = NULL; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Burn Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Burn::~Wiper_Burn() +{ + SafeRelease( BurnTexture ); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Burn :: Run +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Wiper_Burn::Run(int ticks, OpenGLSWFrameBuffer *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); + } + + // Update the burn texture with the new burn data + + if (BurnTexture->Buffers[0] == 0) + { + glGenBuffers(2, (GLuint*)BurnTexture->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, BurnTexture->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, WIDTH * HEIGHT, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, BurnTexture->Buffers[1]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, WIDTH * HEIGHT, nullptr, GL_STREAM_DRAW); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, BurnTexture->Buffers[BurnTexture->CurrentBuffer]); + BurnTexture->CurrentBuffer = (BurnTexture->CurrentBuffer + 1) & 1; + } + + uint8_t *dest = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, WIDTH * HEIGHT, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); + if (dest) + { + memcpy(dest, BurnArray, WIDTH * HEIGHT); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, BurnTexture->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_RED, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + + // Put the initial screen back to the buffer. + DrawScreen(fb, fb->InitialWipeScreen); + + // Burn the new screen on top of it. + float right = float(fb->Width); + float bot = float(fb->Height); + + BURNVERTEX verts[4] = + { + { 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0, 0 }, + { right, 0.f, 0.f, 1.f, 1.f, 0.f, 1, 0 }, + { right, bot, 0.f, 1.f, 1.f, 1.f, 1, 1 }, + { 0.f, bot, 0.f, 1.f, 0.f, 1.f, 0, 1 } + }; + + fb->SetTexture(0, fb->FinalWipeScreen); + fb->SetTexture(1, BurnTexture); + fb->SetAlphaBlend(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + fb->SetPixelShader(fb->Shaders[SHADER_BurnWipe]); + glActiveTexture(GL_TEXTURE1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + fb->DrawTriangleFans(2, verts); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glActiveTexture(GL_TEXTURE0); + + // The fire may not always stabilize, so the wipe is forced to end + // after an arbitrary maximum time. + return done || (BurnTime > 40); +} diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp index 38a06c0872..db03f99c1b 100644 --- a/src/gl/system/gl_wipe.cpp +++ b/src/gl/system/gl_wipe.cpp @@ -25,7 +25,6 @@ */ #include "gl/system/gl_system.h" -#include "files.h" #include "f_wipe.h" #include "m_random.h" #include "w_wad.h" @@ -87,7 +86,7 @@ public: private: static const int WIDTH = 64, HEIGHT = 64; - BYTE BurnArray[WIDTH * (HEIGHT + 5)]; + uint8_t BurnArray[WIDTH * (HEIGHT + 5)]; FHardwareTexture *BurnTexture; int Density; int BurnTime; @@ -524,15 +523,15 @@ bool OpenGLFrameBuffer::Wiper_Burn::Run(int ticks, OpenGLFrameBuffer *fb) BurnTexture = new FHardwareTexture(WIDTH, HEIGHT, true); // Update the burn texture with the new burn data - BYTE rgb_buffer[WIDTH*HEIGHT*4]; + uint8_t rgb_buffer[WIDTH*HEIGHT*4]; - const BYTE *src = BurnArray; - DWORD *dest = (DWORD *)rgb_buffer; + const uint8_t *src = BurnArray; + uint32_t *dest = (uint32_t *)rgb_buffer; for (int y = HEIGHT; y != 0; --y) { for (int x = WIDTH; x != 0; --x) { - BYTE s = clamp((*src++)*2, 0, 255); + uint8_t s = clamp((*src++)*2, 0, 255); *dest++ = MAKEARGB(s,255,255,255); } } diff --git a/src/gl/textures/gl_bitmap.cpp b/src/gl/textures/gl_bitmap.cpp index f5156c7a5d..f6909c9520 100644 --- a/src/gl/textures/gl_bitmap.cpp +++ b/src/gl/textures/gl_bitmap.cpp @@ -39,7 +39,7 @@ // //=========================================================================== template -void iCopyColors(unsigned char * pout, const unsigned char * pin, int count, int step, BYTE tr, BYTE tg, BYTE tb) +void iCopyColors(unsigned char * pout, const unsigned char * pin, int count, int step, uint8_t tr, uint8_t tg, uint8_t tb) { int i; unsigned char a; @@ -58,7 +58,7 @@ void iCopyColors(unsigned char * pout, const unsigned char * pin, int count, int } } -typedef void (*CopyFunc)(unsigned char * pout, const unsigned char * pin, int count, int step, BYTE tr, BYTE tg, BYTE tb); +typedef void (*CopyFunc)(unsigned char * pout, const unsigned char * pin, int count, int step, uint8_t tr, uint8_t tg, uint8_t tb); static CopyFunc copyfuncs[]={ iCopyColors, @@ -81,15 +81,15 @@ static CopyFunc copyfuncs[]={ // //=========================================================================== void FGLBitmap::CopyPixelDataRGB(int originx, int originy, - const BYTE * patch, int srcwidth, int srcheight, int step_x, int step_y, + const uint8_t * patch, int srcwidth, int srcheight, int step_x, int step_y, int rotate, int ct, FCopyInfo *inf, int r, int g, int b) { if (ClipCopyPixelRect(&ClipRect, originx, originy, patch, srcwidth, srcheight, step_x, step_y, rotate)) { - BYTE *buffer = GetPixels() + 4*originx + Pitch*originy; + uint8_t *buffer = GetPixels() + 4*originx + Pitch*originy; for (int y=0;y 0) { diff --git a/src/gl/textures/gl_bitmap.h b/src/gl/textures/gl_bitmap.h index 3c045e5ad7..0ac0f5c670 100644 --- a/src/gl/textures/gl_bitmap.h +++ b/src/gl/textures/gl_bitmap.h @@ -14,7 +14,7 @@ public: FGLBitmap() { } - FGLBitmap(BYTE *buffer, int pitch, int width, int height) + FGLBitmap(uint8_t *buffer, int pitch, int width, int height) : FBitmap(buffer, pitch, width, height) { } @@ -25,11 +25,11 @@ public: alphatrans = _alphatrans; } - virtual void CopyPixelDataRGB(int originx, int originy, const BYTE *patch, int srcwidth, + virtual void CopyPixelDataRGB(int originx, int originy, const uint8_t *patch, int srcwidth, int srcheight, int step_x, int step_y, int rotate, int ct, FCopyInfo *inf = NULL, /* for PNG tRNS */ int r=0, int g=0, int b=0); - virtual void CopyPixelData(int originx, int originy, const BYTE * patch, int srcwidth, int srcheight, + virtual void CopyPixelData(int originx, int originy, const uint8_t * patch, int srcwidth, int srcheight, int step_x, int step_y, int rotate, PalEntry * palette, FCopyInfo *inf = NULL); }; diff --git a/src/gl/textures/gl_hirestex.cpp b/src/gl/textures/gl_hirestex.cpp index b00e550a1a..151ca5660d 100644 --- a/src/gl/textures/gl_hirestex.cpp +++ b/src/gl/textures/gl_hirestex.cpp @@ -42,6 +42,7 @@ #include "doomstat.h" #include "d_main.h" #include "zstring.h" +#include "textures.h" #ifndef _WIN32 #define _access(a,b) access(a,b) @@ -110,7 +111,7 @@ int CheckDDPK3(FTexture *tex) FString checkName; const char ** checklist; - BYTE useType=tex->UseType; + uint8_t useType=tex->UseType; if (useType==FTexture::TEX_SkinSprite || useType==FTexture::TEX_Decal || useType==FTexture::TEX_FontChar) { @@ -291,7 +292,7 @@ int CheckExternalFile(FTexture *tex, bool & hascolorkey) FString checkName; const char ** checklist; - BYTE useType=tex->UseType; + uint8_t useType=tex->UseType; if (useType==FTexture::TEX_SkinSprite || useType==FTexture::TEX_Decal || useType==FTexture::TEX_FontChar) { diff --git a/src/gl/textures/gl_hqresize.cpp b/src/gl/textures/gl_hqresize.cpp index 253d91f4b3..08a340199c 100644 --- a/src/gl/textures/gl_hqresize.cpp +++ b/src/gl/textures/gl_hqresize.cpp @@ -104,7 +104,7 @@ CUSTOM_CVAR(Int, gl_texture_hqresize_mt_height, 4, CVAR_ARCHIVE | CVAR_GLOBALCON #endif // GZ_USE_LIBDISPATCH -static void scale2x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, int inHeight ) +static void scale2x ( uint32_t* inputBuffer, uint32_t* outputBuffer, int inWidth, int inHeight ) { const int width = 2* inWidth; const int height = 2 * inHeight; @@ -117,15 +117,15 @@ static void scale2x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, in { const int jMinus = (j > 0) ? (j-1) : 0; const int jPlus = (j < inHeight - 1 ) ? (j+1) : j; - const uint32 A = inputBuffer[ iMinus +inWidth*jMinus]; - const uint32 B = inputBuffer[ iMinus +inWidth*j ]; - const uint32 C = inputBuffer[ iMinus +inWidth*jPlus]; - const uint32 D = inputBuffer[ i +inWidth*jMinus]; - const uint32 E = inputBuffer[ i +inWidth*j ]; - const uint32 F = inputBuffer[ i +inWidth*jPlus]; - const uint32 G = inputBuffer[ iPlus +inWidth*jMinus]; - const uint32 H = inputBuffer[ iPlus +inWidth*j ]; - const uint32 I = inputBuffer[ iPlus +inWidth*jPlus]; + const uint32_t A = inputBuffer[ iMinus +inWidth*jMinus]; + const uint32_t B = inputBuffer[ iMinus +inWidth*j ]; + const uint32_t C = inputBuffer[ iMinus +inWidth*jPlus]; + const uint32_t D = inputBuffer[ i +inWidth*jMinus]; + const uint32_t E = inputBuffer[ i +inWidth*j ]; + const uint32_t F = inputBuffer[ i +inWidth*jPlus]; + const uint32_t G = inputBuffer[ iPlus +inWidth*jMinus]; + const uint32_t H = inputBuffer[ iPlus +inWidth*j ]; + const uint32_t I = inputBuffer[ iPlus +inWidth*jPlus]; if (B != H && D != F) { outputBuffer[2*i + width*2*j ] = D == B ? D : E; outputBuffer[2*i + width*(2*j+1)] = B == F ? F : E; @@ -141,7 +141,7 @@ static void scale2x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, in } } -static void scale3x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, int inHeight ) +static void scale3x ( uint32_t* inputBuffer, uint32_t* outputBuffer, int inWidth, int inHeight ) { const int width = 3* inWidth; const int height = 3 * inHeight; @@ -154,15 +154,15 @@ static void scale3x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, in { const int jMinus = (j > 0) ? (j-1) : 0; const int jPlus = (j < inHeight - 1 ) ? (j+1) : j; - const uint32 A = inputBuffer[ iMinus +inWidth*jMinus]; - const uint32 B = inputBuffer[ iMinus +inWidth*j ]; - const uint32 C = inputBuffer[ iMinus +inWidth*jPlus]; - const uint32 D = inputBuffer[ i +inWidth*jMinus]; - const uint32 E = inputBuffer[ i +inWidth*j ]; - const uint32 F = inputBuffer[ i +inWidth*jPlus]; - const uint32 G = inputBuffer[ iPlus +inWidth*jMinus]; - const uint32 H = inputBuffer[ iPlus +inWidth*j ]; - const uint32 I = inputBuffer[ iPlus +inWidth*jPlus]; + const uint32_t A = inputBuffer[ iMinus +inWidth*jMinus]; + const uint32_t B = inputBuffer[ iMinus +inWidth*j ]; + const uint32_t C = inputBuffer[ iMinus +inWidth*jPlus]; + const uint32_t D = inputBuffer[ i +inWidth*jMinus]; + const uint32_t E = inputBuffer[ i +inWidth*j ]; + const uint32_t F = inputBuffer[ i +inWidth*jPlus]; + const uint32_t G = inputBuffer[ iPlus +inWidth*jMinus]; + const uint32_t H = inputBuffer[ iPlus +inWidth*j ]; + const uint32_t I = inputBuffer[ iPlus +inWidth*jPlus]; if (B != H && D != F) { outputBuffer[3*i + width*3*j ] = D == B ? D : E; outputBuffer[3*i + width*(3*j+1)] = (D == B && E != C) || (B == F && E != A) ? B : E; @@ -188,21 +188,21 @@ static void scale3x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, in } } -static void scale4x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, int inHeight ) +static void scale4x ( uint32_t* inputBuffer, uint32_t* outputBuffer, int inWidth, int inHeight ) { int width = 2* inWidth; int height = 2 * inHeight; - uint32 * buffer2x = new uint32[width*height]; + uint32_t * buffer2x = new uint32_t[width*height]; - scale2x ( reinterpret_cast ( inputBuffer ), reinterpret_cast ( buffer2x ), inWidth, inHeight ); + scale2x ( reinterpret_cast ( inputBuffer ), reinterpret_cast ( buffer2x ), inWidth, inHeight ); width *= 2; height *= 2; - scale2x ( reinterpret_cast ( buffer2x ), reinterpret_cast ( outputBuffer ), 2*inWidth, 2*inHeight ); + scale2x ( reinterpret_cast ( buffer2x ), reinterpret_cast ( outputBuffer ), 2*inWidth, 2*inHeight ); delete[] buffer2x; } -static unsigned char *scaleNxHelper( void (*scaleNxFunction) ( uint32* , uint32* , int , int), +static unsigned char *scaleNxHelper( void (*scaleNxFunction) ( uint32_t* , uint32_t* , int , int), const int N, unsigned char *inputBuffer, const int inWidth, @@ -214,7 +214,7 @@ static unsigned char *scaleNxHelper( void (*scaleNxFunction) ( uint32* , uint32* outHeight = N *inHeight; unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; - scaleNxFunction ( reinterpret_cast ( inputBuffer ), reinterpret_cast ( newBuffer ), inWidth, inHeight ); + scaleNxFunction ( reinterpret_cast ( inputBuffer ), reinterpret_cast ( newBuffer ), inWidth, inHeight ); delete[] inputBuffer; return newBuffer; } diff --git a/src/gl/textures/gl_material.cpp b/src/gl/textures/gl_material.cpp index 1ebc1c4885..b4a13af7ce 100644 --- a/src/gl/textures/gl_material.cpp +++ b/src/gl/textures/gl_material.cpp @@ -130,7 +130,7 @@ unsigned char *FGLTexture::LoadHiresTexture(FTexture *tex, int *width, int *heig { // This is a crappy Doomsday color keyed image // We have to remove the key manually. :( - DWORD * dwdata=(DWORD*)buffer; + uint32_t * dwdata=(uint32_t*)buffer; for (int i=(w*h);i>0;i--) { if (dwdata[i]==0xffffff00 || dwdata[i]==0xffff00ff) dwdata[i]=0; @@ -285,7 +285,7 @@ const FHardwareTexture *FGLTexture::Bind(int texunit, int clampmode, int transla if (translation <= 0) translation = -translation; else { - alphatrans = (gl.legacyMode && DWORD(translation) == TRANSLATION(TRANSLATION_Standard, 8)); + alphatrans = (gl.legacyMode && uint32_t(translation) == TRANSLATION(TRANSLATION_Standard, 8)); translation = GLTranslationPalette::GetInternalTranslation(translation); } @@ -319,10 +319,10 @@ const FHardwareTexture *FGLTexture::Bind(int texunit, int clampmode, int transla // need to do software warping FWarpTexture *wt = static_cast(tex); unsigned char *warpbuffer = new unsigned char[w*h*4]; - WarpBuffer((DWORD*)warpbuffer, (const DWORD*)buffer, w, h, wt->WidthOffsetMultiplier, wt->HeightOffsetMultiplier, r_FrameTime, wt->Speed, tex->bWarped); + WarpBuffer((uint32_t*)warpbuffer, (const uint32_t*)buffer, w, h, wt->WidthOffsetMultiplier, wt->HeightOffsetMultiplier, r_viewpoint.FrameTime, wt->Speed, tex->bWarped); delete[] buffer; buffer = warpbuffer; - wt->GenTime = r_FrameTime; + wt->GenTime = r_viewpoint.FrameTime; } tex->ProcessData(buffer, w, h, false); } diff --git a/src/gl/textures/gl_material.h b/src/gl/textures/gl_material.h index 671d8c694b..f93e784f4d 100644 --- a/src/gl/textures/gl_material.h +++ b/src/gl/textures/gl_material.h @@ -65,7 +65,7 @@ private: bool bHasColorkey; // only for hires bool bExpandFlag; - BYTE lastSampler; + uint8_t lastSampler; int lastTranslation; unsigned char * LoadHiresTexture(FTexture *hirescheck, int *width, int *height); diff --git a/src/gl/textures/gl_samplers.cpp b/src/gl/textures/gl_samplers.cpp index d9d2d07005..df1d105397 100644 --- a/src/gl/textures/gl_samplers.cpp +++ b/src/gl/textures/gl_samplers.cpp @@ -84,7 +84,7 @@ void FSamplerManager::UnbindAll() } } -BYTE FSamplerManager::Bind(int texunit, int num, int lastval) +uint8_t FSamplerManager::Bind(int texunit, int num, int lastval) { if (gl.flags & RFL_SAMPLER_OBJECTS) { diff --git a/src/gl/textures/gl_samplers.h b/src/gl/textures/gl_samplers.h index b74d49a337..0783e5aefe 100644 --- a/src/gl/textures/gl_samplers.h +++ b/src/gl/textures/gl_samplers.h @@ -16,7 +16,7 @@ public: FSamplerManager(); ~FSamplerManager(); - BYTE Bind(int texunit, int num, int lastval); + uint8_t Bind(int texunit, int num, int lastval); void SetTextureFilterMode(); diff --git a/src/gl/textures/gl_skyboxtexture.cpp b/src/gl/textures/gl_skyboxtexture.cpp index 2f7328e281..acf74da4b0 100644 --- a/src/gl/textures/gl_skyboxtexture.cpp +++ b/src/gl/textures/gl_skyboxtexture.cpp @@ -59,7 +59,7 @@ FSkyBox::~FSkyBox() // //----------------------------------------------------------------------------- -const BYTE *FSkyBox::GetColumn (unsigned int column, const Span **spans_out) +const uint8_t *FSkyBox::GetColumn (unsigned int column, const Span **spans_out) { if (faces[0]) return faces[0]->GetColumn(column, spans_out); return NULL; @@ -71,7 +71,7 @@ const BYTE *FSkyBox::GetColumn (unsigned int column, const Span **spans_out) // //----------------------------------------------------------------------------- -const BYTE *FSkyBox::GetPixels () +const uint8_t *FSkyBox::GetPixels () { if (faces[0]) return faces[0]->GetPixels(); return NULL; diff --git a/src/gl/textures/gl_skyboxtexture.h b/src/gl/textures/gl_skyboxtexture.h index 28a052be94..deca1496df 100644 --- a/src/gl/textures/gl_skyboxtexture.h +++ b/src/gl/textures/gl_skyboxtexture.h @@ -16,8 +16,8 @@ public: FSkyBox(); ~FSkyBox(); - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); + const uint8_t *GetColumn (unsigned int column, const Span **spans_out); + const uint8_t *GetPixels (); int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf); bool UseBasePalette(); void Unload (); diff --git a/src/gl/textures/gl_texture.cpp b/src/gl/textures/gl_texture.cpp index 2b338513a5..cc24f054d8 100644 --- a/src/gl/textures/gl_texture.cpp +++ b/src/gl/textures/gl_texture.cpp @@ -31,6 +31,8 @@ #include "colormatcher.h" #include "r_data/r_translate.h" #include "c_dispatch.h" +#include "r_state.h" +#include "actor.h" #ifdef _WIN32 #include "win32gliface.h" #endif @@ -42,6 +44,9 @@ #include "gl/textures/gl_texture.h" #include "gl/textures/gl_material.h" #include "gl/textures/gl_samplers.h" +#include "gl/textures/gl_skyboxtexture.h" +#include "gl/textures/gl_translate.h" +#include "gl/models/gl_models.h" //========================================================================== // @@ -167,7 +172,7 @@ void gl_GenerateGlobalBrightmapFromColormap() // component becomes one. // //=========================================================================== -static PalEntry averageColor(const DWORD *data, int size, int maxout) +static PalEntry averageColor(const uint32_t *data, int size, int maxout) { int i; unsigned int r, g, b; @@ -267,7 +272,7 @@ void FTexture::CreateDefaultBrightmap() ) { // May have one - let's check when we use this texture - const BYTE *texbuf = GetPixels(); + const uint8_t *texbuf = GetPixels(); const int white = ColorMatcher.Pick(255,255,255); int size = GetWidth() * GetHeight(); @@ -311,7 +316,7 @@ void FTexture::GetGlowColor(float *data) if (buffer) { - gl_info.GlowColor = averageColor((DWORD *) buffer, w*h, 153); + gl_info.GlowColor = averageColor((uint32_t *) buffer, w*h, 153); delete[] buffer; } @@ -426,10 +431,10 @@ void FTexture::CheckTrans(unsigned char * buffer, int size, int trans) gl_info.mIsTransparent = trans; if (trans == -1) { - DWORD * dwbuf = (DWORD*)buffer; + uint32_t * dwbuf = (uint32_t*)buffer; for(int i=0;i>24; + uint32_t alpha = dwbuf[i]>>24; if (alpha != 0xff && alpha != 0) { @@ -457,7 +462,7 @@ void FTexture::CheckTrans(unsigned char * buffer, int size, int trans) #define SOME_MASK 0x00ffffff #endif -#define CHKPIX(ofs) (l1[(ofs)*4+MSB]==255 ? (( ((DWORD*)l1)[0] = ((DWORD*)l1)[ofs]&SOME_MASK), trans=true ) : false) +#define CHKPIX(ofs) (l1[(ofs)*4+MSB]==255 ? (( ((uint32_t*)l1)[0] = ((uint32_t*)l1)[ofs]&SOME_MASK), trans=true ) : false) bool FTexture::SmoothEdges(unsigned char * buffer,int w, int h) { @@ -556,13 +561,13 @@ FBrightmapTexture::~FBrightmapTexture () { } -const BYTE *FBrightmapTexture::GetColumn (unsigned int column, const Span **spans_out) +const uint8_t *FBrightmapTexture::GetColumn (unsigned int column, const Span **spans_out) { // not needed return NULL; } -const BYTE *FBrightmapTexture::GetPixels () +const uint8_t *FBrightmapTexture::GetPixels () { // not needed return NULL; @@ -726,6 +731,189 @@ void gl_ParseDetailTexture(FScanner &sc) } +//========================================================================== +// +// DFrameBuffer :: PrecacheTexture +// +//========================================================================== + +static void PrecacheTexture(FTexture *tex, int cache) +{ + if (cache & (FTextureManager::HIT_Wall | FTextureManager::HIT_Flat | FTextureManager::HIT_Sky)) + { + FMaterial * gltex = FMaterial::ValidateTexture(tex, false); + if (gltex) gltex->Precache(); + } + else + { + // make sure that software pixel buffers do not stick around for unneeded textures. + tex->Unload(); + } +} + +//========================================================================== +// +// DFrameBuffer :: PrecacheSprite +// +//========================================================================== + +static void PrecacheSprite(FTexture *tex, SpriteHits &hits) +{ + FMaterial * gltex = FMaterial::ValidateTexture(tex, true); + if (gltex) gltex->PrecacheList(hits); +} + +//========================================================================== +// +// DFrameBuffer :: Precache +// +//========================================================================== + +void gl_PrecacheTexture(uint8_t *texhitlist, TMap &actorhitlist) +{ + SpriteHits *spritelist = new SpriteHits[sprites.Size()]; + SpriteHits **spritehitlist = new SpriteHits*[TexMan.NumTextures()]; + TMap::Iterator it(actorhitlist); + TMap::Pair *pair; + uint8_t *modellist = new uint8_t[Models.Size()]; + memset(modellist, 0, Models.Size()); + memset(spritehitlist, 0, sizeof(SpriteHits**) * TexMan.NumTextures()); + + // this isn't done by the main code so it needs to be done here first: + // check skybox textures and mark the separate faces as used + for (int i = 0; igl_info.bSkybox) + { + FSkyBox *sb = static_cast(tex); + for (int i = 0; i<6; i++) + { + if (sb->faces[i]) + { + int index = sb->faces[i]->id.GetIndex(); + texhitlist[index] |= FTextureManager::HIT_Flat; + } + } + } + } + } + + // Check all used actors. + // 1. mark all sprites associated with its states + // 2. mark all model data and skins associated with its states + while (it.NextPair(pair)) + { + PClassActor *cls = pair->Key; + int gltrans = GLTranslationPalette::GetInternalTranslation(GetDefaultByType(cls)->Translation); + + for (int i = 0; i < cls->NumOwnedStates; i++) + { + spritelist[cls->OwnedStates[i].sprite].Insert(gltrans, true); + FSpriteModelFrame * smf = gl_FindModelFrame(cls, cls->OwnedStates[i].sprite, cls->OwnedStates[i].Frame, false); + if (smf != NULL) + { + for (int i = 0; i < MAX_MODELS_PER_FRAME; i++) + { + if (smf->skinIDs[i].isValid()) + { + texhitlist[smf->skinIDs[i].GetIndex()] |= FTexture::TEX_Flat; + } + else if (smf->modelIDs[i] != -1) + { + Models[smf->modelIDs[i]]->PushSpriteMDLFrame(smf, i); + Models[smf->modelIDs[i]]->AddSkins(texhitlist); + } + if (smf->modelIDs[i] != -1) + { + modellist[smf->modelIDs[i]] = 1; + } + } + } + } + } + + // mark all sprite textures belonging to the marked sprites. + for (int i = (int)(sprites.Size() - 1); i >= 0; i--) + { + if (spritelist[i].CountUsed()) + { + int j, k; + for (j = 0; j < sprites[i].numframes; j++) + { + const spriteframe_t *frame = &SpriteFrames[sprites[i].spriteframes + j]; + + for (k = 0; k < 16; k++) + { + FTextureID pic = frame->Texture[k]; + if (pic.isValid()) + { + spritehitlist[pic.GetIndex()] = &spritelist[i]; + } + } + } + } + } + + // delete everything unused before creating any new resources to avoid memory usage peaks. + + // delete unused models + for (unsigned i = 0; i < Models.Size(); i++) + { + if (!modellist[i]) Models[i]->DestroyVertexBuffer(); + } + + // delete unused textures + int cnt = TexMan.NumTextures(); + for (int i = cnt - 1; i >= 0; i--) + { + FTexture *tex = TexMan.ByIndex(i); + if (tex != nullptr) + { + if (!texhitlist[i]) + { + if (tex->gl_info.Material[0]) tex->gl_info.Material[0]->Clean(true); + } + if (spritehitlist[i] == nullptr || (*spritehitlist[i]).CountUsed() == 0) + { + if (tex->gl_info.Material[1]) tex->gl_info.Material[1]->Clean(true); + } + } + } + + if (gl_precache) + { + // cache all used textures + for (int i = cnt - 1; i >= 0; i--) + { + FTexture *tex = TexMan.ByIndex(i); + if (tex != nullptr) + { + PrecacheTexture(tex, texhitlist[i]); + if (spritehitlist[i] != nullptr && (*spritehitlist[i]).CountUsed() > 0) + { + PrecacheSprite(tex, *spritehitlist[i]); + } + } + } + + // cache all used models + for (unsigned i = 0; i < Models.Size(); i++) + { + if (modellist[i]) + Models[i]->BuildVertexBuffer(); + } + } + + delete[] spritehitlist; + delete[] spritelist; + delete[] modellist; +} + + //========================================================================== // // Prints some texture info diff --git a/src/gl/textures/gl_texture.h b/src/gl/textures/gl_texture.h index 455678f859..80eede02ff 100644 --- a/src/gl/textures/gl_texture.h +++ b/src/gl/textures/gl_texture.h @@ -2,6 +2,7 @@ #define __GL_TEXTURE_H__ #include "r_defs.h" +#include "textures/textures.h" class FBrightmapTexture : public FTexture { @@ -9,8 +10,8 @@ public: FBrightmapTexture (FTexture *source); ~FBrightmapTexture (); - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); + const uint8_t *GetColumn (unsigned int column, const Span **spans_out); + const uint8_t *GetPixels (); void Unload (); int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf); @@ -18,7 +19,7 @@ public: protected: FTexture *SourcePic; - //BYTE *Pixels; + //uint8_t *Pixels; //Span **Spans; }; diff --git a/src/gl/textures/gl_translate.cpp b/src/gl/textures/gl_translate.cpp index 5a6ecbea2c..20a58e22cf 100644 --- a/src/gl/textures/gl_translate.cpp +++ b/src/gl/textures/gl_translate.cpp @@ -46,7 +46,7 @@ bool GLTranslationPalette::Update() memset(pd.pe, 0, sizeof(pd.pe)); memcpy(pd.pe, remap->Palette, remap->NumEntries * sizeof(*remap->Palette)); - pd.crc32 = CalcCRC32((BYTE*)pd.pe, sizeof(pd.pe)); + pd.crc32 = CalcCRC32((uint8_t*)pd.pe, sizeof(pd.pe)); for(unsigned int i=0;i< AllPalettes.Size(); i++) { if (pd.crc32 == AllPalettes[i].crc32) diff --git a/src/gl/unused/gl_sections.cpp b/src/gl/unused/gl_sections.cpp index 34858bd3a6..08a68be8e8 100644 --- a/src/gl/unused/gl_sections.cpp +++ b/src/gl/unused/gl_sections.cpp @@ -79,8 +79,8 @@ class FSectionCreator { static FSectionCreator *creator; - BYTE *processed_segs; - BYTE *processed_subsectors; + uint8_t *processed_segs; + uint8_t *processed_subsectors; int *section_for_segs; vertex_t *v1_l1, *v2_l1; @@ -97,8 +97,8 @@ public: FSectionCreator() { - processed_segs = new BYTE[(numsegs+7)/8]; - processed_subsectors = new BYTE[(numsubsectors+7)/8]; + processed_segs = new uint8_t[(numsegs+7)/8]; + processed_subsectors = new uint8_t[(numsubsectors+7)/8]; memset(processed_segs, 0, (numsegs+7)/8); memset(processed_subsectors, 0, (numsubsectors+7)/8); diff --git a/src/gl/utility/gl_clock.cpp b/src/gl/utility/gl_clock.cpp index 9ec539f55e..81cb52b011 100644 --- a/src/gl/utility/gl_clock.cpp +++ b/src/gl/utility/gl_clock.cpp @@ -39,7 +39,6 @@ #include #include -#define USE_WINDOWS_DWORD #elif defined __APPLE__ #include #endif @@ -85,7 +84,7 @@ void gl_CalculateCPUSpeed () { LARGE_INTEGER count1, count2; unsigned minDiff; - long long ClockCalibration = 0; + int64_t ClockCalibration = 0; // Count cycles for at least 55 milliseconds. // The performance counter is very low resolution compared to CPU @@ -104,7 +103,7 @@ void gl_CalculateCPUSpeed () do { QueryPerformanceCounter (&count2); - } while ((DWORD)((unsigned __int64)count2.QuadPart - (unsigned __int64)count1.QuadPart) < minDiff); + } while ((uint32_t)((uint64_t)count2.QuadPart - (uint64_t)count1.QuadPart) < minDiff); ClockCalibration = __rdtsc() - ClockCalibration; QueryPerformanceCounter (&count2); SetPriorityClass (GetCurrentProcess (), NORMAL_PRIORITY_CLASS); @@ -232,7 +231,7 @@ void CheckBench() FString compose; compose.Format("Map %s: \"%s\",\nx = %1.4f, y = %1.4f, z = %1.4f, angle = %1.4f, pitch = %1.4f\n", - level.MapName.GetChars(), level.LevelName.GetChars(), ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle.Degrees, ViewPitch.Degrees); + level.MapName.GetChars(), level.LevelName.GetChars(), r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw.Degrees, r_viewpoint.Angles.Pitch.Degrees); AppendRenderStats(compose); AppendRenderTimes(compose); diff --git a/src/gl/utility/gl_clock.h b/src/gl/utility/gl_clock.h index 2ccda639a8..957090861e 100644 --- a/src/gl/utility/gl_clock.h +++ b/src/gl/utility/gl_clock.h @@ -12,7 +12,7 @@ extern double gl_MillisecPerCycle; #ifdef _MSC_VER -__forceinline long long GetClockCycle () +__forceinline int64_t GetClockCycle () { #if _M_X64 return __rdtsc(); @@ -23,18 +23,18 @@ __forceinline long long GetClockCycle () #elif defined __APPLE__ && (defined __i386__ || defined __x86_64__) -inline long long GetClockCycle() +inline int64_t GetClockCycle() { return __builtin_ia32_rdtsc(); } #elif defined(__GNUG__) && defined(__i386__) -inline long long GetClockCycle() +inline int64_t GetClockCycle() { if (CPU.bRDTSC) { - long long res; + int64_t res; asm volatile ("rdtsc" : "=A" (res)); return res; } @@ -46,7 +46,7 @@ inline long long GetClockCycle() #else -inline long long GetClockCycle () +inline int64_t GetClockCycle () { return 0; } @@ -71,13 +71,13 @@ public: // Not using QueryPerformanceCounter directly, so we don't need // to pull in the Windows headers for every single file that // wants to do some profiling. - long long time = (gl_benching? GetClockCycle() : 0); + int64_t time = (gl_benching? GetClockCycle() : 0); Counter -= time; } __forceinline void Unclock() { - long long time = (gl_benching? GetClockCycle() : 0); + int64_t time = (gl_benching? GetClockCycle() : 0); Counter += time; } @@ -92,7 +92,7 @@ public: } private: - long long Counter; + int64_t Counter; }; extern glcycle_t RenderWall,SetupWall,ClipWall; diff --git a/src/gl/utility/gl_geometric.cpp b/src/gl/utility/gl_geometric.cpp deleted file mode 100644 index 6db10af74c..0000000000 --- a/src/gl/utility/gl_geometric.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/* -** gl_geometric.cpp -** -**--------------------------------------------------------------------------- -** Copyright 2003 Timothy 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 -#include -#include "gl/utility/gl_geometric.h" - -static Vector axis[3] = -{ - Vector(1.0f, 0.0f, 0.0f), - Vector(0.0f, 1.0f, 0.0f), - Vector(0.0f, 0.0f, 1.0f) -}; - - - -Vector Vector::Cross(Vector &v) -{ - float x, y, z; - Vector cp; - - x = Y() * v.Z() - Z() * v.Y(); - y = Z() * v.X() - X() * v.Z(); - z = X() * v.Y() - Y() * v.X(); - - cp.Set(x, y, z); - - return cp; -} - - -Vector Vector::operator- (Vector &v) -{ - float x, y, z; - Vector vec; - - x = X() - v.X(); - y = Y() - v.Y(); - z = Z() - v.Z(); - - vec.Set(x, y, z); - - return vec; -} - - -Vector Vector::operator+ (Vector &v) -{ - float x, y, z; - Vector vec; - - x = X() + v.X(); - y = Y() + v.Y(); - z = Z() + v.Z(); - - vec.Set(x, y, z); - - return vec; -} - - -Vector Vector::operator* (float f) -{ - Vector vec(X(), Y(), Z()); - - vec.Scale(f); - - return vec; -} - - -Vector Vector::operator/ (float f) -{ - Vector vec(X(), Y(), Z()); - - vec.Scale(1.f / f); - - return vec; -} - - -bool Vector::operator== (Vector &v) -{ - return X() == v.X() && Y() == v.Y() && Z() == v.Z(); -} - - -void Vector::GetRightUp(Vector &right, Vector &up) -{ - Vector n(X(), Y(), Z()); - Vector fn(fabsf(n.X()), fabsf(n.Y()), fabsf(n.Z())); - int major = 0; - - if (fn[1] > fn[major]) major = 1; - if (fn[2] > fn[major]) major = 2; - - // build right vector by hand - if (fabsf(fn[0]-1.0f) < FLT_EPSILON || fabsf(fn[1]-1.0f) < FLT_EPSILON || fabsf(fn[2]-1.0f) < FLT_EPSILON) - { - if (major == 0 && n[0] > 0.f) - { - right.Set(0.f, 0.f, -1.f); - } - else if (major == 0) - { - right.Set(0.f, 0.f, 1.f); - } - - if (major == 1 || (major == 2 && n[2] > 0.f)) - { - right.Set(1.f, 0.f, 0.f); - } - - if (major == 2 && n[2] < 0.0f) - { - right.Set(-1.f, 0.f, 0.f); - } - } - else - { - right = axis[major].Cross(n); - } - - up = n.Cross(right); - right.Normalize(); - up.Normalize(); -} - - -void Vector::Scale(float scale) -{ - float x, y, z; - - x = X() * scale; - y = Y() * scale; - z = Z() * scale; - - Set(x, y, z); -} - - -Vector Vector::ProjectVector(Vector &a) -{ - Vector res, b; - - b.Set(X(), Y(), Z()); - res.Set(a.X(), a.Y(), a.Z()); - - res.Scale(a.Dot(b) / a.Dot(a)); - - return res; -} - - -Vector Vector::ProjectPlane(Vector &right, Vector &up) -{ - Vector src(X(), Y(), Z()); - Vector t1, t2; - - t1 = src.ProjectVector(right); - t2 = src.ProjectVector(up); - - return t1 + t2; -} - - - - - - -void Plane::Init(float *v1, float *v2, float *v3) -{ - Vector vec1, vec2, vec3; - - vec1.Set(v1); - vec2.Set(v2); - vec3.Set(v3); - -#ifdef _MSC_VER - m_normal = (vec2 - vec1).Cross(vec3 - vec1); -#else - Vector tmpVec = vec3 - vec1; - m_normal = (vec2 - vec1).Cross(tmpVec); -#endif - m_normal.Normalize(); - m_d = vec3.Dot(m_normal) * -1.f; -} - - -#define FNOTEQUAL(a, b) (fabsf(a - b) > 0.001f) -void Plane::Init(float *verts, int numVerts) -{ - float *v[3], *t; - int i, curVert; - - if (numVerts < 3) return; - - curVert = 1; - v[0] = verts + 0; - for (i = 1; i < numVerts; i++) - { - t = verts + (i * 3); - if (FNOTEQUAL(t[0], v[curVert - 1][0]) || FNOTEQUAL(t[1], v[curVert - 1][1]) || FNOTEQUAL(t[2], v[curVert - 1][2])) - { - v[curVert] = t; - curVert++; - } - if (curVert == 3) break; - } - - if (curVert != 3) - { - // degenerate triangle, no valid normal - return; - } - - Init(v[0], v[1], v[2]); -} - - -void Plane::Init(float a, float b, float c, float d) -{ - m_normal.Set(a, b, c); - m_d = d / m_normal.Length(); - m_normal.Normalize(); -} - - -void Plane::Set(secplane_t &plane) -{ - m_normal.Set((float)plane.Normal().X, (float)plane.Normal().Z, (float)plane.Normal().Y); - //m_normal.Normalize(); the vector is already normalized - m_d = (float)plane.fD(); -} - - -float Plane::DistToPoint(float x, float y, float z) -{ - Vector p; - - p.Set(x, y, z); - - return m_normal.Dot(p) + m_d; -} - - -bool Plane::PointOnSide(float x, float y, float z) -{ - return DistToPoint(x, y, z) < 0.f; -} - - diff --git a/src/gl/utility/gl_geometric.h b/src/gl/utility/gl_geometric.h index 243361d412..30c3e2dea9 100644 --- a/src/gl/utility/gl_geometric.h +++ b/src/gl/utility/gl_geometric.h @@ -36,145 +36,54 @@ #include "math.h" #include "r_defs.h" +#include "gl/scene/gl_wall.h" -class Vector -{ -public: - Vector() - { - SetX(0.f); - SetY(1.f); - SetZ(0.f); - m_length = 1.f; - } - - Vector(float x, float y, float z) - { - SetX(x); - SetY(y); - SetZ(z); - m_length=-1.0f; - } - - Vector(float *v) - { - SetX(v[0]); - SetY(v[1]); - SetZ(v[2]); - m_length=-1.0f; - } - - Vector(vertex_t * v) - { - SetX((float)v->fX()); - SetY((float)v->fY()); - SetZ(0); - } - - void Normalize() - { - float l = 1.f / Length(); - - SetX(X() * l); - SetY(Y() * l); - SetZ(Z() * l); - m_length=1.0f; - } - - void UpdateLength() - { - m_length = sqrtf((X() * X()) + (Y() * Y()) + (Z() * Z())); - } - - void Set(float *v) - { - SetX(v[0]); - SetY(v[1]); - SetZ(v[2]); - m_length=-1.0f; - } - - void Set(float x, float y, float z) - { - SetX(x); - SetY(y); - SetZ(z); - m_length=-1.0f; - } - - float Length() - { - if (m_length<0.0f) UpdateLength(); - return m_length; - } - - float Dist(Vector &v) - { - Vector t(X() - v.X(), Y() - v.Y(), Z() - v.Z()); - - return t.Length(); - } - - float Dot(Vector &v) - { - return (X() * v.X()) + (Y() * v.Y()) + (Z() * v.Z()); - } - - Vector Cross(Vector &v); - Vector operator- (Vector &v); - Vector operator+ (Vector &v); - Vector operator* (float f); - Vector operator/ (float f); - bool operator== (Vector &v); - bool operator!= (Vector &v) { return !((*this) == v); } - - void GetRightUp(Vector &up, Vector &right); - float operator[] (int index) const { return m_vec[index]; } - float &operator[] (int index) { return m_vec[index]; } - float X() const { return m_vec[0]; } - float Y() const { return m_vec[1]; } - float Z() const { return m_vec[2]; } - void SetX(float x) { m_vec[0] = x; } - void SetY(float y) { m_vec[1] = y; } - void SetZ(float z) { m_vec[2] = z; } - void Scale(float scale); - - Vector ProjectVector(Vector &a); - Vector ProjectPlane(Vector &right, Vector &up); -protected: - float m_vec[3]; - float m_length; -}; - +struct GLSeg; class Plane { public: - Plane() + void Set(GLSeg *seg) { - m_normal.Set(0.f, 1.f, 0.f); - m_d = 0.f; + m_normal = seg->Normal(); + m_d = m_normal | FVector3(-seg->x1, 0, -seg->y1); } - void Init(float *v1, float *v2, float *v3); - void Init(float a, float b, float c, float d); - void Init(float *verts, int numVerts); - void Set(secplane_t &plane); - float DistToPoint(float x, float y, float z); - bool PointOnSide(float x, float y, float z); - bool PointOnSide(Vector &v) { return PointOnSide(v.X(), v.Y(), v.Z()); } - bool ValidNormal() { return m_normal.Length() == 1.f; } - float A() { return m_normal.X(); } - float B() { return m_normal.Y(); } - float C() { return m_normal.Z(); } - float D() { return m_d; } + void Set(secplane_t &plane) + { + m_normal = { (float)plane.Normal().X, (float)plane.Normal().Z, (float)plane.Normal().Y }; + m_d = (float)plane.fD(); + } - const Vector &Normal() const { return m_normal; } + + float DistToPoint(float x, float y, float z) + { + FVector3 p(x, y, z); + + return (m_normal | p) + m_d; + } + + + bool PointOnSide(float x, float y, float z) + { + return DistToPoint(x, y, z) < 0.f; + } + + bool PointOnSide(FVector3 &v) { return PointOnSide(v.X, v.Y, v.Z); } + bool ValidNormal() { return m_normal.LengthSquared() == 1.f; } + + float A() { return m_normal.X; } + float B() { return m_normal.Y; } + float C() { return m_normal.Z; } + float D() { return m_d; } + + const FVector3 &Normal() const { return m_normal; } protected: - Vector m_normal; - float m_d; + FVector3 m_normal; + float m_d; }; + class Matrix3x4 // used like a 4x4 matrix with the last row always being (0,0,0,1) { float m[3][4]; @@ -213,21 +122,21 @@ public: { Matrix3x4 m1; - Vector axis(ax, ay, az); - axis.Normalize(); + FVector3 axis(ax, ay, az); + axis.MakeUnit(); double c = cos(angle * M_PI/180.), s = sin(angle * M_PI/180.), t = 1 - c; - double sx = s*axis.X(), sy = s*axis.Y(), sz = s*axis.Z(); + double sx = s*axis.X, sy = s*axis.Y, sz = s*axis.Z; double tx, ty, txx, tyy, u, v; - tx = t*axis.X(); - m1.m[0][0] = float( (txx=tx*axis.X()) + c ); - m1.m[0][1] = float( (u=tx*axis.Y()) - sz); - m1.m[0][2] = float( (v=tx*axis.Z()) + sy); + tx = t*axis.X; + m1.m[0][0] = float( (txx=tx*axis.X) + c ); + m1.m[0][1] = float( (u=tx*axis.Y) - sz); + m1.m[0][2] = float( (v=tx*axis.Z) + sy); - ty = t*axis.Y(); + ty = t*axis.Y; m1.m[1][0] = float( u + sz); - m1.m[1][1] = float( (tyy=ty*axis.Y()) + c ); - m1.m[1][2] = float( (u=ty*axis.Z()) - sx); + m1.m[1][1] = float( (tyy=ty*axis.Y) + c ); + m1.m[1][2] = float( (u=ty*axis.Z) - sx); m1.m[2][0] = float( v - sy); m1.m[2][1] = float( u + sx); @@ -262,16 +171,6 @@ public: return result; } - Vector operator *(const Vector &vec) - { - Vector result; - - result.SetX(vec.X()*m[0][0] + vec.Y()*m[0][1] + vec.Z()*m[0][2] + m[0][3]); - result.SetY(vec.X()*m[1][0] + vec.Y()*m[1][1] + vec.Z()*m[1][2] + m[1][3]); - result.SetZ(vec.X()*m[2][0] + vec.Y()*m[2][1] + vec.Z()*m[2][2] + m[2][3]); - return result; - } - FVector3 operator *(const FVector3 &vec) { FVector3 result; @@ -281,14 +180,6 @@ public: result.Z = vec.X*m[2][0] + vec.Y*m[2][1] + vec.Z*m[2][2] + m[2][3]; return result; } - - void MultiplyVector(float *f3 , float *f3o) - { - float x = f3[0] * m[0][0] + f3[1] * m[0][1] + f3[2] * m[0][2] + m[0][3]; - float y = f3[0] * m[1][0] + f3[1] * m[1][1] + f3[2] * m[1][2] + m[1][3]; - float z = f3[0] * m[2][0] + f3[1] * m[2][1] + f3[2] * m[2][2] + m[2][3]; - f3o[2] = z; f3o[1] = y; f3o[0] = x; - } }; #endif diff --git a/src/i_module.cpp b/src/i_module.cpp index a82963ec5f..f28942109f 100644 --- a/src/i_module.cpp +++ b/src/i_module.cpp @@ -36,7 +36,6 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include -#define USE_WINDOWS_DWORD #else #include #endif diff --git a/src/i_net.cpp b/src/i_net.cpp index cc357e2c94..7bf1f29aae 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -37,7 +37,6 @@ # define WIN32_LEAN_AND_MEAN # include # include -#define USE_WINDOWS_DWORD #else # include # include diff --git a/src/info.h b/src/info.h index afa8baa130..697bd836f7 100644 --- a/src/info.h +++ b/src/info.h @@ -35,11 +35,7 @@ #define __INFO_H__ #include -#if !defined(_WIN32) -#include // for intptr_t -#else -#include // for mingw -#endif +#include #include "dobject.h" #include "doomdef.h" @@ -53,6 +49,7 @@ class FScanner; struct FActorInfo; class FIntCVar; class FStateDefinitions; +class FInternalLightAssociation; enum EStateDefineFlags { @@ -118,7 +115,7 @@ struct FState uint16_t StateFlags; uint8_t Frame; uint8_t UseFlags; - uint8_t DefineFlags; // Unused byte so let's use it during state creation. + uint8_t DefineFlags; int32_t Misc1; // Was changed to int8_t, reverted to long for MBF compat int32_t Misc2; // Was changed to uint8_t, reverted to long for MBF compat public: @@ -275,6 +272,7 @@ public: PClassActor *GetReplacement(bool lookskill=true); PClassActor *GetReplacee(bool lookskill=true); + TArray LightAssociations; FState *OwnedStates; PClassActor *Replacement; PClassActor *Replacee; diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index ace2c0c617..d395d0ad21 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -50,6 +50,7 @@ #include "g_levellocals.h" #include "virtual.h" #include "events.h" +#include "p_acs.h" // [RH] Actually handle the cheat. The cheat code in st_stuff.c now just // writes some bytes to the network data stream, and the network code diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 40214f3620..229835405b 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -32,6 +32,8 @@ #include #include +#include "r_defs.h" + #include "doomtype.h" #include "version.h" @@ -41,6 +43,7 @@ #include #endif + #include #include "doomdef.h" @@ -57,7 +60,6 @@ #include "i_system.h" #include "i_video.h" #include "v_video.h" -#include "r_defs.h" #include "hu_stuff.h" diff --git a/src/m_png.cpp b/src/m_png.cpp index 50ecb29f4c..c51f37be80 100644 --- a/src/m_png.cpp +++ b/src/m_png.cpp @@ -1255,7 +1255,7 @@ static void UnpackPixels (int width, int bytesPerRow, int bitdepth, const uint8_ // in a cache line. union { - uint32 bits2l; + uint32_t bits2l; uint8_t bits2[4]; }; diff --git a/src/m_png.h b/src/m_png.h index 2390059c1d..70c45b7108 100644 --- a/src/m_png.h +++ b/src/m_png.h @@ -36,8 +36,9 @@ #include #include "doomtype.h" #include "v_video.h" -#include "files.h" +class FileReader; +class FileWriter; // PNG Writing -------------------------------------------------------------- // Start writing an 8-bit palettized PNG file. diff --git a/src/m_random.h b/src/m_random.h index c82c77a62d..250d88d133 100644 --- a/src/m_random.h +++ b/src/m_random.h @@ -90,9 +90,9 @@ public: // SFMT interface unsigned int GenRand32(); - QWORD GenRand64(); + uint64_t GenRand64(); void FillArray32(uint32_t *array, int size); - void FillArray64(QWORD *array, int size); + void FillArray64(uint64_t *array, int size); void InitGenRand(uint32_t seed); void InitByArray(uint32_t *init_key, int key_length); int GetMinArraySize32(); @@ -140,7 +140,7 @@ public: /** These real versions are due to Isaku Wada */ /** generates a random number on [0,1) with 53-bit resolution*/ - static inline double ToRes53(QWORD v) + static inline double ToRes53(uint64_t v) { return v * (1.0/18446744073709551616.0L); } @@ -149,7 +149,7 @@ public: * 32 bit integers */ static inline double ToRes53Mix(uint32_t x, uint32_t y) { - return ToRes53(x | ((QWORD)y << 32)); + return ToRes53(x | ((uint64_t)y << 32)); } /** generates a random number on [0,1) with 53-bit resolution @@ -204,7 +204,7 @@ private: { w128_t w128[SFMT::N]; unsigned int u[SFMT::N32]; - QWORD u64[SFMT::N64]; + uint64_t u64[SFMT::N64]; } sfmt; /** index counter to the 32-bit internal state array */ int idx; diff --git a/src/md5.cpp b/src/md5.cpp index ae3cb53c0b..aa4b506daa 100644 --- a/src/md5.cpp +++ b/src/md5.cpp @@ -20,6 +20,7 @@ #include "doomtype.h" #include "md5.h" #include "templates.h" +#include "files.h" #ifdef __BIG_ENDIAN__ void byteSwap(uint32_t *buf, unsigned words) diff --git a/src/md5.h b/src/md5.h index 1f2765378d..a645919db1 100644 --- a/src/md5.h +++ b/src/md5.h @@ -18,7 +18,7 @@ #ifndef MD5_H #define MD5_H -#include "files.h" +class FileReader; struct MD5Context { diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 435a867a2b..ca96593762 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -49,7 +49,7 @@ #include "hu_stuff.h" #include "gi.h" #include "v_palette.h" -#include "i_input.h" +#include "g_input.h" #include "gameconfigfile.h" #include "gstrings.h" #include "r_utility.h" diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index bf4966526d..6c4527b31c 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -48,6 +48,7 @@ #include "p_spec.h" #include "r_data/colormaps.h" #include "g_levellocals.h" +#include "actorinlines.h" EXTERN_CVAR(Int, vid_renderer) @@ -129,8 +130,6 @@ static void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flag ffloor->top.copied = ffloor->bottom.copied = false; ffloor->top.model = ffloor->bottom.model = ffloor->model = sec2; ffloor->target = sec; - ffloor->ceilingclip = ffloor->floorclip = NULL; - ffloor->validcount = 0; if (!(flags&FF_THINFLOOR)) { diff --git a/src/p_3dfloors.h b/src/p_3dfloors.h index 75b8656e41..cc3f71a17a 100644 --- a/src/p_3dfloors.h +++ b/src/p_3dfloors.h @@ -37,11 +37,7 @@ typedef enum } ffloortype_e; // This is for the purpose of Sector_SetContents: -#ifdef _MSC_VER -enum : unsigned int // MSVC is apparently the only compiler that supports this syntax -#else -enum -#endif +enum : unsigned int { VC_EMPTY = 0, // Here's the original values of the color shifts in Vavoom, and in ARGB: VC_WATER = 0x80825032, // 130, 80, 50, 128 -> 80.82.50.32 (was 0x101080) @@ -60,7 +56,9 @@ enum struct secplane_t; struct FDynamicColormap; - +struct line_t; +struct sector_t; +class AActor; struct F3DFloor { @@ -95,11 +93,6 @@ struct F3DFloor int lastlight; int alpha; - // kg3D - for software - short *floorclip; - short *ceilingclip; - int validcount; - FDynamicColormap *GetColormap(); void UpdateColormap(FDynamicColormap *&map); PalEntry GetBlend(); @@ -120,7 +113,7 @@ struct lightlist_t -class player_s; +class player_t; void P_PlayerOnSpecial3DFloor(player_t* player); bool P_CheckFor3DFloorHit(AActor * mo, double z); diff --git a/src/p_3dmidtex.cpp b/src/p_3dmidtex.cpp index 4cfe01f19f..0a4c5507de 100644 --- a/src/p_3dmidtex.cpp +++ b/src/p_3dmidtex.cpp @@ -42,6 +42,7 @@ #include "p_maputl.h" #include "p_spec.h" #include "g_levellocals.h" +#include "actor.h" //============================================================================ diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 29230616f0..99e092b281 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -84,6 +84,7 @@ #include "a_pickups.h" #include "r_data/colormaps.h" #include "g_levellocals.h" +#include "actorinlines.h" #include "stats.h" // P-codes for ACS scripts @@ -659,6 +660,134 @@ struct CallReturn unsigned int EntryInstrCount; }; + +class DLevelScript : public DObject +{ + DECLARE_CLASS(DLevelScript, DObject) + HAS_OBJECT_POINTERS +public: + + + enum EScriptState + { + SCRIPT_Running, + SCRIPT_Suspended, + SCRIPT_Delayed, + SCRIPT_TagWait, + SCRIPT_PolyWait, + SCRIPT_ScriptWaitPre, + SCRIPT_ScriptWait, + SCRIPT_PleaseRemove, + SCRIPT_DivideBy0, + SCRIPT_ModulusBy0, + }; + + DLevelScript(AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, + const int *args, int argcount, int flags); + ~DLevelScript(); + + void Serialize(FSerializer &arc); + int RunScript(); + + inline void SetState(EScriptState newstate) { state = newstate; } + inline EScriptState GetState() { return state; } + + DLevelScript *GetNext() const { return next; } + + void MarkLocalVarStrings() const + { + GlobalACSStrings.MarkStringArray(&Localvars[0], Localvars.Size()); + } + void LockLocalVarStrings() const + { + GlobalACSStrings.LockStringArray(&Localvars[0], Localvars.Size()); + } + void UnlockLocalVarStrings() const + { + GlobalACSStrings.UnlockStringArray(&Localvars[0], Localvars.Size()); + } + +protected: + DLevelScript *next, *prev; + int script; + TArray Localvars; + int *pc; + EScriptState state; + int statedata; + TObjPtr activator; + line_t *activationline; + bool backSide; + FFont *activefont; + int hudwidth, hudheight; + int ClipRectLeft, ClipRectTop, ClipRectWidth, ClipRectHeight; + int WrapWidth; + bool HandleAspect; + FBehavior *activeBehavior; + int InModuleScriptNumber; + + void Link(); + void Unlink(); + void PutLast(); + void PutFirst(); + static int Random(int min, int max); + static int ThingCount(int type, int stringid, int tid, int tag); + static void ChangeFlat(int tag, int name, bool floorOrCeiling); + static int CountPlayers(); + static void SetLineTexture(int lineid, int side, int position, int name); + static int DoSpawn(int type, const DVector3 &pos, int tid, DAngle angle, bool force); + static int DoSpawn(int type, int x, int y, int z, int tid, int angle, bool force); + static bool DoCheckActorTexture(int tid, AActor *activator, int string, bool floor); + int DoSpawnSpot(int type, int spot, int tid, int angle, bool forced); + int DoSpawnSpotFacing(int type, int spot, int tid, bool forced); + int DoClassifyActor(int tid); + int CallFunction(int argCount, int funcIndex, int32_t *args); + + void DoFadeTo(int r, int g, int b, int a, int time); + void DoFadeRange(int r1, int g1, int b1, int a1, + int r2, int g2, int b2, int a2, int time); + void DoSetFont(int fontnum); + void SetActorProperty(int tid, int property, int value); + void DoSetActorProperty(AActor *actor, int property, int value); + int GetActorProperty(int tid, int property); + int CheckActorProperty(int tid, int property, int value); + int GetPlayerInput(int playernum, int inputnum); + + int LineFromID(int id); + int SideFromID(int id, int side); + +private: + DLevelScript(); + + friend class DACSThinker; +}; + +class DACSThinker : public DThinker +{ + DECLARE_CLASS(DACSThinker, DThinker) + HAS_OBJECT_POINTERS +public: + DACSThinker(); + ~DACSThinker(); + + void Serialize(FSerializer &arc); + void Tick(); + + typedef TMap ScriptMap; + ScriptMap RunningScripts; // Array of all synchronous scripts + static TObjPtr ActiveThinker; + + void DumpScriptStatus(); + void StopScriptsFor(AActor *actor); + +private: + DLevelScript *LastScript; + DLevelScript *Scripts; // List of all running scripts + + friend class DLevelScript; + friend class FBehavior; +}; + + static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, const int *args, int argcount, int flags); @@ -10863,6 +10992,11 @@ CCMD(acsprofile) ShowProfileData(FuncProfiles, limit, sorter, true); } +void MarkACSThinker() +{ + GC::Mark(DACSThinker::ActiveThinker); +} + ADD_STAT(ACS) { return FStringf("ACS time: %f ms", ACSTime.TimeMS()); diff --git a/src/p_acs.h b/src/p_acs.h index 4943ef401c..178a6ecce8 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -35,8 +35,6 @@ #ifndef __P_ACS_H__ #define __P_ACS_H__ -#include "dobject.h" -#include "dthinker.h" #include "doomtype.h" #define LOCAL_SIZE 20 @@ -385,147 +383,4 @@ private: friend void ArrangeFunctionProfiles(TArray &profiles); }; -class DLevelScript : public DObject -{ - DECLARE_CLASS (DLevelScript, DObject) - HAS_OBJECT_POINTERS -public: - - - enum EScriptState - { - SCRIPT_Running, - SCRIPT_Suspended, - SCRIPT_Delayed, - SCRIPT_TagWait, - SCRIPT_PolyWait, - SCRIPT_ScriptWaitPre, - SCRIPT_ScriptWait, - SCRIPT_PleaseRemove, - SCRIPT_DivideBy0, - SCRIPT_ModulusBy0, - }; - - DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, - const int *args, int argcount, int flags); - ~DLevelScript (); - - void Serialize(FSerializer &arc); - int RunScript (); - - inline void SetState (EScriptState newstate) { state = newstate; } - inline EScriptState GetState () { return state; } - - DLevelScript *GetNext() const { return next; } - - void MarkLocalVarStrings() const - { - GlobalACSStrings.MarkStringArray(&Localvars[0], Localvars.Size()); - } - void LockLocalVarStrings() const - { - GlobalACSStrings.LockStringArray(&Localvars[0], Localvars.Size()); - } - void UnlockLocalVarStrings() const - { - GlobalACSStrings.UnlockStringArray(&Localvars[0], Localvars.Size()); - } - -protected: - DLevelScript *next, *prev; - int script; - TArray Localvars; - int *pc; - EScriptState state; - int statedata; - TObjPtr activator; - line_t *activationline; - bool backSide; - FFont *activefont; - int hudwidth, hudheight; - int ClipRectLeft, ClipRectTop, ClipRectWidth, ClipRectHeight; - int WrapWidth; - bool HandleAspect; - FBehavior *activeBehavior; - int InModuleScriptNumber; - - void Link (); - void Unlink (); - void PutLast (); - void PutFirst (); - static int Random (int min, int max); - static int ThingCount (int type, int stringid, int tid, int tag); - static void ChangeFlat (int tag, int name, bool floorOrCeiling); - static int CountPlayers (); - static void SetLineTexture (int lineid, int side, int position, int name); - static int DoSpawn (int type, const DVector3 &pos, int tid, DAngle angle, bool force); - static int DoSpawn(int type, int x, int y, int z, int tid, int angle, bool force); - static bool DoCheckActorTexture(int tid, AActor *activator, int string, bool floor); - int DoSpawnSpot (int type, int spot, int tid, int angle, bool forced); - int DoSpawnSpotFacing (int type, int spot, int tid, bool forced); - int DoClassifyActor (int tid); - int CallFunction(int argCount, int funcIndex, int32_t *args); - - void DoFadeTo (int r, int g, int b, int a, int time); - void DoFadeRange (int r1, int g1, int b1, int a1, - int r2, int g2, int b2, int a2, int time); - void DoSetFont (int fontnum); - void SetActorProperty (int tid, int property, int value); - void DoSetActorProperty (AActor *actor, int property, int value); - int GetActorProperty (int tid, int property); - int CheckActorProperty (int tid, int property, int value); - int GetPlayerInput (int playernum, int inputnum); - - int LineFromID(int id); - int SideFromID(int id, int side); - -private: - DLevelScript (); - - friend class DACSThinker; -}; - -class DACSThinker : public DThinker -{ - DECLARE_CLASS (DACSThinker, DThinker) - HAS_OBJECT_POINTERS -public: - DACSThinker (); - ~DACSThinker (); - - void Serialize(FSerializer &arc); - void Tick (); - - typedef TMap ScriptMap; - ScriptMap RunningScripts; // Array of all synchronous scripts - static TObjPtr ActiveThinker; - - void DumpScriptStatus(); - void StopScriptsFor (AActor *actor); - -private: - DLevelScript *LastScript; - DLevelScript *Scripts; // List of all running scripts - - friend class DLevelScript; - friend class FBehavior; -}; - -// The structure used to control scripts between maps -struct acsdefered_t -{ - enum EType - { - defexecute, - defexealways, - defsuspend, - defterminate - } type; - int script; - int args[3]; - int playernum; -}; - -FSerializer &Serialize(FSerializer &arc, const char *key, acsdefered_t &defer, acsdefered_t *def); - #endif //__P_ACS_H__ diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 2556c3b24d..efec8adc29 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -81,6 +81,7 @@ #include "g_levellocals.h" #include "r_utility.h" #include "sbar.h" +#include "actorinlines.h" AActor *SingleActorFromTID(int tid, AActor *defactor); diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index b978fd0d32..4c2c509e8f 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -32,6 +32,7 @@ #include "serializer.h" #include "p_spec.h" #include "g_levellocals.h" +#include "textures.h" //============================================================================ // diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 46475c04dd..d01fb51934 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -62,6 +62,7 @@ #include "menu/menu.h" #include "g_levellocals.h" #include "virtual.h" +#include "actorinlines.h" // The conversations as they exist inside a SCRIPTxx lump. struct Response diff --git a/src/p_effect.cpp b/src/p_effect.cpp index b2e5e99a0f..200fc367d3 100644 --- a/src/p_effect.cpp +++ b/src/p_effect.cpp @@ -217,7 +217,7 @@ void P_FindParticleSubsectors () static TMap ColorSaver; -static uint32 ParticleColor(int rgb) +static uint32_t ParticleColor(int rgb) { int *val; int stuff; @@ -232,7 +232,7 @@ static uint32 ParticleColor(int rgb) return stuff; } -static uint32 ParticleColor(int r, int g, int b) +static uint32_t ParticleColor(int r, int g, int b) { return ParticleColor(MAKERGB(r, g, b)); } @@ -732,7 +732,7 @@ void P_DrawRailTrail(AActor *source, TArray &portalhits, int color1, { if (shortest == NULL || shortest->sounddist > seg.sounddist) shortest = &seg; } - S_Sound (DVector3(shortest->soundpos, ViewPos.Z), CHAN_WEAPON, sound, 1, ATTN_NORM); + S_Sound (DVector3(shortest->soundpos, r_viewpoint.Pos.Z), CHAN_WEAPON, sound, 1, ATTN_NORM); } } } diff --git a/src/p_effect.h b/src/p_effect.h index c745a6d992..bb87a53d54 100644 --- a/src/p_effect.h +++ b/src/p_effect.h @@ -31,6 +31,8 @@ ** */ +#pragma once + #include "vectors.h" #define FX_ROCKET 0x00000001 diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index dde4fce29c..91d99791dd 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -53,6 +53,7 @@ #include "math/cmath.h" #include "g_levellocals.h" #include "virtual.h" +#include "actorinlines.h" #include "gi.h" diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 95de877f01..708b022e53 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -61,6 +61,7 @@ #include "virtual.h" #include "g_levellocals.h" #include "events.h" +#include "actorinlines.h" static FRandom pr_botrespawn ("BotRespawn"); static FRandom pr_killmobj ("ActorDie"); diff --git a/src/p_map.cpp b/src/p_map.cpp index 73cda449c7..1aa762dbdd 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -64,6 +64,7 @@ #include "g_level.h" #include "r_sky.h" #include "g_levellocals.h" +#include "actorinlines.h" CVAR(Bool, cl_bloodsplats, true, CVAR_ARCHIVE) CVAR(Int, sv_smartaim, 0, CVAR_ARCHIVE | CVAR_SERVERINFO) diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 053f44c13a..7ec64e8dbd 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -39,6 +39,8 @@ #include "p_3dmidtex.h" #include "p_blockmap.h" #include "r_utility.h" +#include "actor.h" +#include "actorinlines.h" // State. #include "r_state.h" diff --git a/src/p_maputl.h b/src/p_maputl.h index 99a9acc331..8ac3ea76af 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -7,6 +7,7 @@ #include "m_bbox.h" extern int validcount; +struct FBlockNode; struct divline_t { diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 5606a1102d..ca8d603768 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -72,6 +72,7 @@ #include "g_levellocals.h" #include "a_morph.h" #include "events.h" +#include "actorinlines.h" // MACROS ------------------------------------------------------------------ @@ -699,7 +700,7 @@ bool AActor::SetState (FState *newstate, bool nofunction) if (Renderer != NULL) { - Renderer->StateChanged(this); + SetDynamicLights(); } return true; } @@ -1493,7 +1494,7 @@ bool AActor::IsInsideVisibleAngles() const if (mo != nullptr) { - DVector3 diffang = ViewPos - Pos(); + DVector3 diffang = r_viewpoint.Pos - Pos(); DAngle to = diffang.Angle(); if (!(renderflags & RF_ABSMASKANGLE)) @@ -5073,10 +5074,7 @@ void AActor::CallBeginPlay() void AActor::PostBeginPlay () { - if (Renderer != NULL) - { - Renderer->StateChanged(this); - } + SetDynamicLights(); PrevAngles = Angles; flags7 |= MF7_HANDLENODELAY; } diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 47675699a2..11bfc4b026 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -120,6 +120,8 @@ DEFINE_FIELD(DPSprite, oldx) DEFINE_FIELD(DPSprite, oldy) DEFINE_FIELD(DPSprite, firstTic) DEFINE_FIELD(DPSprite, Tics) +DEFINE_FIELD(DPSprite, alpha) +DEFINE_FIELD(DPSprite, RenderStyle) DEFINE_FIELD_BIT(DPSprite, Flags, bAddWeapon, PSPF_ADDWEAPON) DEFINE_FIELD_BIT(DPSprite, Flags, bAddBob, PSPF_ADDBOB) DEFINE_FIELD_BIT(DPSprite, Flags, bPowDouble, PSPF_POWDOUBLE) @@ -143,6 +145,9 @@ DPSprite::DPSprite(player_t *owner, AActor *caller, int id) ID(id), processPending(true) { + alpha = 1; + RenderStyle = STYLE_Normal; + DPSprite *prev = nullptr; DPSprite *next = Owner->psprites; while (next != nullptr && next->ID < ID) @@ -1037,7 +1042,7 @@ void A_OverlayOffset(AActor *self, int layer, double wx, double wy, int flags) player_t *player = self->player; DPSprite *psp; - if (player && (player->playerstate != PST_DEAD)) + if (player) { psp = player->FindPSprite(layer); @@ -1184,7 +1189,69 @@ DEFINE_ACTION_FUNCTION(AActor, OverlayID) ACTION_RETURN_INT(0); } +//--------------------------------------------------------------------------- +// +// PROC A_OverlayAlpha +// Sets the alpha of an overlay. +//--------------------------------------------------------------------------- +DEFINE_ACTION_FUNCTION(AActor, A_OverlayAlpha) +{ + PARAM_ACTION_PROLOGUE(AActor); + PARAM_INT(layer); + PARAM_FLOAT(alph); + + if (ACTION_CALL_FROM_PSPRITE()) + { + DPSprite *pspr = self->player->FindPSprite((layer != 0) ? layer : stateinfo->mPSPIndex); + + if (pspr != nullptr) + pspr->alpha = clamp(alph, 0.0, 1.0); + } + return 0; +} + +// NON-ACTION function to get the overlay alpha of a layer. +DEFINE_ACTION_FUNCTION(AActor, OverlayAlpha) +{ + PARAM_ACTION_PROLOGUE(AActor); + PARAM_INT_DEF(layer); + + if (ACTION_CALL_FROM_PSPRITE()) + { + DPSprite *pspr = self->player->FindPSprite((layer != 0) ? layer : stateinfo->mPSPIndex); + + if (pspr != nullptr) + { + ACTION_RETURN_FLOAT(pspr->alpha); + } + } + ACTION_RETURN_FLOAT(0.0); +} + +//--------------------------------------------------------------------------- +// +// PROC A_OverlayRenderStyle +// +//--------------------------------------------------------------------------- + +DEFINE_ACTION_FUNCTION(AActor, A_OverlayRenderStyle) +{ + PARAM_ACTION_PROLOGUE(AActor); + PARAM_INT(layer); + PARAM_INT(style); + + if (ACTION_CALL_FROM_PSPRITE()) + { + DPSprite *pspr = self->player->FindPSprite((layer != 0) ? layer : stateinfo->mPSPIndex); + + if (pspr == nullptr || style >= STYLE_Count || style < 0) + return 0; + + pspr->RenderStyle = style; + } + return 0; +} //--------------------------------------------------------------------------- // @@ -1414,7 +1481,9 @@ void DPSprite::Serialize(FSerializer &arc) ("x", x) ("y", y) ("oldx", oldx) - ("oldy", oldy); + ("oldy", oldy) + ("alpha", alpha) + ("renderstyle", RenderStyle); } //------------------------------------------------------------------------ diff --git a/src/p_pspr.h b/src/p_pspr.h index 0fc5674c39..656cc67a9b 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -31,6 +31,7 @@ #define WEAPONTOP 32. #define WEAPON_FUDGE_Y 0.375 class AInventory; +struct FTranslatedLineTarget; // // Overlay psprites are scaled shapes @@ -49,11 +50,15 @@ enum PSPLayers enum PSPFlags { - PSPF_ADDWEAPON = 1 << 0, - PSPF_ADDBOB = 1 << 1, - PSPF_POWDOUBLE = 1 << 2, - PSPF_CVARFAST = 1 << 3, - PSPF_FLIP = 1 << 6, + PSPF_ADDWEAPON = 1 << 0, + PSPF_ADDBOB = 1 << 1, + PSPF_POWDOUBLE = 1 << 2, + PSPF_CVARFAST = 1 << 3, + PSPF_ALPHA = 1 << 4, + PSPF_RENDERSTYLE = 1 << 5, + PSPF_FLIP = 1 << 6, + PSPF_FORCEALPHA = 1 << 7, + PSPF_FORCESTYLE = 1 << 8, }; class DPSprite : public DObject @@ -77,11 +82,12 @@ public: void ResetInterpolation() { oldx = x; oldy = y; } void OnDestroy() override; - double x, y; + double x, y, alpha; double oldx, oldy; bool firstTic; int Tics; int Flags; + int RenderStyle; private: DPSprite () {} diff --git a/src/p_pusher.cpp b/src/p_pusher.cpp index af63467106..0b08b4f65b 100644 --- a/src/p_pusher.cpp +++ b/src/p_pusher.cpp @@ -33,6 +33,7 @@ #include "p_local.h" #include "d_player.h" #include "g_levellocals.h" +#include "actorinlines.h" CVAR(Bool, var_pushers, true, CVAR_SERVERINFO); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 210ded45d4..b65f53bcea 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -998,6 +998,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) } } } + AActor::RecreateAllAttachedLights(); Renderer->EndSerialize(arc); } diff --git a/src/p_secnodes.cpp b/src/p_secnodes.cpp index b351f0e580..346f5ff8e7 100644 --- a/src/p_secnodes.cpp +++ b/src/p_secnodes.cpp @@ -26,6 +26,7 @@ #include "p_maputl.h" #include "p_blockmap.h" #include "memarena.h" +#include "actor.h" //============================================================================= // phares 3/21/98 diff --git a/src/p_setup.cpp b/src/p_setup.cpp index edab7add78..c464a13862 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -3276,7 +3276,7 @@ void P_LoadReject (MapData * map, bool junk) if (qwords > 0) { - const QWORD *qreject = (const QWORD *)rejectmatrix; + const uint64_t *qreject = (const uint64_t *)rejectmatrix; i = 0; do diff --git a/src/p_sight.cpp b/src/p_sight.cpp index 92cd52b95b..d468d52a91 100644 --- a/src/p_sight.cpp +++ b/src/p_sight.cpp @@ -30,6 +30,7 @@ #include "stats.h" #include "g_levellocals.h" +#include "actorinlines.h" static FRandom pr_botchecksight ("BotCheckSight"); static FRandom pr_checksight ("CheckSight"); diff --git a/src/p_slopes.cpp b/src/p_slopes.cpp index c574d189b3..cec49003d2 100644 --- a/src/p_slopes.cpp +++ b/src/p_slopes.cpp @@ -39,6 +39,7 @@ #include "p_maputl.h" #include "p_spec.h" #include "g_levellocals.h" +#include "actor.h" //=========================================================================== // diff --git a/src/p_spec.cpp b/src/p_spec.cpp index c8ffc67e3a..fcfd0ddf46 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -72,6 +72,7 @@ #include "p_maputl.h" #include "p_blockmap.h" #include "g_levellocals.h" +#include "actorinlines.h" #ifndef NO_EDATA #include "edata.h" #endif diff --git a/src/p_spec.h b/src/p_spec.h index 8d6e53a23a..48535d0b03 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -31,6 +31,7 @@ class FScanner; struct level_info_t; +struct FDoorAnimation; struct FThinkerCollection { diff --git a/src/p_switch.cpp b/src/p_switch.cpp index ef700b05bf..d23e30ea22 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -49,6 +49,9 @@ #include "serializer.h" #include "p_maputl.h" #include "p_spec.h" +#include "textures.h" +#include "actor.h" +#include "actorinlines.h" #include "gi.h" diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index bacd81da3b..b044b3066c 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -46,6 +46,7 @@ #include "s_sound.h" #include "p_local.h" #include "templates.h" +#include "actor.h" // MACROS ------------------------------------------------------------------ diff --git a/src/p_things.cpp b/src/p_things.cpp index d4298f934b..da6f56cac4 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -52,6 +52,7 @@ #include "math/cmath.h" #include "actorptrselect.h" #include "g_levellocals.h" +#include "actorinlines.h" // Set of spawnable things for the Thing_Spawn and Thing_Projectile specials. FClassMap SpawnableThings; diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 4d5e00e48a..5aa2945acc 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -49,6 +49,7 @@ #include "p_tags.h" #include "p_terrain.h" #include "g_levellocals.h" +#include "info.h" //=========================================================================== // @@ -1812,7 +1813,7 @@ public: vd->zCeiling = vd->zFloor = vd->flags = 0; sc.MustGetToken('{'); - double x, y; + double x = 0, y = 0; while (!sc.CheckToken('}')) { FName key = ParseKey(); diff --git a/src/p_user.cpp b/src/p_user.cpp index e479ad963c..23fdcd865a 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -60,6 +60,7 @@ #include "p_spec.h" #include "virtual.h" #include "g_levellocals.h" +#include "actorinlines.h" #include "r_data/r_translate.h" static FRandom pr_skullpop ("SkullPop"); @@ -2038,11 +2039,12 @@ void P_CalcHeight (player_t *player) return; } + //[SP] Added (x*player->mo->ViewBob) to allow DECORATE changes to view bobbing speed. if (still) { if (player->health > 0) { - angle = level.time / (120 * TICRATE / 35.) * 360.; + angle = level.time / (120 * TICRATE / 35.) * 360. * player->mo->ViewBob; bob = player->userinfo.GetStillBob() * angle.Sin(); } else @@ -2052,7 +2054,7 @@ void P_CalcHeight (player_t *player) } else { - angle = level.time / (20 * TICRATE / 35.) * 360.; + angle = level.time / (20 * TICRATE / 35.) * 360. * player->mo->ViewBob; bob = player->bob * angle.Sin() * (player->mo->waterlevel > 1 ? 0.25f : 0.5f); } diff --git a/src/p_xlat.cpp b/src/p_xlat.cpp index 6c29482d74..9ec582a923 100644 --- a/src/p_xlat.cpp +++ b/src/p_xlat.cpp @@ -45,6 +45,7 @@ #include "sc_man.h" #include "cmdlib.h" #include "g_levellocals.h" +#include "actorinlines.h" #include "xlat/xlat.h" // define names for the TriggerType field of the general linedefs diff --git a/src/po_man.cpp b/src/po_man.cpp index a5e47401fe..5572db1fa0 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -33,6 +33,7 @@ #include "r_utility.h" #include "p_blockmap.h" #include "g_levellocals.h" +#include "actorinlines.h" // MACROS ------------------------------------------------------------------ diff --git a/src/po_man.h b/src/po_man.h index d9e42c7fb4..4bf56a147d 100644 --- a/src/po_man.h +++ b/src/po_man.h @@ -4,6 +4,7 @@ #include "tarray.h" #include "r_defs.h" #include "m_bbox.h" +#include "dthinker.h" class DPolyAction : public DThinker { diff --git a/src/polyrenderer/drawers/poly_buffer.cpp b/src/polyrenderer/drawers/poly_buffer.cpp new file mode 100644 index 0000000000..291e05acb4 --- /dev/null +++ b/src/polyrenderer/drawers/poly_buffer.cpp @@ -0,0 +1,100 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "poly_buffer.h" +#include "screen_triangle.h" + +///////////////////////////////////////////////////////////////////////////// + +PolySubsectorGBuffer *PolySubsectorGBuffer::Instance() +{ + static PolySubsectorGBuffer buffer; + return &buffer; +} + +void PolySubsectorGBuffer::Resize(int newwidth, int newheight) +{ + width = newwidth; + height = newheight; + values.resize(width * height); +} + +///////////////////////////////////////////////////////////////////////////// + +PolyStencilBuffer *PolyStencilBuffer::Instance() +{ + static PolyStencilBuffer buffer; + return &buffer; +} + +void PolyStencilBuffer::Clear(int newwidth, int newheight, uint8_t stencil_value) +{ + width = newwidth; + height = newheight; + int count = BlockWidth() * BlockHeight(); + values.resize(count * 64); + masks.resize(count); + + uint8_t *v = Values(); + uint32_t *m = Masks(); + for (int i = 0; i < count; i++) + { + m[i] = 0xffffff00 | stencil_value; + } +} + +///////////////////////////////////////////////////////////////////////////// + +namespace +{ + int NextBufferVertex = 0; +} + +TriVertex *PolyVertexBuffer::GetVertices(int count) +{ + enum { VertexBufferSize = 256 * 1024 }; + static TriVertex Vertex[VertexBufferSize]; + + if (NextBufferVertex + count > VertexBufferSize) + return nullptr; + TriVertex *v = Vertex + NextBufferVertex; + NextBufferVertex += count; + return v; +} + +void PolyVertexBuffer::Clear() +{ + NextBufferVertex = 0; +} diff --git a/src/polyrenderer/drawers/poly_buffer.h b/src/polyrenderer/drawers/poly_buffer.h new file mode 100644 index 0000000000..a135722fd0 --- /dev/null +++ b/src/polyrenderer/drawers/poly_buffer.h @@ -0,0 +1,70 @@ +/* +** Frame buffers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include + +struct TriVertex; + +class PolySubsectorGBuffer +{ +public: + static PolySubsectorGBuffer *Instance(); + void Resize(int newwidth, int newheight); + int Width() const { return width; } + int Height() const { return height; } + uint32_t *Values() { return values.data(); } + +private: + int width; + int height; + std::vector values; +}; + +class PolyStencilBuffer +{ +public: + static PolyStencilBuffer *Instance(); + void Clear(int newwidth, int newheight, uint8_t stencil_value = 0); + int Width() const { return width; } + int Height() const { return height; } + int BlockWidth() const { return (width + 7) / 8; } + int BlockHeight() const { return (height + 7) / 8; } + uint8_t *Values() { return values.data(); } + uint32_t *Masks() { return masks.data(); } + +private: + int width; + int height; + + // 8x8 blocks of stencil values, plus a mask for each block indicating if values are the same for early out stencil testing + std::vector values; + std::vector masks; +}; + +class PolyVertexBuffer +{ +public: + static TriVertex *GetVertices(int count); + static void Clear(); +}; diff --git a/src/polyrenderer/drawers/poly_draw_args.cpp b/src/polyrenderer/drawers/poly_draw_args.cpp new file mode 100644 index 0000000000..8d7687f64d --- /dev/null +++ b/src/polyrenderer/drawers/poly_draw_args.cpp @@ -0,0 +1,107 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "poly_draw_args.h" +#include "swrenderer/viewport/r_viewport.h" + +void PolyDrawArgs::SetClipPlane(float a, float b, float c, float d) +{ + clipPlane[0] = a; + clipPlane[1] = b; + clipPlane[2] = c; + clipPlane[3] = d; +} + +void PolyDrawArgs::SetTexture(FTexture *texture) +{ + textureWidth = texture->GetWidth(); + textureHeight = texture->GetHeight(); + auto viewport = PolyRenderer::Instance()->Thread.Viewport.get(); + if (viewport->RenderTarget->IsBgra()) + texturePixels = (const uint8_t *)texture->GetPixelsBgra(); + else + texturePixels = texture->GetPixels(); + translation = nullptr; +} + +void PolyDrawArgs::SetTexture(FTexture *texture, uint32_t translationID, bool forcePal) +{ + if (translationID != 0xffffffff && translationID != 0) + { + FRemapTable *table = TranslationToTable(translationID); + if (table != nullptr && !table->Inactive) + { + if (PolyRenderer::Instance()->Thread.Viewport->RenderTarget->IsBgra()) + translation = (uint8_t*)table->Palette; + else + translation = table->Remap; + + textureWidth = texture->GetWidth(); + textureHeight = texture->GetHeight(); + texturePixels = texture->GetPixels(); + return; + } + } + + if (forcePal) + { + textureWidth = texture->GetWidth(); + textureHeight = texture->GetHeight(); + texturePixels = texture->GetPixels(); + } + else + { + SetTexture(texture); + } +} + +void PolyDrawArgs::SetColormap(FSWColormap *base_colormap) +{ + uniforms.light_red = base_colormap->Color.r * 256 / 255; + uniforms.light_green = base_colormap->Color.g * 256 / 255; + uniforms.light_blue = base_colormap->Color.b * 256 / 255; + uniforms.light_alpha = base_colormap->Color.a * 256 / 255; + uniforms.fade_red = base_colormap->Fade.r; + uniforms.fade_green = base_colormap->Fade.g; + uniforms.fade_blue = base_colormap->Fade.b; + uniforms.fade_alpha = base_colormap->Fade.a; + uniforms.desaturate = MIN(abs(base_colormap->Desaturate), 255) * 255 / 256; + bool simple_shade = (base_colormap->Color.d == 0x00ffffff && base_colormap->Fade.d == 0x00000000 && base_colormap->Desaturate == 0); + if (simple_shade) + uniforms.flags |= TriUniforms::simple_shade; + else + uniforms.flags &= ~TriUniforms::simple_shade; + colormaps = base_colormap->Maps; +} diff --git a/src/polyrenderer/drawers/poly_draw_args.h b/src/polyrenderer/drawers/poly_draw_args.h new file mode 100644 index 0000000000..bf38ffab9e --- /dev/null +++ b/src/polyrenderer/drawers/poly_draw_args.h @@ -0,0 +1,69 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "screen_triangle.h" + +class FTexture; + +enum class TriangleDrawMode +{ + Normal, + Fan, + Strip +}; + +struct TriDrawTriangleArgs; +struct TriMatrix; + +class PolyDrawArgs +{ +public: + TriUniforms uniforms; + const TriMatrix *objectToClip = nullptr; + const TriVertex *vinput = nullptr; + int vcount = 0; + TriangleDrawMode mode = TriangleDrawMode::Normal; + bool ccw = false; + // bool stencilTest = true; // Always true for now + bool subsectorTest = false; + bool writeStencil = true; + bool writeColor = true; + bool writeSubsector = true; + const uint8_t *texturePixels = nullptr; + int textureWidth = 0; + int textureHeight = 0; + const uint8_t *translation = nullptr; + uint8_t stenciltestvalue = 0; + uint8_t stencilwritevalue = 0; + const uint8_t *colormaps = nullptr; + float clipPlane[4]; + TriBlendMode blendmode = TriBlendMode::Copy; + + void SetClipPlane(float a, float b, float c, float d); + void SetTexture(FTexture *texture); + void SetTexture(FTexture *texture, uint32_t translationID, bool forcePal = false); + void SetColormap(FSWColormap *base_colormap); +}; diff --git a/src/polyrenderer/drawers/poly_drawers.h b/src/polyrenderer/drawers/poly_drawers.h new file mode 100644 index 0000000000..d17d570f2c --- /dev/null +++ b/src/polyrenderer/drawers/poly_drawers.h @@ -0,0 +1,15414 @@ +/* +** Projected triangle drawer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +/* + Warning: this C++ source file has been auto-generated. Please modify the original php script that generated it. +*/ + +#pragma once + +#include "screen_triangle.h" + +static float FindGradientX(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (y0 - y2) - (c0 - c2) * (y1 - y2); + float bottom = (x1 - x2) * (y0 - y2) - (x0 - x2) * (y1 - y2); + return top / bottom; +} + +static float FindGradientY(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (x0 - x2) - (c0 - c2) * (x1 - x2); + float bottom = (x0 - x2) * (y1 - y2) - (x1 - x2) * (y0 - y2); + return top / bottom; +} + +static void TriFill32Copy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32AlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32AddSolid(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Add(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Sub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32RevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Stencil(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Shaded(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateCopy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateAlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateAdd(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateRevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32AddSrcColorOneMinusSrcColor(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Skycap(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Copy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32AlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32AddSolid(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Add(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Sub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32RevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Stencil(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Shaded(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateCopy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateAlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateAdd(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateRevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32AddSrcColorOneMinusSrcColor(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Skycap(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Copy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8AlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8AddSolid(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Add(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Sub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8RevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Stencil(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Shaded(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateCopy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateAlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateAdd(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateRevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8AddSrcColorOneMinusSrcColor(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Skycap(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Copy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8AlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8AddSolid(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Add(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Sub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8RevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Stencil(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Shaded(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateCopy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateAlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateAdd(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateRevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8AddSrcColorOneMinusSrcColor(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Skycap(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +std::vector ScreenTriangle::TriFill32 = +{ + &TriFill32Copy, + &TriFill32AlphaBlend, + &TriFill32AddSolid, + &TriFill32Add, + &TriFill32Sub, + &TriFill32RevSub, + &TriFill32Stencil, + &TriFill32Shaded, + &TriFill32TranslateCopy, + &TriFill32TranslateAlphaBlend, + &TriFill32TranslateAdd, + &TriFill32TranslateSub, + &TriFill32TranslateRevSub, + &TriFill32AddSrcColorOneMinusSrcColor, + &TriFill32Skycap, +}; + +std::vector ScreenTriangle::TriDraw32 = +{ + &TriDraw32Copy, + &TriDraw32AlphaBlend, + &TriDraw32AddSolid, + &TriDraw32Add, + &TriDraw32Sub, + &TriDraw32RevSub, + &TriDraw32Stencil, + &TriDraw32Shaded, + &TriDraw32TranslateCopy, + &TriDraw32TranslateAlphaBlend, + &TriDraw32TranslateAdd, + &TriDraw32TranslateSub, + &TriDraw32TranslateRevSub, + &TriDraw32AddSrcColorOneMinusSrcColor, + &TriDraw32Skycap, +}; + +std::vector ScreenTriangle::TriFill8 = +{ + &TriFill8Copy, + &TriFill8AlphaBlend, + &TriFill8AddSolid, + &TriFill8Add, + &TriFill8Sub, + &TriFill8RevSub, + &TriFill8Stencil, + &TriFill8Shaded, + &TriFill8TranslateCopy, + &TriFill8TranslateAlphaBlend, + &TriFill8TranslateAdd, + &TriFill8TranslateSub, + &TriFill8TranslateRevSub, + &TriFill8AddSrcColorOneMinusSrcColor, + &TriFill8Skycap, +}; + +std::vector ScreenTriangle::TriDraw8 = +{ + &TriDraw8Copy, + &TriDraw8AlphaBlend, + &TriDraw8AddSolid, + &TriDraw8Add, + &TriDraw8Sub, + &TriDraw8RevSub, + &TriDraw8Stencil, + &TriDraw8Shaded, + &TriDraw8TranslateCopy, + &TriDraw8TranslateAlphaBlend, + &TriDraw8TranslateAdd, + &TriDraw8TranslateSub, + &TriDraw8TranslateRevSub, + &TriDraw8AddSrcColorOneMinusSrcColor, + &TriDraw8Skycap, +}; + diff --git a/src/polyrenderer/drawers/poly_drawers.php b/src/polyrenderer/drawers/poly_drawers.php new file mode 100644 index 0000000000..741fcf3c7b --- /dev/null +++ b/src/polyrenderer/drawers/poly_drawers.php @@ -0,0 +1,449 @@ +/* +** Projected triangle drawer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +/* + Warning: this C++ source file has been auto-generated. Please modify the original php script that generated it. +*/ + +#pragma once + +#include "screen_triangle.h" + +static float FindGradientX(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (y0 - y2) - (c0 - c2) * (y1 - y2); + float bottom = (x1 - x2) * (y0 - y2) - (x0 - x2) * (y1 - y2); + return top / bottom; +} + +static float FindGradientY(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (x0 - x2) - (c0 - c2) * (x1 - x2); + float bottom = (x0 - x2) * (y1 - y2) - (x1 - x2) * (y0 - y2); + return top / bottom; +} + + +std::vector ScreenTriangle:: = +{ + +}; + + + &, + +static void (const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const * RESTRICT translation = (const *)args->translation; + + const * RESTRICT texPixels = (const *)args->texturePixels; + + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + * RESTRICT destOrg = (*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + *destptr = dest + x * 8 + ix; + + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if ( & (1 << 31)) + { + *destptr = dest + x; + + *destptr = fg; + } + <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + + } +} + + + fg = color; + + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + fg = texPixels[texelX * texHeight + texelY]; + + fg = translation[fg]; + + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + + + + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + \ No newline at end of file diff --git a/src/polyrenderer/drawers/poly_triangle.cpp b/src/polyrenderer/drawers/poly_triangle.cpp new file mode 100644 index 0000000000..0a58c6e60d --- /dev/null +++ b/src/polyrenderer/drawers/poly_triangle.cpp @@ -0,0 +1,413 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "poly_triangle.h" +#include "polyrenderer/poly_renderer.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "screen_triangle.h" + +CVAR(Bool, r_debug_trisetup, false, 0); + +int PolyTriangleDrawer::viewport_x; +int PolyTriangleDrawer::viewport_y; +int PolyTriangleDrawer::viewport_width; +int PolyTriangleDrawer::viewport_height; +int PolyTriangleDrawer::dest_pitch; +int PolyTriangleDrawer::dest_width; +int PolyTriangleDrawer::dest_height; +uint8_t *PolyTriangleDrawer::dest; +bool PolyTriangleDrawer::dest_bgra; +bool PolyTriangleDrawer::mirror; + +void PolyTriangleDrawer::set_viewport(int x, int y, int width, int height, DCanvas *canvas) +{ + dest = (uint8_t*)canvas->GetBuffer(); + dest_width = canvas->GetWidth(); + dest_height = canvas->GetHeight(); + dest_pitch = canvas->GetPitch(); + dest_bgra = canvas->IsBgra(); + + int offsetx = clamp(x, 0, dest_width); + int offsety = clamp(y, 0, dest_height); + int pixelsize = dest_bgra ? 4 : 1; + + viewport_x = x - offsetx; + viewport_y = y - offsety; + viewport_width = width; + viewport_height = height; + + dest += (offsetx + offsety * dest_pitch) * pixelsize; + dest_width = clamp(viewport_x + viewport_width, 0, dest_width - offsetx); + dest_height = clamp(viewport_y + viewport_height, 0, dest_height - offsety); + + mirror = false; +} + +void PolyTriangleDrawer::toggle_mirror() +{ + mirror = !mirror; +} + +void PolyTriangleDrawer::draw(const PolyDrawArgs &args) +{ + PolyRenderer::Instance()->Thread.DrawQueue->Push(args, mirror); +} + +void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadData *thread) +{ + if (drawargs.vcount < 3) + return; + + PolyDrawFuncPtr drawfuncs[4]; + int num_drawfuncs = 0; + + drawfuncs[num_drawfuncs++] = drawargs.subsectorTest ? &ScreenTriangle::SetupSubsector : &ScreenTriangle::SetupNormal; + + if (!r_debug_trisetup) // For profiling how much time is spent in setup vs drawal + { + int bmode = (int)drawargs.blendmode; + + if (drawargs.writeColor && drawargs.texturePixels) + drawfuncs[num_drawfuncs++] = dest_bgra ? ScreenTriangle::TriDraw32[bmode] : ScreenTriangle::TriDraw8[bmode]; + else if (drawargs.writeColor) + drawfuncs[num_drawfuncs++] = dest_bgra ? ScreenTriangle::TriFill32[bmode] : ScreenTriangle::TriFill8[bmode]; + } + + if (drawargs.writeStencil) + drawfuncs[num_drawfuncs++] = &ScreenTriangle::StencilWrite; + + if (drawargs.writeSubsector) + drawfuncs[num_drawfuncs++] = &ScreenTriangle::SubsectorWrite; + + TriDrawTriangleArgs args; + args.dest = dest; + args.pitch = dest_pitch; + args.clipleft = 0; + args.clipright = dest_width; + args.cliptop = 0; + args.clipbottom = dest_height; + args.texturePixels = drawargs.texturePixels; + args.textureWidth = drawargs.textureWidth; + args.textureHeight = drawargs.textureHeight; + args.translation = drawargs.translation; + args.uniforms = &drawargs.uniforms; + args.stencilTestValue = drawargs.stenciltestvalue; + args.stencilWriteValue = drawargs.stencilwritevalue; + args.stencilPitch = PolyStencilBuffer::Instance()->BlockWidth(); + args.stencilValues = PolyStencilBuffer::Instance()->Values(); + args.stencilMasks = PolyStencilBuffer::Instance()->Masks(); + args.subsectorGBuffer = PolySubsectorGBuffer::Instance()->Values(); + args.colormaps = drawargs.colormaps; + args.RGB256k = RGB256k.All; + args.BaseColors = (const uint8_t *)GPalette.BaseColors; + + bool ccw = drawargs.ccw; + const TriVertex *vinput = drawargs.vinput; + int vcount = drawargs.vcount; + + ShadedTriVertex vert[3]; + if (drawargs.mode == TriangleDrawMode::Normal) + { + for (int i = 0; i < vcount / 3; i++) + { + for (int j = 0; j < 3; j++) + vert[j] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + draw_shaded_triangle(vert, ccw, &args, thread, drawfuncs, num_drawfuncs); + } + } + else if (drawargs.mode == TriangleDrawMode::Fan) + { + vert[0] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + vert[1] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + for (int i = 2; i < vcount; i++) + { + vert[2] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + draw_shaded_triangle(vert, ccw, &args, thread, drawfuncs, num_drawfuncs); + vert[1] = vert[2]; + } + } + else // TriangleDrawMode::Strip + { + vert[0] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + vert[1] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + for (int i = 2; i < vcount; i++) + { + vert[2] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + draw_shaded_triangle(vert, ccw, &args, thread, drawfuncs, num_drawfuncs); + vert[0] = vert[1]; + vert[1] = vert[2]; + ccw = !ccw; + } + } +} + +ShadedTriVertex PolyTriangleDrawer::shade_vertex(const TriMatrix &objectToClip, const float *clipPlane, const TriVertex &v) +{ + // Apply transform to get clip coordinates: + ShadedTriVertex sv = objectToClip * v; + + // Calculate gl_ClipDistance[0] + sv.clipDistance0 = v.x * clipPlane[0] + v.y * clipPlane[1] + v.z * clipPlane[2] + v.w * clipPlane[3]; + + return sv; +} + +void PolyTriangleDrawer::draw_shaded_triangle(const ShadedTriVertex *vert, bool ccw, TriDrawTriangleArgs *args, WorkerThreadData *thread, PolyDrawFuncPtr *drawfuncs, int num_drawfuncs) +{ + // Cull, clip and generate additional vertices as needed + TriVertex clippedvert[max_additional_vertices]; + int numclipvert; + clipedge(vert, clippedvert, numclipvert); + + // Map to 2D viewport: + for (int j = 0; j < numclipvert; j++) + { + auto &v = clippedvert[j]; + + // Calculate normalized device coordinates: + v.w = 1.0f / v.w; + v.x *= v.w; + v.y *= v.w; + v.z *= v.w; + + // Apply viewport scale to get screen coordinates: + v.x = viewport_x + viewport_width * (1.0f + v.x) * 0.5f; + v.y = viewport_y + viewport_height * (1.0f - v.y) * 0.5f; + } + + // Keep varyings in -128 to 128 range if possible + if (numclipvert > 0) + { + for (int j = 0; j < TriVertex::NumVarying; j++) + { + float newOrigin = floorf(clippedvert[0].varying[j] * 0.1f) * 10.0f; + for (int i = 0; i < numclipvert; i++) + { + clippedvert[i].varying[j] -= newOrigin; + } + } + } + + // Draw screen triangles + if (ccw) + { + for (int i = numclipvert; i > 1; i--) + { + args->v1 = &clippedvert[numclipvert - 1]; + args->v2 = &clippedvert[i - 1]; + args->v3 = &clippedvert[i - 2]; + + for (int j = 0; j < num_drawfuncs; j++) + drawfuncs[j](args, thread); + } + } + else + { + for (int i = 2; i < numclipvert; i++) + { + args->v1 = &clippedvert[0]; + args->v2 = &clippedvert[i - 1]; + args->v3 = &clippedvert[i]; + + for (int j = 0; j < num_drawfuncs; j++) + drawfuncs[j](args, thread); + } + } +} + +bool PolyTriangleDrawer::cullhalfspace(float clipdistance1, float clipdistance2, float &t1, float &t2) +{ + if (clipdistance1 < 0.0f && clipdistance2 < 0.0f) + return true; + + if (clipdistance1 < 0.0f) + t1 = MAX(-clipdistance1 / (clipdistance2 - clipdistance1), 0.0f); + else + t1 = 0.0f; + + if (clipdistance2 < 0.0f) + t2 = MIN(1.0f + clipdistance2 / (clipdistance1 - clipdistance2), 1.0f); + else + t2 = 1.0f; + + return false; +} + +void PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, TriVertex *clippedvert, int &numclipvert) +{ + // Clip and cull so that the following is true for all vertices: + // -v.w <= v.x <= v.w + // -v.w <= v.y <= v.w + // -v.w <= v.z <= v.w + + // use barycentric weights while clipping vertices + float weights[max_additional_vertices * 3 * 2]; + for (int i = 0; i < 3; i++) + { + weights[i * 3 + 0] = 0.0f; + weights[i * 3 + 1] = 0.0f; + weights[i * 3 + 2] = 0.0f; + weights[i * 3 + i] = 1.0f; + } + + // halfspace clip distances + static const int numclipdistances = 7; + float clipdistance[numclipdistances * 3]; + for (int i = 0; i < 3; i++) + { + const auto &v = verts[i]; + clipdistance[i * numclipdistances + 0] = v.x + v.w; + clipdistance[i * numclipdistances + 1] = v.w - v.x; + clipdistance[i * numclipdistances + 2] = v.y + v.w; + clipdistance[i * numclipdistances + 3] = v.w - v.y; + clipdistance[i * numclipdistances + 4] = v.z + v.w; + clipdistance[i * numclipdistances + 5] = v.w - v.z; + clipdistance[i * numclipdistances + 6] = v.clipDistance0; + } + + // Clip against each halfspace + float *input = weights; + float *output = weights + max_additional_vertices * 3; + int inputverts = 3; + int outputverts = 0; + for (int p = 0; p < numclipdistances; p++) + { + // Clip each edge + outputverts = 0; + for (int i = 0; i < inputverts; i++) + { + int j = (i + 1) % inputverts; + float clipdistance1 = + clipdistance[0 * numclipdistances + p] * input[i * 3 + 0] + + clipdistance[1 * numclipdistances + p] * input[i * 3 + 1] + + clipdistance[2 * numclipdistances + p] * input[i * 3 + 2]; + + float clipdistance2 = + clipdistance[0 * numclipdistances + p] * input[j * 3 + 0] + + clipdistance[1 * numclipdistances + p] * input[j * 3 + 1] + + clipdistance[2 * numclipdistances + p] * input[j * 3 + 2]; + + float t1, t2; + if (!cullhalfspace(clipdistance1, clipdistance2, t1, t2) && outputverts + 1 < max_additional_vertices) + { + // add t1 vertex + for (int k = 0; k < 3; k++) + output[outputverts * 3 + k] = input[i * 3 + k] * (1.0f - t1) + input[j * 3 + k] * t1; + outputverts++; + + if (t2 != 1.0f && t2 > t1) + { + // add t2 vertex + for (int k = 0; k < 3; k++) + output[outputverts * 3 + k] = input[i * 3 + k] * (1.0f - t2) + input[j * 3 + k] * t2; + outputverts++; + } + } + } + std::swap(input, output); + std::swap(inputverts, outputverts); + if (inputverts == 0) + break; + } + + // Convert barycentric weights to actual vertices + numclipvert = inputverts; + for (int i = 0; i < numclipvert; i++) + { + auto &v = clippedvert[i]; + memset(&v, 0, sizeof(TriVertex)); + for (int w = 0; w < 3; w++) + { + float weight = input[i * 3 + w]; + v.x += verts[w].x * weight; + v.y += verts[w].y * weight; + v.z += verts[w].z * weight; + v.w += verts[w].w * weight; + for (int iv = 0; iv < TriVertex::NumVarying; iv++) + v.varying[iv] += verts[w].varying[iv] * weight; + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +DrawPolyTrianglesCommand::DrawPolyTrianglesCommand(const PolyDrawArgs &args, bool mirror) + : args(args) +{ + if (mirror) + this->args.ccw = !this->args.ccw; +} + +void DrawPolyTrianglesCommand::Execute(DrawerThread *thread) +{ + WorkerThreadData thread_data; + thread_data.core = thread->core; + thread_data.num_cores = thread->num_cores; + thread_data.pass_start_y = thread->pass_start_y; + thread_data.pass_end_y = thread->pass_end_y; + thread_data.FullSpans = thread->FullSpansBuffer.data(); + thread_data.PartialBlocks = thread->PartialBlocksBuffer.data(); + + PolyTriangleDrawer::draw_arrays(args, &thread_data); +} + +FString DrawPolyTrianglesCommand::DebugInfo() +{ + FString blendmodestr; + switch (args.blendmode) + { + default: blendmodestr = "Unknown"; break; + case TriBlendMode::Copy: blendmodestr = "Copy"; break; + case TriBlendMode::AlphaBlend: blendmodestr = "AlphaBlend"; break; + case TriBlendMode::AddSolid: blendmodestr = "AddSolid"; break; + case TriBlendMode::Add: blendmodestr = "Add"; break; + case TriBlendMode::Sub: blendmodestr = "Sub"; break; + case TriBlendMode::RevSub: blendmodestr = "RevSub"; break; + case TriBlendMode::Stencil: blendmodestr = "Stencil"; break; + case TriBlendMode::Shaded: blendmodestr = "Shaded"; break; + case TriBlendMode::TranslateCopy: blendmodestr = "TranslateCopy"; break; + case TriBlendMode::TranslateAlphaBlend: blendmodestr = "TranslateAlphaBlend"; break; + case TriBlendMode::TranslateAdd: blendmodestr = "TranslateAdd"; break; + case TriBlendMode::TranslateSub: blendmodestr = "TranslateSub"; break; + case TriBlendMode::TranslateRevSub: blendmodestr = "TranslateRevSub"; break; + case TriBlendMode::AddSrcColorOneMinusSrcColor: blendmodestr = "AddSrcColorOneMinusSrcColor"; break; + } + + FString info; + info.Format("DrawPolyTriangles: blend mode = %s, color = %d, light = %d, textureWidth = %d, textureHeight = %d, texture = %s, translation = %s, colormaps = %s", + blendmodestr.GetChars(), args.uniforms.color, args.uniforms.light, args.textureWidth, args.textureHeight, + args.texturePixels ? "ptr" : "null", args.translation ? "ptr" : "null", args.colormaps ? "ptr" : "null"); + return info; +} diff --git a/src/polyrenderer/drawers/poly_triangle.h b/src/polyrenderer/drawers/poly_triangle.h new file mode 100644 index 0000000000..8debeec8ac --- /dev/null +++ b/src/polyrenderer/drawers/poly_triangle.h @@ -0,0 +1,73 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/drawers/r_thread.h" +#include "polyrenderer/drawers/screen_triangle.h" +#include "polyrenderer/math/tri_matrix.h" +#include "polyrenderer/drawers/poly_buffer.h" +#include "polyrenderer/drawers/poly_draw_args.h" + +struct ShadedTriVertex : public TriVertex +{ + float clipDistance0; +}; + +typedef void(*PolyDrawFuncPtr)(const TriDrawTriangleArgs *, WorkerThreadData *); + +class PolyTriangleDrawer +{ +public: + static void set_viewport(int x, int y, int width, int height, DCanvas *canvas); + static void draw(const PolyDrawArgs &args); + static void toggle_mirror(); + +private: + static ShadedTriVertex shade_vertex(const TriMatrix &objectToClip, const float *clipPlane, const TriVertex &v); + static void draw_arrays(const PolyDrawArgs &args, WorkerThreadData *thread); + static void draw_shaded_triangle(const ShadedTriVertex *vertices, bool ccw, TriDrawTriangleArgs *args, WorkerThreadData *thread, PolyDrawFuncPtr *drawfuncs, int num_drawfuncs); + static bool cullhalfspace(float clipdistance1, float clipdistance2, float &t1, float &t2); + static void clipedge(const ShadedTriVertex *verts, TriVertex *clippedvert, int &numclipvert); + + static int viewport_x, viewport_y, viewport_width, viewport_height, dest_pitch, dest_width, dest_height; + static bool dest_bgra; + static uint8_t *dest; + static bool mirror; + + enum { max_additional_vertices = 16 }; + + friend class DrawPolyTrianglesCommand; +}; + +class DrawPolyTrianglesCommand : public DrawerCommand +{ +public: + DrawPolyTrianglesCommand(const PolyDrawArgs &args, bool mirror); + + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + +private: + PolyDrawArgs args; +}; diff --git a/src/polyrenderer/drawers/screen_triangle.cpp b/src/polyrenderer/drawers/screen_triangle.cpp new file mode 100644 index 0000000000..54af97f2c1 --- /dev/null +++ b/src/polyrenderer/drawers/screen_triangle.cpp @@ -0,0 +1,966 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "poly_triangle.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "screen_triangle.h" +#include "poly_drawers.h" + +void ScreenTriangle::SetupNormal(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + int clipright = args->clipright; + int clipbottom = args->clipbottom; + + int stencilPitch = args->stencilPitch; + uint8_t * RESTRICT stencilValues = args->stencilValues; + uint32_t * RESTRICT stencilMasks = args->stencilMasks; + uint8_t stencilTestValue = args->stencilTestValue; + + TriFullSpan * RESTRICT span = thread->FullSpans; + TriPartialBlock * RESTRICT partial = thread->PartialBlocks; + + // 28.4 fixed-point coordinates + const int Y1 = (int)round(16.0f * v1.y); + const int Y2 = (int)round(16.0f * v2.y); + const int Y3 = (int)round(16.0f * v3.y); + + const int X1 = (int)round(16.0f * v1.x); + const int X2 = (int)round(16.0f * v2.x); + const int X3 = (int)round(16.0f * v3.x); + + // Deltas + const int DX12 = X1 - X2; + const int DX23 = X2 - X3; + const int DX31 = X3 - X1; + + const int DY12 = Y1 - Y2; + const int DY23 = Y2 - Y3; + const int DY31 = Y3 - Y1; + + // Fixed-point deltas + const int FDX12 = DX12 << 4; + const int FDX23 = DX23 << 4; + const int FDX31 = DX31 << 4; + + const int FDY12 = DY12 << 4; + const int FDY23 = DY23 << 4; + const int FDY31 = DY31 << 4; + + // Bounding rectangle + int minx = MAX((MIN(MIN(X1, X2), X3) + 0xF) >> 4, 0); + int maxx = MIN((MAX(MAX(X1, X2), X3) + 0xF) >> 4, clipright - 1); + int miny = MAX((MIN(MIN(Y1, Y2), Y3) + 0xF) >> 4, 0); + int maxy = MIN((MAX(MAX(Y1, Y2), Y3) + 0xF) >> 4, clipbottom - 1); + if (minx >= maxx || miny >= maxy) + { + thread->NumFullSpans = 0; + thread->NumPartialBlocks = 0; + return; + } + + // Block size, standard 8x8 (must be power of two) + const int q = 8; + + // Start in corner of 8x8 block + minx &= ~(q - 1); + miny &= ~(q - 1); + + // Half-edge constants + int C1 = DY12 * X1 - DX12 * Y1; + int C2 = DY23 * X2 - DX23 * Y2; + int C3 = DY31 * X3 - DX31 * Y3; + + // Correct for fill convention + if (DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++; + if (DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++; + if (DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++; + + // First block line for this thread + int core = thread->core; + int num_cores = thread->num_cores; + int core_skip = (num_cores - ((miny / q) - core) % num_cores) % num_cores; + miny += core_skip * q; + + thread->StartX = minx; + thread->StartY = miny; + span->Length = 0; + + // Loop through blocks + for (int y = miny; y < maxy; y += q * num_cores) + { + for (int x = minx; x < maxx; x += q) + { + // Corners of block + int x0 = x << 4; + int x1 = (x + q - 1) << 4; + int y0 = y << 4; + int y1 = (y + q - 1) << 4; + + // Evaluate half-space functions + bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0; + bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0; + bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0; + bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0; + int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3); + + bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0; + bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0; + bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0; + bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0; + int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3); + + bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0; + bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0; + bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0; + bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0; + int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3); + + // Stencil test the whole block, if possible + int block = x / 8 + y / 8 * stencilPitch; + uint8_t *stencilBlock = &stencilValues[block * 64]; + uint32_t *stencilBlockMask = &stencilMasks[block]; + bool blockIsSingleStencil = ((*stencilBlockMask) & 0xffffff00) == 0xffffff00; + bool skipBlock = blockIsSingleStencil && ((*stencilBlockMask) & 0xff) != stencilTestValue; + + // Skip block when outside an edge + if (a == 0 || b == 0 || c == 0 || skipBlock) + { + if (span->Length != 0) + { + span++; + span->Length = 0; + } + continue; + } + + // Accept whole block when totally covered + if (a == 0xf && b == 0xf && c == 0xf && x + q <= clipright && y + q <= clipbottom && blockIsSingleStencil) + { + if (span->Length != 0) + { + span->Length++; + } + else + { + span->X = x; + span->Y = y; + span->Length = 1; + } + } + else // Partially covered block + { + x0 = x << 4; + x1 = (x + q - 1) << 4; + int CY1 = C1 + DX12 * y0 - DY12 * x0; + int CY2 = C2 + DX23 * y0 - DY23 * x0; + int CY3 = C3 + DX31 * y0 - DY31 * x0; + + uint32_t mask0 = 0; + uint32_t mask1 = 0; + + for (int iy = 0; iy < 4; iy++) + { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + + for (int ix = 0; ix < q; ix++) + { + bool passStencilTest = blockIsSingleStencil || stencilBlock[ix + iy * q] == stencilTestValue; + bool covered = (CX1 > 0 && CX2 > 0 && CX3 > 0 && (x + ix) < clipright && (y + iy) < clipbottom && passStencilTest); + mask0 <<= 1; + mask0 |= (uint32_t)covered; + + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + } + + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + } + + for (int iy = 4; iy < q; iy++) + { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + + for (int ix = 0; ix < q; ix++) + { + bool passStencilTest = blockIsSingleStencil || stencilBlock[ix + iy * q] == stencilTestValue; + bool covered = (CX1 > 0 && CX2 > 0 && CX3 > 0 && (x + ix) < clipright && (y + iy) < clipbottom && passStencilTest); + mask1 <<= 1; + mask1 |= (uint32_t)covered; + + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + } + + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + } + + if (mask0 != 0xffffffff || mask1 != 0xffffffff) + { + if (span->Length > 0) + { + span++; + span->Length = 0; + } + + if (mask0 == 0 && mask1 == 0) + continue; + + partial->X = x; + partial->Y = y; + partial->Mask0 = mask0; + partial->Mask1 = mask1; + partial++; + } + else if (span->Length != 0) + { + span->Length++; + } + else + { + span->X = x; + span->Y = y; + span->Length = 1; + } + } + } + + if (span->Length != 0) + { + span++; + span->Length = 0; + } + } + + thread->NumFullSpans = (int)(span - thread->FullSpans); + thread->NumPartialBlocks = (int)(partial - thread->PartialBlocks); +} + +void ScreenTriangle::SetupSubsector(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + int clipright = args->clipright; + int clipbottom = args->clipbottom; + + int stencilPitch = args->stencilPitch; + uint8_t * RESTRICT stencilValues = args->stencilValues; + uint32_t * RESTRICT stencilMasks = args->stencilMasks; + uint8_t stencilTestValue = args->stencilTestValue; + + uint32_t * RESTRICT subsectorGBuffer = args->subsectorGBuffer; + uint32_t subsectorDepth = args->uniforms->subsectorDepth; + int32_t pitch = args->pitch; + + TriFullSpan * RESTRICT span = thread->FullSpans; + TriPartialBlock * RESTRICT partial = thread->PartialBlocks; + + // 28.4 fixed-point coordinates + const int Y1 = (int)round(16.0f * v1.y); + const int Y2 = (int)round(16.0f * v2.y); + const int Y3 = (int)round(16.0f * v3.y); + + const int X1 = (int)round(16.0f * v1.x); + const int X2 = (int)round(16.0f * v2.x); + const int X3 = (int)round(16.0f * v3.x); + + // Deltas + const int DX12 = X1 - X2; + const int DX23 = X2 - X3; + const int DX31 = X3 - X1; + + const int DY12 = Y1 - Y2; + const int DY23 = Y2 - Y3; + const int DY31 = Y3 - Y1; + + // Fixed-point deltas + const int FDX12 = DX12 << 4; + const int FDX23 = DX23 << 4; + const int FDX31 = DX31 << 4; + + const int FDY12 = DY12 << 4; + const int FDY23 = DY23 << 4; + const int FDY31 = DY31 << 4; + + // Bounding rectangle + int minx = MAX((MIN(MIN(X1, X2), X3) + 0xF) >> 4, 0); + int maxx = MIN((MAX(MAX(X1, X2), X3) + 0xF) >> 4, clipright - 1); + int miny = MAX((MIN(MIN(Y1, Y2), Y3) + 0xF) >> 4, 0); + int maxy = MIN((MAX(MAX(Y1, Y2), Y3) + 0xF) >> 4, clipbottom - 1); + if (minx >= maxx || miny >= maxy) + { + thread->NumFullSpans = 0; + thread->NumPartialBlocks = 0; + return; + } + + // Block size, standard 8x8 (must be power of two) + const int q = 8; + + // Start in corner of 8x8 block + minx &= ~(q - 1); + miny &= ~(q - 1); + + // Half-edge constants + int C1 = DY12 * X1 - DX12 * Y1; + int C2 = DY23 * X2 - DX23 * Y2; + int C3 = DY31 * X3 - DX31 * Y3; + + // Correct for fill convention + if (DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++; + if (DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++; + if (DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++; + + // First block line for this thread + int core = thread->core; + int num_cores = thread->num_cores; + int core_skip = (num_cores - ((miny / q) - core) % num_cores) % num_cores; + miny += core_skip * q; + + thread->StartX = minx; + thread->StartY = miny; + span->Length = 0; + + // Loop through blocks + for (int y = miny; y < maxy; y += q * num_cores) + { + for (int x = minx; x < maxx; x += q) + { + // Corners of block + int x0 = x << 4; + int x1 = (x + q - 1) << 4; + int y0 = y << 4; + int y1 = (y + q - 1) << 4; + + // Evaluate half-space functions + bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0; + bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0; + bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0; + bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0; + int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3); + + bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0; + bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0; + bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0; + bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0; + int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3); + + bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0; + bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0; + bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0; + bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0; + int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3); + + // Stencil test the whole block, if possible + int block = x / 8 + y / 8 * stencilPitch; + uint8_t *stencilBlock = &stencilValues[block * 64]; + uint32_t *stencilBlockMask = &stencilMasks[block]; + bool blockIsSingleStencil = ((*stencilBlockMask) & 0xffffff00) == 0xffffff00; + bool skipBlock = blockIsSingleStencil && ((*stencilBlockMask) & 0xff) < stencilTestValue; + + // Skip block when outside an edge + if (a == 0 || b == 0 || c == 0 || skipBlock) + { + if (span->Length != 0) + { + span++; + span->Length = 0; + } + continue; + } + + // Accept whole block when totally covered + if (a == 0xf && b == 0xf && c == 0xf && x + q <= clipright && y + q <= clipbottom && blockIsSingleStencil) + { + // Totally covered block still needs a subsector coverage test: + + uint32_t *subsector = subsectorGBuffer + x + y * pitch; + + uint32_t mask0 = 0; + uint32_t mask1 = 0; + + for (int iy = 0; iy < 4; iy++) + { + for (int ix = 0; ix < q; ix++) + { + bool covered = subsector[ix] >= subsectorDepth; + mask0 <<= 1; + mask0 |= (uint32_t)covered; + } + subsector += pitch; + } + + for (int iy = 4; iy < q; iy++) + { + for (int ix = 0; ix < q; ix++) + { + bool covered = subsector[ix] >= subsectorDepth; + mask1 <<= 1; + mask1 |= (uint32_t)covered; + } + subsector += pitch; + } + + if (mask0 != 0xffffffff || mask1 != 0xffffffff) + { + if (span->Length > 0) + { + span++; + span->Length = 0; + } + + if (mask0 == 0 && mask1 == 0) + continue; + + partial->X = x; + partial->Y = y; + partial->Mask0 = mask0; + partial->Mask1 = mask1; + partial++; + } + else if (span->Length != 0) + { + span->Length++; + } + else + { + span->X = x; + span->Y = y; + span->Length = 1; + } + } + else // Partially covered block + { + x0 = x << 4; + x1 = (x + q - 1) << 4; + int CY1 = C1 + DX12 * y0 - DY12 * x0; + int CY2 = C2 + DX23 * y0 - DY23 * x0; + int CY3 = C3 + DX31 * y0 - DY31 * x0; + + uint32_t *subsector = subsectorGBuffer + x + y * pitch; + + uint32_t mask0 = 0; + uint32_t mask1 = 0; + + for (int iy = 0; iy < 4; iy++) + { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + + for (int ix = 0; ix < q; ix++) + { + bool passStencilTest = blockIsSingleStencil || stencilBlock[ix + iy * q] >= stencilTestValue; + bool covered = (CX1 > 0 && CX2 > 0 && CX3 > 0 && (x + ix) < clipright && (y + iy) < clipbottom && passStencilTest && subsector[ix] >= subsectorDepth); + mask0 <<= 1; + mask0 |= (uint32_t)covered; + + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + } + + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + subsector += pitch; + } + + for (int iy = 4; iy < q; iy++) + { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + + for (int ix = 0; ix < q; ix++) + { + bool passStencilTest = blockIsSingleStencil || stencilBlock[ix + iy * q] >= stencilTestValue; + bool covered = (CX1 > 0 && CX2 > 0 && CX3 > 0 && (x + ix) < clipright && (y + iy) < clipbottom && passStencilTest && subsector[ix] >= subsectorDepth); + mask1 <<= 1; + mask1 |= (uint32_t)covered; + + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + } + + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + subsector += pitch; + } + + if (mask0 != 0xffffffff || mask1 != 0xffffffff) + { + if (span->Length > 0) + { + span++; + span->Length = 0; + } + + if (mask0 == 0 && mask1 == 0) + continue; + + partial->X = x; + partial->Y = y; + partial->Mask0 = mask0; + partial->Mask1 = mask1; + partial++; + } + else if (span->Length != 0) + { + span->Length++; + } + else + { + span->X = x; + span->Y = y; + span->Length = 1; + } + } + } + + if (span->Length != 0) + { + span++; + span->Length = 0; + } + } + + thread->NumFullSpans = (int)(span - thread->FullSpans); + thread->NumPartialBlocks = (int)(partial - thread->PartialBlocks); +} + +void ScreenTriangle::StencilWrite(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + uint8_t * RESTRICT stencilValues = args->stencilValues; + uint32_t * RESTRICT stencilMasks = args->stencilMasks; + uint32_t stencilWriteValue = args->stencilWriteValue; + uint32_t stencilPitch = args->stencilPitch; + + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + int block = span.X / 8 + span.Y / 8 * stencilPitch; + uint8_t *stencilBlock = &stencilValues[block * 64]; + uint32_t *stencilBlockMask = &stencilMasks[block]; + + int width = span.Length; + for (int x = 0; x < width; x++) + stencilBlockMask[x] = 0xffffff00 | stencilWriteValue; + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + + int sblock = block.X / 8 + block.Y / 8 * stencilPitch; + uint8_t *stencilBlock = &stencilValues[sblock * 64]; + uint32_t *stencilBlockMask = &stencilMasks[sblock]; + + bool isSingleValue = ((*stencilBlockMask) & 0xffffff00) == 0xffffff00; + if (isSingleValue) + { + uint8_t value = (*stencilBlockMask) & 0xff; + for (int v = 0; v < 64; v++) + stencilBlock[v] = value; + *stencilBlockMask = 0; + } + + int count = 0; + for (int v = 0; v < 32; v++) + { + if ((mask0 & (1 << 31)) || stencilBlock[v] == stencilWriteValue) + { + stencilBlock[v] = stencilWriteValue; + count++; + } + mask0 <<= 1; + } + for (int v = 32; v < 64; v++) + { + if ((mask1 & (1 << 31)) || stencilBlock[v] == stencilWriteValue) + { + stencilBlock[v] = stencilWriteValue; + count++; + } + mask1 <<= 1; + } + + if (count == 64) + *stencilBlockMask = 0xffffff00 | stencilWriteValue; + } +} + +void ScreenTriangle::SubsectorWrite(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + uint32_t * RESTRICT subsectorGBuffer = args->subsectorGBuffer; + uint32_t subsectorDepth = args->uniforms->subsectorDepth; + int pitch = args->pitch; + + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *subsector = subsectorGBuffer + span.X + span.Y * pitch; + int width = span.Length * 8; + int height = 8; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + subsector[x] = subsectorDepth; + subsector += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + uint32_t *subsector = subsectorGBuffer + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + subsector[x] = subsectorDepth; + mask0 <<= 1; + } + subsector += pitch; + } + for (int y = 4; y < 8; y++) + { + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + subsector[x] = subsectorDepth; + mask1 <<= 1; + } + subsector += pitch; + } + } +} + +#if 0 +float ScreenTriangle::FindGradientX(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (y0 - y2) - (c0 - c2) * (y1 - y2); + float bottom = (x1 - x2) * (y0 - y2) - (x0 - x2) * (y1 - y2); + return top / bottom; +} + +float ScreenTriangle::FindGradientY(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (x0 - x2) - (c0 - c2) * (x1 - x2); + float bottom = (x0 - x2) * (y1 - y2) - (x1 - x2) * (y0 - y2); + return top / bottom; +} + +void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + uint32_t * RESTRICT subsectorGBuffer = (uint32_t*)args->subsectorGBuffer; + int pitch = args->pitch; + + uint32_t subsectorDepth = args->uniforms->subsectorDepth; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = 1706.0f; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + uint32_t *subsector = subsectorGBuffer + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + int lightpos = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + int lightstep = (lightnext - lightpos) / 8; + + for (int ix = 0; ix < 8; ix++) + { + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = r * lightpos / 256; + g = g * lightpos / 256; + b = b * lightpos / 256; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + + dest[x * 8 + ix] = fg; + subsector[x * 8 + ix] = subsectorDepth; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + subsector += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t *subsector = subsectorGBuffer + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + int lightstep = (lightnext - lightpos) / 8; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = r * lightpos / 256; + g = g * lightpos / 256; + b = b * lightpos / 256; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + + dest[x] = fg; + subsector[x] = subsectorDepth; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + subsector += pitch; + } + for (int y = 4; y < 8; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + int lightstep = (lightnext - lightpos) / 8; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = r * lightpos / 256; + g = g * lightpos / 256; + b = b * lightpos / 256; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + + dest[x] = fg; + subsector[x] = subsectorDepth; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + subsector += pitch; + } + } +} +#endif diff --git a/src/polyrenderer/drawers/screen_triangle.h b/src/polyrenderer/drawers/screen_triangle.h new file mode 100644 index 0000000000..cd2a6cbe08 --- /dev/null +++ b/src/polyrenderer/drawers/screen_triangle.h @@ -0,0 +1,164 @@ +/* +** Projected triangle drawer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include +#include + +class FString; + +struct TriFullSpan +{ + uint16_t X; + uint16_t Y; + uint32_t Length; +}; + +struct TriPartialBlock +{ + uint16_t X; + uint16_t Y; + uint32_t Mask0; + uint32_t Mask1; +}; + +struct WorkerThreadData +{ + int32_t core; + int32_t num_cores; + int32_t pass_start_y; + int32_t pass_end_y; + uint32_t *temp; + + // Triangle working data: + TriFullSpan *FullSpans; + TriPartialBlock *PartialBlocks; + uint32_t NumFullSpans; + uint32_t NumPartialBlocks; + int32_t StartX; + int32_t StartY; +}; + +struct TriVertex +{ + TriVertex() { } + TriVertex(float x, float y, float z, float w, float u, float v) : x(x), y(y), z(z), w(w) { varying[0] = u; varying[1] = v; } + + enum { NumVarying = 2 }; + float x, y, z, w; + float varying[NumVarying]; +}; + +struct TriUniforms +{ + uint32_t light; + uint32_t subsectorDepth; + uint32_t color; + uint32_t srcalpha; + uint32_t destalpha; + uint16_t light_alpha; + uint16_t light_red; + uint16_t light_green; + uint16_t light_blue; + uint16_t fade_alpha; + uint16_t fade_red; + uint16_t fade_green; + uint16_t fade_blue; + uint16_t desaturate; + float globvis; + uint32_t flags; + enum Flags + { + simple_shade = 1, + nearest_filter = 2, + fixed_light = 4 + }; +}; + +struct TriDrawTriangleArgs +{ + uint8_t *dest; + int32_t pitch; + TriVertex *v1; + TriVertex *v2; + TriVertex *v3; + int32_t clipleft; + int32_t clipright; + int32_t cliptop; + int32_t clipbottom; + const uint8_t *texturePixels; + uint32_t textureWidth; + uint32_t textureHeight; + const uint8_t *translation; + const TriUniforms *uniforms; + uint8_t *stencilValues; + uint32_t *stencilMasks; + int32_t stencilPitch; + uint8_t stencilTestValue; + uint8_t stencilWriteValue; + uint32_t *subsectorGBuffer; + const uint8_t *colormaps; + const uint8_t *RGB256k; + const uint8_t *BaseColors; +}; + +enum class TriBlendMode +{ + Copy, // blend_copy(shade(fg)) + AlphaBlend, // blend_alpha_blend(shade(fg), bg) + AddSolid, // blend_add(shade(fg), bg, srcalpha, destalpha) + Add, // blend_add(shade(fg), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + Sub, // blend_sub(shade(fg), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + RevSub, // blend_revsub(shade(fg), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + Stencil, // blend_stencil(shade(color), fg.a, bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + Shaded, // blend_stencil(shade(color), fg.index, bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + TranslateCopy, // blend_copy(shade(translate(fg))) + TranslateAlphaBlend, // blend_alpha_blend(shade(translate(fg)), bg) + TranslateAdd, // blend_add(shade(translate(fg)), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + TranslateSub, // blend_sub(shade(translate(fg)), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + TranslateRevSub,// blend_revsub(shade(translate(fg)), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + AddSrcColorOneMinusSrcColor, // glBlendMode(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR) used by GZDoom's fullbright additive sprites + Skycap // Fade to sky color when the V texture coordinate go beyond the [-1, 1] range +}; + +inline int NumTriBlendModes() { return (int)TriBlendMode::Skycap + 1; } + +class ScreenTriangle +{ +public: + static void SetupNormal(const TriDrawTriangleArgs *args, WorkerThreadData *thread); + static void SetupSubsector(const TriDrawTriangleArgs *args, WorkerThreadData *thread); + static void StencilWrite(const TriDrawTriangleArgs *args, WorkerThreadData *thread); + static void SubsectorWrite(const TriDrawTriangleArgs *args, WorkerThreadData *thread); + + static std::vector TriDraw8; + static std::vector TriDraw32; + static std::vector TriFill8; + static std::vector TriFill32; +}; + +struct ScreenTriangleStepVariables +{ + float W; + float Varying[TriVertex::NumVarying]; +}; diff --git a/src/polyrenderer/math/poly_intersection.cpp b/src/polyrenderer/math/poly_intersection.cpp new file mode 100644 index 0000000000..ed5e8ef438 --- /dev/null +++ b/src/polyrenderer/math/poly_intersection.cpp @@ -0,0 +1,235 @@ +/* +** Various 3D intersection tests +** Copyright (c) 1997-2015 The UICore Team +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "poly_intersection.h" + +IntersectionTest::Result IntersectionTest::plane_aabb(const Vec4f &plane, const AxisAlignedBoundingBox &aabb) +{ + Vec3f center = aabb.center(); + Vec3f extents = aabb.extents(); + float e = extents.x * std::abs(plane.x) + extents.y * std::abs(plane.y) + extents.z * std::abs(plane.z); + float s = center.x * plane.x + center.y * plane.y + center.z * plane.z + plane.w; + if (s - e > 0) + return inside; + else if (s + e < 0) + return outside; + else + return intersecting; +} + +IntersectionTest::Result IntersectionTest::plane_obb(const Vec4f &plane, const OrientedBoundingBox &obb) +{ + Vec3f n(plane); + float d = plane.w; + float e = obb.extents.x * std::abs(Vec3f::dot(obb.axis_x, n)) + obb.extents.y * std::abs(Vec3f::dot(obb.axis_y, n)) + obb.extents.z * std::abs(Vec3f::dot(obb.axis_z, n)); + float s = Vec3f::dot(obb.center, n) + d; + if (s - e > 0) + return inside; + else if (s + e < 0) + return outside; + else + return intersecting; +} + +IntersectionTest::OverlapResult IntersectionTest::sphere(const Vec3f ¢er1, float radius1, const Vec3f ¢er2, float radius2) +{ + Vec3f h = center1 - center2; + float square_distance = Vec3f::dot(h, h); + float radius_sum = radius1 + radius2; + if (square_distance > radius_sum * radius_sum) + return disjoint; + else + return overlap; +} + +IntersectionTest::OverlapResult IntersectionTest::sphere_aabb(const Vec3f ¢er, float radius, const AxisAlignedBoundingBox &aabb) +{ + Vec3f a = aabb.aabb_min - center; + Vec3f b = center - aabb.aabb_max; + a.x = std::max(a.x, 0.0f); + a.y = std::max(a.y, 0.0f); + a.z = std::max(a.z, 0.0f); + b.x = std::max(b.x, 0.0f); + b.y = std::max(b.y, 0.0f); + b.z = std::max(b.z, 0.0f); + Vec3f e = a + b; + float d = Vec3f::dot(e, e); + if (d > radius * radius) + return disjoint; + else + return overlap; +} + +IntersectionTest::OverlapResult IntersectionTest::aabb(const AxisAlignedBoundingBox &a, const AxisAlignedBoundingBox &b) +{ + if (a.aabb_min.x > b.aabb_max.x || b.aabb_min.x > a.aabb_max.x || + a.aabb_min.y > b.aabb_max.y || b.aabb_min.y > a.aabb_max.y || + a.aabb_min.z > b.aabb_max.z || b.aabb_min.z > a.aabb_max.z) + { + return disjoint; + } + else + { + return overlap; + } +} + +IntersectionTest::Result IntersectionTest::frustum_aabb(const FrustumPlanes &frustum, const AxisAlignedBoundingBox &box) +{ + bool is_intersecting = false; + for (int i = 0; i < 6; i++) + { + Result result = plane_aabb(frustum.planes[i], box); + if (result == outside) + return outside; + else if (result == intersecting) + is_intersecting = true; + break; + } + if (is_intersecting) + return intersecting; + else + return inside; +} + +IntersectionTest::Result IntersectionTest::frustum_obb(const FrustumPlanes &frustum, const OrientedBoundingBox &box) +{ + bool is_intersecting = false; + for (int i = 0; i < 6; i++) + { + Result result = plane_obb(frustum.planes[i], box); + if (result == outside) + return outside; + else if (result == intersecting) + is_intersecting = true; + } + if (is_intersecting) + return intersecting; + else + return inside; +} + +IntersectionTest::OverlapResult IntersectionTest::ray_aabb(const Vec3f &ray_start, const Vec3f &ray_end, const AxisAlignedBoundingBox &aabb) +{ + Vec3f c = (ray_start + ray_end) * 0.5f; + Vec3f w = ray_end - c; + Vec3f h = aabb.extents(); + + c -= aabb.center(); + + Vec3f v(std::abs(w.x), std::abs(w.y), std::abs(w.z)); + + if (std::abs(c.x) > v.x + h.x || std::abs(c.y) > v.y + h.y || std::abs(c.z) > v.z + h.z) + return disjoint; + + if (std::abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y || + std::abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x || + std::abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x) + return disjoint; + + return overlap; +} + +///////////////////////////////////////////////////////////////////////////// + +FrustumPlanes::FrustumPlanes() +{ +} + +FrustumPlanes::FrustumPlanes(const Mat4f &world_to_projection) +{ + planes[0] = near_frustum_plane(world_to_projection); + planes[1] = far_frustum_plane(world_to_projection); + planes[2] = left_frustum_plane(world_to_projection); + planes[3] = right_frustum_plane(world_to_projection); + planes[4] = top_frustum_plane(world_to_projection); + planes[5] = bottom_frustum_plane(world_to_projection); +} + +Vec4f FrustumPlanes::left_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] + m.matrix[0 + 0 * 4], + m.matrix[3 + 1 * 4] + m.matrix[0 + 1 * 4], + m.matrix[3 + 2 * 4] + m.matrix[0 + 2 * 4], + m.matrix[3 + 3 * 4] + m.matrix[0 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::right_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] - m.matrix[0 + 0 * 4], + m.matrix[3 + 1 * 4] - m.matrix[0 + 1 * 4], + m.matrix[3 + 2 * 4] - m.matrix[0 + 2 * 4], + m.matrix[3 + 3 * 4] - m.matrix[0 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::top_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] - m.matrix[1 + 0 * 4], + m.matrix[3 + 1 * 4] - m.matrix[1 + 1 * 4], + m.matrix[3 + 2 * 4] - m.matrix[1 + 2 * 4], + m.matrix[3 + 3 * 4] - m.matrix[1 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::bottom_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] + m.matrix[1 + 0 * 4], + m.matrix[3 + 1 * 4] + m.matrix[1 + 1 * 4], + m.matrix[3 + 2 * 4] + m.matrix[1 + 2 * 4], + m.matrix[3 + 3 * 4] + m.matrix[1 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::near_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] + m.matrix[2 + 0 * 4], + m.matrix[3 + 1 * 4] + m.matrix[2 + 1 * 4], + m.matrix[3 + 2 * 4] + m.matrix[2 + 2 * 4], + m.matrix[3 + 3 * 4] + m.matrix[2 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::far_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] - m.matrix[2 + 0 * 4], + m.matrix[3 + 1 * 4] - m.matrix[2 + 1 * 4], + m.matrix[3 + 2 * 4] - m.matrix[2 + 2 * 4], + m.matrix[3 + 3 * 4] - m.matrix[2 + 3 * 4]); + plane /= plane.length3(); + return plane; +} diff --git a/src/polyrenderer/math/poly_intersection.h b/src/polyrenderer/math/poly_intersection.h new file mode 100644 index 0000000000..438146fce3 --- /dev/null +++ b/src/polyrenderer/math/poly_intersection.h @@ -0,0 +1,179 @@ +/* +** Various 3D intersection tests +** Copyright (c) 1997-2015 The UICore Team +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" +#include +#include + +class Vec3f; + +class Vec4f +{ +public: + Vec4f() = default; + Vec4f(const Vec4f &) = default; + Vec4f(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) { } + Vec4f(float v) : x(v), y(v), z(v), w(v) { } + Vec4f(const Vec3f &xyz, float w); + + static float dot(const Vec4f &a, const Vec4f &b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } + static float dot3(const Vec4f &a, const Vec4f &b) { return a.x * b.x + a.y * b.y + a.z * b.z; } + float length3() const { return std::sqrt(dot3(*this, *this)); } + float magnitude() const { return std::sqrt(dot(*this, *this)); } + + Vec4f &operator+=(const Vec4f &b) { *this = Vec4f(x + b.x, y + b.y, z + b.z, w + b.w); return *this; } + Vec4f &operator-=(const Vec4f &b) { *this = Vec4f(x - b.x, y - b.y, z - b.z, w - b.w); return *this; } + Vec4f &operator*=(const Vec4f &b) { *this = Vec4f(x * b.x, y * b.y, z * b.z, w * b.w); return *this; } + Vec4f &operator/=(const Vec4f &b) { *this = Vec4f(x / b.x, y / b.y, z / b.z, w / b.w); return *this; } + Vec4f &operator+=(float b) { *this = Vec4f(x + b, y + b, z + b, w + b); return *this; } + Vec4f &operator-=(float b) { *this = Vec4f(x - b, y - b, z - b, w - b); return *this; } + Vec4f &operator*=(float b) { *this = Vec4f(x * b, y * b, z * b, w * b); return *this; } + Vec4f &operator/=(float b) { *this = Vec4f(x / b, y / b, z / b, w / b); return *this; } + + float x, y, z, w; +}; + +inline bool operator==(const Vec4f &a, const Vec4f &b) { return a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w; } +inline bool operator!=(const Vec4f &a, const Vec4f &b) { return a.x != b.x || a.y != b.y || a.z != b.z || a.w == b.w; } + +class Vec3f +{ +public: + Vec3f() = default; + Vec3f(const Vec3f &) = default; + Vec3f(const Vec4f &v) : x(v.x), y(v.y), z(v.z) { } + Vec3f(float x, float y, float z) : x(x), y(y), z(z) { } + Vec3f(float v) : x(v), y(v), z(v) { } + + static float dot(const Vec3f &a, const Vec3f &b) { return a.x * b.x + a.y * b.y + a.z * b.z; } + float length() const { return std::sqrt(dot(*this, *this)); } + + Vec3f &operator+=(const Vec3f &b) { *this = Vec3f(x + b.x, y + b.y, z + b.z); return *this; } + Vec3f &operator-=(const Vec3f &b) { *this = Vec3f(x - b.x, y - b.y, z - b.z); return *this; } + Vec3f &operator*=(const Vec3f &b) { *this = Vec3f(x * b.x, y * b.y, z * b.z); return *this; } + Vec3f &operator/=(const Vec3f &b) { *this = Vec3f(x / b.x, y / b.y, z / b.z); return *this; } + Vec3f &operator+=(float b) { *this = Vec3f(x + b, y + b, z + b); return *this; } + Vec3f &operator-=(float b) { *this = Vec3f(x - b, y - b, z - b); return *this; } + Vec3f &operator*=(float b) { *this = Vec3f(x * b, y * b, z * b); return *this; } + Vec3f &operator/=(float b) { *this = Vec3f(x / b, y / b, z / b); return *this; } + + float x, y, z; +}; + +inline bool operator==(const Vec3f &a, const Vec3f &b) { return a.x == b.x && a.y == b.y && a.z == b.z; } +inline bool operator!=(const Vec3f &a, const Vec3f &b) { return a.x != b.x || a.y != b.y || a.z != b.z; } + +inline Vec3f operator+(const Vec3f &a, const Vec3f &b) { return Vec3f(a.x + b.x, a.y + b.y, a.z + b.z); } +inline Vec3f operator-(const Vec3f &a, const Vec3f &b) { return Vec3f(a.x - b.x, a.y - b.y, a.z - b.z); } +inline Vec3f operator*(const Vec3f &a, const Vec3f &b) { return Vec3f(a.x * b.x, a.y * b.y, a.z * b.z); } +inline Vec3f operator/(const Vec3f &a, const Vec3f &b) { return Vec3f(a.x / b.x, a.y / b.y, a.z / b.z); } + +inline Vec3f operator+(const Vec3f &a, float b) { return Vec3f(a.x + b, a.y + b, a.z + b); } +inline Vec3f operator-(const Vec3f &a, float b) { return Vec3f(a.x - b, a.y - b, a.z - b); } +inline Vec3f operator*(const Vec3f &a, float b) { return Vec3f(a.x * b, a.y * b, a.z * b); } +inline Vec3f operator/(const Vec3f &a, float b) { return Vec3f(a.x / b, a.y / b, a.z / b); } + +inline Vec3f operator+(float a, const Vec3f &b) { return Vec3f(a + b.x, a + b.y, a + b.z); } +inline Vec3f operator-(float a, const Vec3f &b) { return Vec3f(a - b.x, a - b.y, a - b.z); } +inline Vec3f operator*(float a, const Vec3f &b) { return Vec3f(a * b.x, a * b.y, a * b.z); } +inline Vec3f operator/(float a, const Vec3f &b) { return Vec3f(a / b.x, a / b.y, a / b.z); } + +inline Vec4f::Vec4f(const Vec3f &xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) { } + +typedef TriMatrix Mat4f; + +class AxisAlignedBoundingBox +{ +public: + AxisAlignedBoundingBox() : aabb_min(), aabb_max() {} + AxisAlignedBoundingBox(const Vec3f &aabb_min, const Vec3f &aabb_max) : aabb_min(aabb_min), aabb_max(aabb_max) { } + AxisAlignedBoundingBox(const AxisAlignedBoundingBox &aabb, const Vec3f &barycentric_min, const Vec3f &barycentric_max) + : aabb_min(mix(aabb.aabb_min, aabb.aabb_max, barycentric_min)), aabb_max(mix(aabb.aabb_min, aabb.aabb_max, barycentric_max)) { } + + Vec3f center() const { return (aabb_max + aabb_min) * 0.5f; } + Vec3f extents() const { return (aabb_max - aabb_min) * 0.5f; } + + Vec3f aabb_min; + Vec3f aabb_max; + +private: + template + inline A mix(A a, B b, C mix) + { + return a * (C(1) - mix) + b * mix; + } +}; + +class OrientedBoundingBox +{ +public: + Vec3f center; + Vec3f extents; + Vec3f axis_x; + Vec3f axis_y; + Vec3f axis_z; +}; + +class FrustumPlanes +{ +public: + FrustumPlanes(); + explicit FrustumPlanes(const Mat4f &world_to_projection); + + Vec4f planes[6]; + +private: + static Vec4f left_frustum_plane(const Mat4f &matrix); + static Vec4f right_frustum_plane(const Mat4f &matrix); + static Vec4f top_frustum_plane(const Mat4f &matrix); + static Vec4f bottom_frustum_plane(const Mat4f &matrix); + static Vec4f near_frustum_plane(const Mat4f &matrix); + static Vec4f far_frustum_plane(const Mat4f &matrix); +}; + +class IntersectionTest +{ +public: + enum Result + { + outside, + inside, + intersecting, + }; + + enum OverlapResult + { + disjoint, + overlap + }; + + static Result plane_aabb(const Vec4f &plane, const AxisAlignedBoundingBox &aabb); + static Result plane_obb(const Vec4f &plane, const OrientedBoundingBox &obb); + static OverlapResult sphere(const Vec3f ¢er1, float radius1, const Vec3f ¢er2, float radius2); + static OverlapResult sphere_aabb(const Vec3f ¢er, float radius, const AxisAlignedBoundingBox &aabb); + static OverlapResult aabb(const AxisAlignedBoundingBox &a, const AxisAlignedBoundingBox &b); + static Result frustum_aabb(const FrustumPlanes &frustum, const AxisAlignedBoundingBox &box); + static Result frustum_obb(const FrustumPlanes &frustum, const OrientedBoundingBox &box); + static OverlapResult ray_aabb(const Vec3f &ray_start, const Vec3f &ray_end, const AxisAlignedBoundingBox &box); +}; diff --git a/src/polyrenderer/math/tri_matrix.cpp b/src/polyrenderer/math/tri_matrix.cpp new file mode 100644 index 0000000000..85a2117970 --- /dev/null +++ b/src/polyrenderer/math/tri_matrix.cpp @@ -0,0 +1,188 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_utility.h" +#include "tri_matrix.h" +#include "polyrenderer/drawers/poly_triangle.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_viewport.h" + +TriMatrix TriMatrix::null() +{ + TriMatrix m; + memset(m.matrix, 0, sizeof(m.matrix)); + return m; +} + +TriMatrix TriMatrix::identity() +{ + TriMatrix m = null(); + m.matrix[0] = 1.0f; + m.matrix[5] = 1.0f; + m.matrix[10] = 1.0f; + m.matrix[15] = 1.0f; + return m; +} + +TriMatrix TriMatrix::translate(float x, float y, float z) +{ + TriMatrix m = identity(); + m.matrix[0 + 3 * 4] = x; + m.matrix[1 + 3 * 4] = y; + m.matrix[2 + 3 * 4] = z; + return m; +} + +TriMatrix TriMatrix::scale(float x, float y, float z) +{ + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = x; + m.matrix[1 + 1 * 4] = y; + m.matrix[2 + 2 * 4] = z; + m.matrix[3 + 3 * 4] = 1; + return m; +} + +TriMatrix TriMatrix::rotate(float angle, float x, float y, float z) +{ + float c = cosf(angle); + float s = sinf(angle); + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = (x*x*(1.0f - c) + c); + m.matrix[0 + 1 * 4] = (x*y*(1.0f - c) - z*s); + m.matrix[0 + 2 * 4] = (x*z*(1.0f - c) + y*s); + m.matrix[1 + 0 * 4] = (y*x*(1.0f - c) + z*s); + m.matrix[1 + 1 * 4] = (y*y*(1.0f - c) + c); + m.matrix[1 + 2 * 4] = (y*z*(1.0f - c) - x*s); + m.matrix[2 + 0 * 4] = (x*z*(1.0f - c) - y*s); + m.matrix[2 + 1 * 4] = (y*z*(1.0f - c) + x*s); + m.matrix[2 + 2 * 4] = (z*z*(1.0f - c) + c); + m.matrix[3 + 3 * 4] = 1.0f; + return m; +} + +TriMatrix TriMatrix::swapYZ() +{ + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = 1.0f; + m.matrix[1 + 2 * 4] = 1.0f; + m.matrix[2 + 1 * 4] = -1.0f; + m.matrix[3 + 3 * 4] = 1.0f; + return m; +} + +TriMatrix TriMatrix::perspective(float fovy, float aspect, float z_near, float z_far) +{ + float f = (float)(1.0 / tan(fovy * M_PI / 360.0)); + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = f / aspect; + m.matrix[1 + 1 * 4] = f; + m.matrix[2 + 2 * 4] = (z_far + z_near) / (z_near - z_far); + m.matrix[2 + 3 * 4] = (2.0f * z_far * z_near) / (z_near - z_far); + m.matrix[3 + 2 * 4] = -1.0f; + return m; +} + +TriMatrix TriMatrix::frustum(float left, float right, float bottom, float top, float near, float far) +{ + float a = (right + left) / (right - left); + float b = (top + bottom) / (top - bottom); + float c = -(far + near) / (far - near); + float d = -(2.0f * far) / (far - near); + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = 2.0f * near / (right - left); + m.matrix[1 + 1 * 4] = 2.0f * near / (top - bottom); + m.matrix[0 + 2 * 4] = a; + m.matrix[1 + 2 * 4] = b; + m.matrix[2 + 2 * 4] = c; + m.matrix[2 + 3 * 4] = d; + m.matrix[3 + 2 * 4] = -1; + return m; +} + +TriMatrix TriMatrix::worldToView(const FRenderViewpoint &viewpoint) +{ + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = (float)viewpoint.Sin; + m.matrix[0 + 1 * 4] = (float)-viewpoint.Cos; + m.matrix[1 + 2 * 4] = 1.0f; + m.matrix[2 + 0 * 4] = (float)-viewpoint.Cos; + m.matrix[2 + 1 * 4] = (float)-viewpoint.Sin; + m.matrix[3 + 3 * 4] = 1.0f; + return m * translate((float)-viewpoint.Pos.X, (float)-viewpoint.Pos.Y, (float)-viewpoint.Pos.Z); +} + +TriMatrix TriMatrix::viewToClip(swrenderer::RenderViewport *viewport) +{ + float near = 5.0f; + float far = 65536.0f; + float width = (float)(viewport->viewwindow.FocalTangent * near); + float top = (float)(viewport->CenterY / viewport->InvZtoScale * near); + float bottom = (float)(top - viewheight / viewport->InvZtoScale * near); + return frustum(-width, width, bottom, top, near, far); +} + +TriMatrix TriMatrix::operator*(const TriMatrix &mult) const +{ + TriMatrix result; + for (int x = 0; x < 4; x++) + { + for (int y = 0; y < 4; y++) + { + result.matrix[x + y * 4] = + matrix[0 * 4 + x] * mult.matrix[y * 4 + 0] + + matrix[1 * 4 + x] * mult.matrix[y * 4 + 1] + + matrix[2 * 4 + x] * mult.matrix[y * 4 + 2] + + matrix[3 * 4 + x] * mult.matrix[y * 4 + 3]; + } + } + return result; +} + +ShadedTriVertex TriMatrix::operator*(TriVertex v) const +{ + float vx = matrix[0 * 4 + 0] * v.x + matrix[1 * 4 + 0] * v.y + matrix[2 * 4 + 0] * v.z + matrix[3 * 4 + 0] * v.w; + float vy = matrix[0 * 4 + 1] * v.x + matrix[1 * 4 + 1] * v.y + matrix[2 * 4 + 1] * v.z + matrix[3 * 4 + 1] * v.w; + float vz = matrix[0 * 4 + 2] * v.x + matrix[1 * 4 + 2] * v.y + matrix[2 * 4 + 2] * v.z + matrix[3 * 4 + 2] * v.w; + float vw = matrix[0 * 4 + 3] * v.x + matrix[1 * 4 + 3] * v.y + matrix[2 * 4 + 3] * v.z + matrix[3 * 4 + 3] * v.w; + ShadedTriVertex sv; + sv.x = vx; + sv.y = vy; + sv.z = vz; + sv.w = vw; + for (int i = 0; i < TriVertex::NumVarying; i++) + sv.varying[i] = v.varying[i]; + return sv; +} diff --git a/src/polyrenderer/math/tri_matrix.h b/src/polyrenderer/math/tri_matrix.h new file mode 100644 index 0000000000..aaf7c0073d --- /dev/null +++ b/src/polyrenderer/math/tri_matrix.h @@ -0,0 +1,47 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +struct TriVertex; +struct ShadedTriVertex; +namespace swrenderer { class RenderViewport; } + +struct TriMatrix +{ + static TriMatrix null(); + static TriMatrix identity(); + static TriMatrix translate(float x, float y, float z); + static TriMatrix scale(float x, float y, float z); + static TriMatrix rotate(float angle, float x, float y, float z); + static TriMatrix swapYZ(); + static TriMatrix perspective(float fovy, float aspect, float near, float far); + static TriMatrix frustum(float left, float right, float bottom, float top, float near, float far); + + static TriMatrix worldToView(const FRenderViewpoint &viewpoint); // Software renderer world to view space transform + static TriMatrix viewToClip(swrenderer::RenderViewport *viewport); // Software renderer shearing projection + + ShadedTriVertex operator*(TriVertex v) const; + TriMatrix operator*(const TriMatrix &m) const; + + float matrix[16]; +}; diff --git a/src/polyrenderer/poly_all.cpp b/src/polyrenderer/poly_all.cpp new file mode 100644 index 0000000000..8968b94ec1 --- /dev/null +++ b/src/polyrenderer/poly_all.cpp @@ -0,0 +1,18 @@ +#include "poly_renderer.cpp" +#include "drawers/poly_buffer.cpp" +#include "drawers/poly_draw_args.cpp" +#include "drawers/poly_triangle.cpp" +#include "drawers/screen_triangle.cpp" +#include "math/poly_intersection.cpp" +#include "math/tri_matrix.cpp" +#include "scene/poly_cull.cpp" +#include "scene/poly_decal.cpp" +#include "scene/poly_particle.cpp" +#include "scene/poly_plane.cpp" +#include "scene/poly_playersprite.cpp" +#include "scene/poly_portal.cpp" +#include "scene/poly_scene.cpp" +#include "scene/poly_sky.cpp" +#include "scene/poly_sprite.cpp" +#include "scene/poly_wall.cpp" +#include "scene/poly_wallsprite.cpp" diff --git a/src/polyrenderer/poly_renderer.cpp b/src/polyrenderer/poly_renderer.cpp new file mode 100644 index 0000000000..1545e6a247 --- /dev/null +++ b/src/polyrenderer/poly_renderer.cpp @@ -0,0 +1,226 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "st_stuff.h" +#include "r_data/r_translate.h" +#include "r_data/r_interpolate.h" +#include "poly_renderer.h" +#include "gl/data/gl_data.h" +#include "d_net.h" +#include "po_man.h" +#include "st_stuff.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_viewport.h" + +EXTERN_CVAR(Bool, r_shadercolormaps) +EXTERN_CVAR(Int, screenblocks) +void InitGLRMapinfoData(); + +///////////////////////////////////////////////////////////////////////////// + +PolyRenderer *PolyRenderer::Instance() +{ + static PolyRenderer scene; + return &scene; +} + +PolyRenderer::PolyRenderer() : Thread(nullptr) +{ +} + +void PolyRenderer::RenderView(player_t *player) +{ + using namespace swrenderer; + + auto viewport = Thread.Viewport.get(); + + viewport->RenderTarget = screen; + + int width = SCREENWIDTH; + int height = SCREENHEIGHT; + int stHeight = gST_Y; + float trueratio; + ActiveRatio(width, height, &trueratio); + viewport->SetViewport(width, height, trueratio); + + RenderActorView(player->mo, false); + + // Apply special colormap if the target cannot do it + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->ShaderColormap() && viewport->RenderTarget->IsBgra() && !(r_shadercolormaps && screen->Accel2D)) + { + Thread.DrawQueue->Push(cameraLight->ShaderColormap(), screen); + } + + DrawerThreads::Execute({ Thread.DrawQueue }); +} + +void PolyRenderer::RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines) +{ + auto viewport = Thread.Viewport.get(); + + const bool savedviewactive = viewactive; + + viewwidth = width; + viewport->RenderTarget = canvas; + R_SetWindow(viewport->viewpoint, viewport->viewwindow, 12, width, height, height, true); + viewport->SetViewport(width, height, viewport->viewwindow.WidescreenRatio); + viewwindowx = x; + viewwindowy = y; + viewactive = true; + + canvas->Lock(true); + + RenderActorView(actor, dontmaplines); + DrawerThreads::Execute({ Thread.DrawQueue }); + + canvas->Unlock(); + + viewport->RenderTarget = screen; + R_ExecuteSetViewSize(viewport->viewpoint, viewport->viewwindow); + float trueratio; + ActiveRatio(width, height, &trueratio); + viewport->SetViewport(width, height, viewport->viewwindow.WidescreenRatio); + viewactive = savedviewactive; +} + +void PolyRenderer::RenderActorView(AActor *actor, bool dontmaplines) +{ + NetUpdate(); + + DontMapLines = dontmaplines; + + P_FindParticleSubsectors(); + PO_LinkToSubsectors(); + R_SetupFrame(Thread.Viewport->viewpoint, Thread.Viewport->viewwindow, actor); + swrenderer::CameraLight::Instance()->SetCamera(Thread.Viewport.get(), actor); + Thread.Viewport->SetupFreelook(); + + ActorRenderFlags savedflags = Thread.Viewport->viewpoint.camera->renderflags; + // Never draw the player unless in chasecam mode + if (!Thread.Viewport->viewpoint.showviewer) + Thread.Viewport->viewpoint.camera->renderflags |= RF_INVISIBLE; + + ClearBuffers(); + SetSceneViewport(); + SetupPerspectiveMatrix(); + MainPortal.SetViewpoint(WorldToClip, Vec4f(0.0f, 0.0f, 0.0f, 1.0f), GetNextStencilValue()); + MainPortal.Render(0); + Skydome.Render(WorldToClip); + MainPortal.RenderTranslucent(0); + PlayerSprites.Render(); + + Thread.Viewport->viewpoint.camera->renderflags = savedflags; + interpolator.RestoreInterpolations (); + + NetUpdate(); +} + +void PolyRenderer::RenderRemainingPlayerSprites() +{ + PlayerSprites.RenderRemainingSprites(); +} + +void PolyRenderer::ClearBuffers() +{ + PolyVertexBuffer::Clear(); + auto viewport = Thread.Viewport.get(); + PolyStencilBuffer::Instance()->Clear(viewport->RenderTarget->GetWidth(), viewport->RenderTarget->GetHeight(), 0); + PolySubsectorGBuffer::Instance()->Resize(viewport->RenderTarget->GetPitch(), viewport->RenderTarget->GetHeight()); + NextStencilValue = 0; + SeenLinePortals.clear(); + SeenMirrors.clear(); +} + +void PolyRenderer::SetSceneViewport() +{ + using namespace swrenderer; + + auto viewport = Thread.Viewport.get(); + + if (viewport->RenderTarget == screen) // Rendering to screen + { + int height; + if (screenblocks >= 10) + height = SCREENHEIGHT; + else + height = (screenblocks*SCREENHEIGHT / 10) & ~7; + + int bottom = SCREENHEIGHT - (height + viewwindowy - ((height - viewheight) / 2)); + PolyTriangleDrawer::set_viewport(viewwindowx, SCREENHEIGHT - bottom - height, viewwidth, height, viewport->RenderTarget); + } + else // Rendering to camera texture + { + PolyTriangleDrawer::set_viewport(0, 0, viewport->RenderTarget->GetWidth(), viewport->RenderTarget->GetHeight(), viewport->RenderTarget); + } +} + +void PolyRenderer::SetupPerspectiveMatrix() +{ + static bool bDidSetup = false; + + if (!bDidSetup) + { + InitGLRMapinfoData(); + bDidSetup = true; + } + + // Code provided courtesy of Graf Zahl. Now we just have to plug it into the viewmatrix code... + // We have to scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1. + const auto &viewpoint = Thread.Viewport->viewpoint; + const auto &viewwindow = Thread.Viewport->viewwindow; + double radPitch = viewpoint.Angles.Pitch.Normalized180().Radians(); + double angx = cos(radPitch); + double angy = sin(radPitch) * glset.pixelstretch; + double alen = sqrt(angx*angx + angy*angy); + float adjustedPitch = (float)asin(angy / alen); + float adjustedViewAngle = (float)(viewpoint.Angles.Yaw - 90).Radians(); + + float ratio = viewwindow.WidescreenRatio; + float fovratio = (viewwindow.WidescreenRatio >= 1.3f) ? 1.333333f : ratio; + float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(viewpoint.FieldOfView.Radians() / 2) / fovratio)).Degrees); + + TriMatrix worldToView = + TriMatrix::rotate(adjustedPitch, 1.0f, 0.0f, 0.0f) * + TriMatrix::rotate(adjustedViewAngle, 0.0f, -1.0f, 0.0f) * + TriMatrix::scale(1.0f, glset.pixelstretch, 1.0f) * + TriMatrix::swapYZ() * + TriMatrix::translate((float)-viewpoint.Pos.X, (float)-viewpoint.Pos.Y, (float)-viewpoint.Pos.Z); + + WorldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; +} + +bool PolyRenderer::InsertSeenLinePortal(FLinePortal *portal) +{ + return SeenLinePortals.insert(portal).second; +} + +bool PolyRenderer::InsertSeenMirror(line_t *mirrorLine) +{ + return SeenMirrors.insert(mirrorLine).second; +} diff --git a/src/polyrenderer/poly_renderer.h b/src/polyrenderer/poly_renderer.h new file mode 100644 index 0000000000..565eec0045 --- /dev/null +++ b/src/polyrenderer/poly_renderer.h @@ -0,0 +1,75 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include +#include +#include +#include +#include "doomdata.h" +#include "r_utility.h" +#include "scene/poly_portal.h" +#include "scene/poly_playersprite.h" +#include "scene/poly_sky.h" +#include "swrenderer/r_renderthread.h" + +class AActor; +class DCanvas; +class DrawerCommandQueue; +typedef std::shared_ptr DrawerCommandQueuePtr; + +class PolyRenderer +{ +public: + PolyRenderer(); + + void RenderView(player_t *player); + void RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines); + void RenderRemainingPlayerSprites(); + + static PolyRenderer *Instance(); + + uint32_t GetNextStencilValue() { uint32_t value = NextStencilValue; NextStencilValue += 2; return value; } + + bool InsertSeenLinePortal(FLinePortal *portal); + bool InsertSeenMirror(line_t *mirrorLine); + + bool DontMapLines = false; + + swrenderer::RenderThread Thread; + +private: + void RenderActorView(AActor *actor, bool dontmaplines); + void ClearBuffers(); + void SetSceneViewport(); + void SetupPerspectiveMatrix(); + + TriMatrix WorldToClip; + RenderPolyScene MainPortal; + PolySkyDome Skydome; + RenderPolyPlayerSprites PlayerSprites; + uint32_t NextStencilValue = 0; + + std::set SeenLinePortals; + std::set SeenMirrors; +}; diff --git a/src/polyrenderer/scene/poly_cull.cpp b/src/polyrenderer/scene/poly_cull.cpp new file mode 100644 index 0000000000..de579d799b --- /dev/null +++ b/src/polyrenderer/scene/poly_cull.cpp @@ -0,0 +1,312 @@ +/* +** Potential visible set (PVS) handling +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_cull.h" +#include "polyrenderer/poly_renderer.h" + +void PolyCull::CullScene(const TriMatrix &worldToClip, const Vec4f &portalClipPlane) +{ + PvsSectors.clear(); + frustumPlanes = FrustumPlanes(worldToClip); + PortalClipPlane = portalClipPlane; + + // Cull front to back + MaxCeilingHeight = 0.0; + MinFloorHeight = 0.0; + if (numnodes == 0) + CullSubsector(subsectors); + else + CullNode(nodes + numnodes - 1); // The head node is the last node output. +} + +void PolyCull::CullNode(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 = PointOnSide(PolyRenderer::Instance()->Thread.Viewport->viewpoint.Pos, bsp); + + // Recursively divide front space (toward the viewer). + CullNode(bsp->children[side]); + + // Possibly divide back space (away from the viewer). + side ^= 1; + + if (!CheckBBox(bsp->bbox[side])) + return; + + node = bsp->children[side]; + } + + subsector_t *sub = (subsector_t *)((uint8_t *)node - 1); + CullSubsector(sub); +} + +void PolyCull::CullSubsector(subsector_t *sub) +{ + // Update sky heights for the scene + MaxCeilingHeight = MAX(MaxCeilingHeight, sub->sector->ceilingplane.Zat0()); + MinFloorHeight = MIN(MinFloorHeight, sub->sector->floorplane.Zat0()); + + // Mark that we need to render this + PvsSectors.push_back(sub); + + // Update culling info for further bsp clipping + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + if ((line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) && line->backsector == nullptr) + { + // Skip lines not facing viewer + DVector2 pt1 = line->v1->fPos() - PolyRenderer::Instance()->Thread.Viewport->viewpoint.Pos; + DVector2 pt2 = line->v2->fPos() - PolyRenderer::Instance()->Thread.Viewport->viewpoint.Pos; + if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) + continue; + + int sx1, sx2; + if (GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2) == LineSegmentRange::HasSegment) + { + MarkSegmentCulled(sx1, sx2); + } + } + } +} + +void PolyCull::ClearSolidSegments() +{ + SolidSegments.clear(); + SolidSegments.reserve(SolidCullScale + 2); + SolidSegments.push_back({ -0x7fff, -SolidCullScale }); + SolidSegments.push_back({ SolidCullScale , 0x7fff }); +} + +void PolyCull::InvertSegments() +{ + TempInvertSolidSegments.swap(SolidSegments); + ClearSolidSegments(); + int x = -0x7fff; + for (const auto &segment : TempInvertSolidSegments) + { + MarkSegmentCulled(x, segment.X1 - 1); + x = segment.X2 + 1; + } +} + +bool PolyCull::IsSegmentCulled(int x1, int x2) const +{ + x1 = clamp(x1, -0x7ffe, 0x7ffd); + x2 = clamp(x2, -0x7ffd, 0x7ffe); + + int next = 0; + while (SolidSegments[next].X2 <= x2) + next++; + return (x1 >= SolidSegments[next].X1 && x2 <= SolidSegments[next].X2); +} + +void PolyCull::MarkSegmentCulled(int x1, int x2) +{ + if (x1 >= x2) + return; + + x1 = clamp(x1, -0x7ffe, 0x7ffd); + x2 = clamp(x2, -0x7ffd, 0x7ffe); + + int cur = 0; + while (true) + { + if (SolidSegments[cur].X1 <= x1 && SolidSegments[cur].X2 >= x2) // Already fully marked + { + break; + } + else if (SolidSegments[cur].X2 >= x1 && SolidSegments[cur].X1 <= x2) // Merge segments + { + // Find last segment + int merge = cur; + while (merge + 1 != (int)SolidSegments.size() && SolidSegments[merge + 1].X1 <= x2) + merge++; + + // Apply new merged range + SolidSegments[cur].X1 = MIN(SolidSegments[cur].X1, x1); + SolidSegments[cur].X2 = MAX(SolidSegments[merge].X2, x2); + + // Remove additional segments we merged with + if (merge > cur) + SolidSegments.erase(SolidSegments.begin() + (cur + 1), SolidSegments.begin() + (merge + 1)); + + break; + } + else if (SolidSegments[cur].X1 > x1) // Insert new segment + { + SolidSegments.insert(SolidSegments.begin() + cur, { x1, x2 }); + break; + } + cur++; + } +} + +int PolyCull::PointOnSide(const DVector2 &pos, const node_t *node) +{ + return DMulScale32(FLOAT2FIXED(pos.Y) - node->y, node->dx, node->x - FLOAT2FIXED(pos.X), node->dy) > 0; +} + +bool PolyCull::CheckBBox(float *bspcoord) +{ + // Start using a quick frustum AABB test: + + AxisAlignedBoundingBox aabb(Vec3f(bspcoord[BOXLEFT], bspcoord[BOXBOTTOM], (float)PolyRenderer::Instance()->Thread.Viewport->viewpoint.Pos.Z - 1000.0f), Vec3f(bspcoord[BOXRIGHT], bspcoord[BOXTOP], (float)PolyRenderer::Instance()->Thread.Viewport->viewpoint.Pos.Z + 1000.0f)); + auto result = IntersectionTest::frustum_aabb(frustumPlanes, aabb); + if (result == IntersectionTest::outside) + return false; + + // Skip if its in front of the portal: + + if (IntersectionTest::plane_aabb(PortalClipPlane, aabb) == IntersectionTest::outside) + return false; + + // Occlusion test using solid segments: + + static const int lines[4][4] = + { + { BOXLEFT, BOXBOTTOM, BOXRIGHT, BOXBOTTOM }, + { BOXRIGHT, BOXBOTTOM, BOXRIGHT, BOXTOP }, + { BOXRIGHT, BOXTOP, BOXLEFT, BOXTOP }, + { BOXLEFT, BOXTOP, BOXLEFT, BOXBOTTOM } + }; + + bool foundline = false; + int minsx1, maxsx2; + for (int i = 0; i < 4; i++) + { + int j = i < 3 ? i + 1 : 0; + float x1 = bspcoord[lines[i][0]]; + float y1 = bspcoord[lines[i][1]]; + float x2 = bspcoord[lines[i][2]]; + float y2 = bspcoord[lines[i][3]]; + int sx1, sx2; + LineSegmentRange result = GetSegmentRangeForLine(x1, y1, x2, y2, sx1, sx2); + if (result == LineSegmentRange::HasSegment) + { + if (foundline) + { + minsx1 = MIN(minsx1, sx1); + maxsx2 = MAX(maxsx2, sx2); + } + else + { + minsx1 = sx1; + maxsx2 = sx2; + foundline = true; + } + } + else if (result == LineSegmentRange::AlwaysVisible) + { + return true; + } + } + if (!foundline) + return false; + + return !IsSegmentCulled(minsx1, maxsx2); +} + +LineSegmentRange PolyCull::GetSegmentRangeForLine(double x1, double y1, double x2, double y2, int &sx1, int &sx2) const +{ + double znear = 5.0; + double updownnear = -400.0; + double sidenear = 400.0; + + // Clip line to the portal clip plane + float distance1 = Vec4f::dot(PortalClipPlane, Vec4f((float)x1, (float)y1, 0.0f, 1.0f)); + float distance2 = Vec4f::dot(PortalClipPlane, Vec4f((float)x2, (float)y2, 0.0f, 1.0f)); + if (distance1 < 0.0f && distance2 < 0.0f) + { + return LineSegmentRange::NotVisible; + } + else if (distance1 < 0.0f || distance2 < 0.0f) + { + double t1 = 0.0f, t2 = 1.0f; + if (distance1 < 0.0f) + t1 = clamp(distance1 / (distance1 - distance2), 0.0f, 1.0f); + else + t2 = clamp(distance2 / (distance1 - distance2), 0.0f, 1.0f); + double nx1 = x1 * (1.0 - t1) + x2 * t1; + double ny1 = y1 * (1.0 - t1) + y2 * t1; + double nx2 = x1 * (1.0 - t2) + x2 * t2; + double ny2 = y1 * (1.0 - t2) + y2 * t2; + x1 = nx1; + x2 = nx2; + y1 = ny1; + y2 = ny2; + } + + // Transform to 2D view space: + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + x1 = x1 - viewpoint.Pos.X; + y1 = y1 - viewpoint.Pos.Y; + x2 = x2 - viewpoint.Pos.X; + y2 = y2 - viewpoint.Pos.Y; + double rx1 = x1 * viewpoint.Sin - y1 * viewpoint.Cos; + double rx2 = x2 * viewpoint.Sin - y2 * viewpoint.Cos; + double ry1 = x1 * viewpoint.Cos + y1 * viewpoint.Sin; + double ry2 = x2 * viewpoint.Cos + y2 * viewpoint.Sin; + + // Is it potentially visible when looking straight up or down? + if (!(ry1 < updownnear && ry2 < updownnear) && !(ry1 > znear && ry2 > znear) && + !(rx1 < -sidenear && rx2 < -sidenear) && !(rx1 > sidenear && rx2 > sidenear)) + return LineSegmentRange::AlwaysVisible; + + // Cull if line is entirely behind view + if (ry1 < znear && ry2 < znear) + return LineSegmentRange::NotVisible; + + // Clip line, if needed + double t1 = 0.0f, t2 = 1.0f; + if (ry1 < znear) + t1 = clamp((znear - ry1) / (ry2 - ry1), 0.0, 1.0); + if (ry2 < znear) + t2 = clamp((znear - ry2) / (ry2 - ry1), 0.0, 1.0); + if (t1 != 0.0 || t2 != 1.0) + { + double nx1 = rx1 * (1.0 - t1) + rx2 * t1; + double ny1 = ry1 * (1.0 - t1) + ry2 * t1; + double nx2 = rx1 * (1.0 - t2) + rx2 * t2; + double ny2 = ry1 * (1.0 - t2) + ry2 * t2; + rx1 = nx1; + rx2 = nx2; + ry1 = ny1; + ry2 = ny2; + } + + sx1 = (int)floor(clamp(rx1 / ry1 * (SolidCullScale / 3), (double)-SolidCullScale, (double)SolidCullScale)); + sx2 = (int)floor(clamp(rx2 / ry2 * (SolidCullScale / 3), (double)-SolidCullScale, (double)SolidCullScale)); + + if (sx1 > sx2) + std::swap(sx1, sx2); + return (sx1 != sx2) ? LineSegmentRange::HasSegment : LineSegmentRange::AlwaysVisible; +} diff --git a/src/polyrenderer/scene/poly_cull.h b/src/polyrenderer/scene/poly_cull.h new file mode 100644 index 0000000000..faa8a0740c --- /dev/null +++ b/src/polyrenderer/scene/poly_cull.h @@ -0,0 +1,71 @@ +/* +** Potential visible set (PVS) handling +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" +#include "polyrenderer/math/poly_intersection.h" + +enum class LineSegmentRange +{ + NotVisible, + HasSegment, + AlwaysVisible +}; + +class PolyCull +{ +public: + void ClearSolidSegments(); + void CullScene(const TriMatrix &worldToClip, const Vec4f &portalClipPlane); + + LineSegmentRange GetSegmentRangeForLine(double x1, double y1, double x2, double y2, int &sx1, int &sx2) const; + void MarkSegmentCulled(int x1, int x2); + bool IsSegmentCulled(int x1, int x2) const; + void InvertSegments(); + + std::vector PvsSectors; + double MaxCeilingHeight = 0.0; + double MinFloorHeight = 0.0; + +private: + struct SolidSegment + { + SolidSegment(int x1, int x2) : X1(x1), X2(x2) { } + int X1, X2; + }; + + void CullNode(void *node); + void CullSubsector(subsector_t *sub); + int PointOnSide(const DVector2 &pos, const node_t *node); + + // Checks BSP node/subtree bounding box. + // Returns true if some part of the bbox might be visible. + bool CheckBBox(float *bspcoord); + + std::vector SolidSegments; + std::vector TempInvertSolidSegments; + const int SolidCullScale = 3000; + + FrustumPlanes frustumPlanes; + Vec4f PortalClipPlane; +}; diff --git a/src/polyrenderer/scene/poly_decal.cpp b/src/polyrenderer/scene/poly_decal.cpp new file mode 100644 index 0000000000..c73def6d0e --- /dev/null +++ b/src/polyrenderer/scene/poly_decal.cpp @@ -0,0 +1,180 @@ +/* +** Handling drawing a decal +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_decal.h" +#include "polyrenderer/poly_renderer.h" +#include "a_sharedglobal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" + +void RenderPolyDecal::RenderWallDecals(const TriMatrix &worldToClip, const Vec4f &clipPlane, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue) +{ + if (line->linedef == nullptr && line->sidedef == nullptr) + return; + + for (DBaseDecal *decal = line->sidedef->AttachedDecals; decal != nullptr; decal = decal->WallNext) + { + RenderPolyDecal render; + render.Render(worldToClip, clipPlane, decal, line, subsectorDepth, stencilValue); + } +} + +void RenderPolyDecal::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, DBaseDecal *decal, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue) +{ + if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid()) + return; + + FTexture *tex = TexMan(decal->PicNum, true); + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + return; + + double edge_right = tex->GetWidth(); + double edge_left = tex->LeftOffset; + edge_right = (edge_right - edge_left) * decal->ScaleX; + edge_left *= decal->ScaleX; + + double dcx, dcy; + decal->GetXY(line->sidedef, dcx, dcy); + DVector2 decal_pos = { dcx, dcy }; + + DVector2 angvec = (line->v2->fPos() - line->v1->fPos()).Unit(); + DVector2 decal_left = decal_pos - edge_left * angvec; + DVector2 decal_right = decal_pos + edge_right * angvec; + + // Determine actor z + double zpos = decal->Z; + sector_t *front = line->frontsector; + sector_t *back = (line->backsector != nullptr) ? line->backsector : line->frontsector; + switch (decal->RenderFlags & RF_RELMASK) + { + default: + zpos = decal->Z; + break; + case RF_RELUPPER: + if (line->linedef->flags & ML_DONTPEGTOP) + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + else + zpos = decal->Z + back->GetPlaneTexZ(sector_t::ceiling); + break; + case RF_RELLOWER: + if (line->linedef->flags & ML_DONTPEGBOTTOM) + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + else + zpos = decal->Z + back->GetPlaneTexZ(sector_t::floor); + break; + case RF_RELMID: + if (line->linedef->flags & ML_DONTPEGBOTTOM) + zpos = decal->Z + front->GetPlaneTexZ(sector_t::floor); + else + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + + DVector2 spriteScale = { decal->ScaleX, decal->ScaleY }; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + bool flipTextureX = (decal->RenderFlags & RF_XFLIP) == RF_XFLIP; + + DVector2 points[2] = { decal_left, decal_right }; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + bool foggy = false; + int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Thread.Viewport->viewpoint.extralight << 4; + + std::pair offsets[4] = + { + { 0.0f, 1.0f }, + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + { 0.0f, 0.0f }, + }; + + for (int i = 0; i < 4; i++) + { + auto &p = (i == 0 || i == 3) ? points[0] : points[1]; + + vertices[i].x = (float)p.X; + vertices[i].y = (float)p.Y; + vertices[i].z = (float)(zpos + spriteHeight * offsets[i].second - spriteHeight * 0.5); + vertices[i].w = 1.0f; + vertices[i].varying[0] = (float)(offsets[i].first * tex->Scale.X); + vertices[i].varying[1] = (float)((1.0f - offsets[i].second) * tex->Scale.Y); + if (flipTextureX) + vertices[i].varying[0] = 1.0f - vertices[i].varying[0]; + } + + bool fullbrightSprite = (decal->RenderFlags & RF_FULLBRIGHT) == RF_FULLBRIGHT; + + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + args.uniforms.flags = 0; + args.SetColormap(front->ColorMap); + args.SetTexture(tex, decal->Translation, true); + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + if (fullbrightSprite || cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + args.uniforms.light = 256; + args.uniforms.flags |= TriUniforms::fixed_light; + } + else + { + args.uniforms.light = (uint32_t)((front->lightlevel + actualextralight) / 255.0f * 256.0f); + } + args.uniforms.subsectorDepth = subsectorDepth; + + if (PolyRenderer::Instance()->Thread.Viewport->RenderTarget->IsBgra()) + { + args.uniforms.color = 0xff000000 | decal->AlphaColor; + } + else + { + args.uniforms.color = ((uint32_t)decal->AlphaColor) >> 24; + } + + args.uniforms.srcalpha = (uint32_t)(decal->Alpha * 256.0 + 0.5); + args.uniforms.destalpha = 256 - args.uniforms.srcalpha; + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue; + //mode = R_SetPatchStyle (decal->RenderStyle, (float)decal->Alpha, decal->Translation, decal->AlphaColor); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + args.blendmode = TriBlendMode::Shaded; + args.subsectorTest = true; + args.writeStencil = false; + args.writeSubsector = false; + PolyTriangleDrawer::draw(args); +} diff --git a/src/polyrenderer/scene/poly_decal.h b/src/polyrenderer/scene/poly_decal.h new file mode 100644 index 0000000000..48907780a8 --- /dev/null +++ b/src/polyrenderer/scene/poly_decal.h @@ -0,0 +1,36 @@ +/* +** Handling drawing a decal +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class Vec4f; + +class RenderPolyDecal +{ +public: + static void RenderWallDecals(const TriMatrix &worldToClip, const Vec4f &clipPlane, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue); + +private: + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, DBaseDecal *decal, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue); +}; diff --git a/src/polyrenderer/scene/poly_particle.cpp b/src/polyrenderer/scene/poly_particle.cpp new file mode 100644 index 0000000000..b3887792e7 --- /dev/null +++ b/src/polyrenderer/scene/poly_particle.cpp @@ -0,0 +1,120 @@ +/* +** Particle drawing +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_particle.h" +#include "polyrenderer/poly_renderer.h" +#include "swrenderer/scene/r_light.h" + +void RenderPolyParticle::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue) +{ + DVector3 pos = particle->Pos; + double psize = particle->size / 8.0; + double zpos = pos.Z; + + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + + DVector2 points[2] = + { + { pos.X - viewpoint.Sin * psize, pos.Y + viewpoint.Cos * psize }, + { pos.X + viewpoint.Sin * psize, pos.Y - viewpoint.Cos * psize } + }; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + bool foggy = false; + int actualextralight = foggy ? 0 : viewpoint.extralight << 4; + + std::pair offsets[4] = + { + { 0.0f, 1.0f }, + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + { 0.0f, 0.0f }, + }; + + for (int i = 0; i < 4; i++) + { + auto &p = (i == 0 || i == 3) ? points[0] : points[1]; + + vertices[i].x = (float)p.X; + vertices[i].y = (float)p.Y; + vertices[i].z = (float)(zpos + psize * (2.0 * offsets[i].second - 1.0)); + vertices[i].w = 1.0f; + vertices[i].varying[0] = (float)(offsets[i].first); + vertices[i].varying[1] = (float)(1.0f - offsets[i].second); + } + + // int color = (particle->color >> 24) & 0xff; // pal index, I think + bool fullbrightSprite = particle->bright != 0; + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->ParticleGlobVis(foggy); + + if (fullbrightSprite || cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + args.uniforms.light = 256; + args.uniforms.flags = TriUniforms::fixed_light; + } + else + { + args.uniforms.light = (uint32_t)((sub->sector->lightlevel + actualextralight) / 255.0f * 256.0f); + args.uniforms.flags = 0; + } + args.uniforms.subsectorDepth = subsectorDepth; + + uint32_t alpha = (uint32_t)clamp(particle->alpha * 255.0f + 0.5f, 0.0f, 255.0f); + + if (PolyRenderer::Instance()->Thread.Viewport->RenderTarget->IsBgra()) + { + args.uniforms.color = (alpha << 24) | (particle->color & 0xffffff); + } + else + { + args.uniforms.color = ((uint32_t)particle->color) >> 24; + args.uniforms.srcalpha = alpha; + args.uniforms.destalpha = 255 - alpha; + } + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue; + args.SetColormap(sub->sector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + args.subsectorTest = true; + args.writeStencil = false; + args.writeSubsector = false; + args.blendmode = TriBlendMode::AlphaBlend; + PolyTriangleDrawer::draw(args); +} diff --git a/src/polyrenderer/scene/poly_particle.h b/src/polyrenderer/scene/poly_particle.h new file mode 100644 index 0000000000..b3b25b996d --- /dev/null +++ b/src/polyrenderer/scene/poly_particle.h @@ -0,0 +1,34 @@ +/* +** Handling drawing a particle +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" +#include "p_effect.h" + +class Vec4f; + +class RenderPolyParticle +{ +public: + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue); +}; diff --git a/src/polyrenderer/scene/poly_plane.cpp b/src/polyrenderer/scene/poly_plane.cpp new file mode 100644 index 0000000000..f37e707c76 --- /dev/null +++ b/src/polyrenderer/scene/poly_plane.cpp @@ -0,0 +1,508 @@ +/* +** Handling drawing a plane (ceiling, floor) +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_plane.h" +#include "poly_portal.h" +#include "polyrenderer/poly_renderer.h" +#include "r_sky.h" +#include "swrenderer/scene/r_light.h" + +EXTERN_CVAR(Int, r_3dfloors) + +void RenderPolyPlane::RenderPlanes(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector> §orPortals) +{ + RenderPolyPlane plane; + + if (r_3dfloors) + { + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + + auto frontsector = sub->sector; + auto &ffloors = frontsector->e->XFloor.ffloors; + + // 3D floor floors + for (int i = 0; i < (int)ffloors.Size(); i++) + { + F3DFloor *fakeFloor = ffloors[i]; + if (!(fakeFloor->flags & FF_EXISTS)) continue; + if (!fakeFloor->model) continue; + if (fakeFloor->bottom.plane->isSlope()) continue; + //if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) + // R_3D_AddHeight(fakeFloor->top.plane, frontsector); + if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; + if (fakeFloor->alpha == 0) continue; + if (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR) continue; + //fakeFloor->alpha + + double fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight < viewpoint.Pos.Z && fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)) + { + plane.Render3DFloor(worldToClip, clipPlane, sub, subsectorDepth, stencilValue, false, fakeFloor); + } + } + + // 3D floor ceilings + for (int i = 0; i < (int)ffloors.Size(); i++) + { + F3DFloor *fakeFloor = ffloors[i]; + if (!(fakeFloor->flags & FF_EXISTS)) continue; + if (!fakeFloor->model) continue; + if (fakeFloor->top.plane->isSlope()) continue; + //if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) + // R_3D_AddHeight(fakeFloor->bottom.plane, frontsector); + if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; + if (fakeFloor->alpha == 0) continue; + if (!(fakeFloor->flags & FF_THISINSIDE) && (fakeFloor->flags & (FF_SWIMMABLE | FF_INVERTSECTOR)) == (FF_SWIMMABLE | FF_INVERTSECTOR)) continue; + //fakeFloor->alpha + + double fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight > viewpoint.Pos.Z && fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)) + { + plane.Render3DFloor(worldToClip, clipPlane, sub, subsectorDepth, stencilValue, true, fakeFloor); + } + } + } + + plane.Render(worldToClip, clipPlane, cull, sub, subsectorDepth, stencilValue, true, skyCeilingHeight, sectorPortals); + plane.Render(worldToClip, clipPlane, cull, sub, subsectorDepth, stencilValue, false, skyFloorHeight, sectorPortals); +} + +void RenderPolyPlane::Render3DFloor(const TriMatrix &worldToClip, const Vec4f &clipPlane, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, F3DFloor *fakeFloor) +{ + FTextureID picnum = ceiling ? *fakeFloor->bottom.texture : *fakeFloor->top.texture; + FTexture *tex = TexMan(picnum); + if (tex->UseType == FTexture::TEX_Null) + return; + + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + int lightlevel = 255; + bool foggy = false; + if (cameraLight->FixedLightLevel() < 0 && sub->sector->e->XFloor.lightlist.Size()) + { + lightlist_t *light = P_GetPlaneLight(sub->sector, &sub->sector->ceilingplane, false); + //basecolormap = light->extra_colormap; + lightlevel = *light->p_lightlevel; + } + + UVTransform xform(ceiling ? fakeFloor->top.model->planes[sector_t::ceiling].xform : fakeFloor->top.model->planes[sector_t::floor].xform, tex); + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->SlopePlaneGlobVis(foggy) * 48.0f; + args.uniforms.light = (uint32_t)(lightlevel / 255.0f * 256.0f); + if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + args.uniforms.light = 256; + args.uniforms.flags = 0; + args.uniforms.subsectorDepth = subsectorDepth; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(sub->numlines); + if (!vertices) + return; + + if (ceiling) + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[sub->numlines - 1 - i] = PlaneVertex(line->v1, fakeFloor->bottom.plane->ZatPoint(line->v1), xform); + } + } + else + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[i] = PlaneVertex(line->v1, fakeFloor->top.plane->ZatPoint(line->v1), xform); + } + } + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = sub->numlines; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue + 1; + args.SetTexture(tex); + args.SetColormap(sub->sector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); +} + +void RenderPolyPlane::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector> §orPortals) +{ + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + std::vector portalSegments; + FSectorPortal *portal = nullptr;// sub->sector->ValidatePortal(ceiling ? sector_t::ceiling : sector_t::floor); + bool foggy = false; + PolyDrawSectorPortal *polyportal = nullptr; + if (portal && (portal->mFlags & PORTSF_INSKYBOX) == PORTSF_INSKYBOX) // Do not recurse into portals we already recursed into + portal = nullptr; + if (portal) + { + for (auto &p : sectorPortals) + { + if (p->Portal == portal) // To do: what other criteria do we need to check for? + { + polyportal = p.get(); + break; + } + } + if (!polyportal) + { + sectorPortals.push_back(std::make_unique(portal, ceiling)); + polyportal = sectorPortals.back().get(); + } + + // Calculate portal clipping + + DVector2 v; + bool inside = true; + double vdist = 1.0e10; + + portalSegments.reserve(sub->numlines); + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + + DVector2 pt1 = line->v1->fPos() - viewpoint.Pos; + DVector2 pt2 = line->v2->fPos() - viewpoint.Pos; + if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) + inside = false; + + double dist = pt1.LengthSquared(); + if (dist < vdist) + { + v = line->v1->fPos(); + vdist = dist; + } + dist = pt2.LengthSquared(); + if (dist < vdist) + { + v = line->v2->fPos(); + vdist = dist; + } + + int sx1, sx2; + LineSegmentRange range = cull.GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2); + if (range == LineSegmentRange::HasSegment) + portalSegments.push_back({ sx1, sx2 }); + } + + if (inside) + { + polyportal->PortalPlane = Vec4f(0.0f, 0.0f, 0.0f, 1.0f); + } + else if(polyportal->PortalPlane == Vec4f(0.0f) || Vec4f::dot(polyportal->PortalPlane, Vec4f((float)v.X, (float)v.Y, 0.0f, 1.0f)) > 0.0f) + { + DVector2 planePos = v; + DVector2 planeNormal = v - viewpoint.Pos; + planeNormal.MakeUnit(); + double planeD = -(planeNormal | (planePos + planeNormal * 0.001)); + polyportal->PortalPlane = Vec4f((float)planeNormal.X, (float)planeNormal.Y, 0.0f, (float)planeD); + } + } + + sector_t *fakesector = sub->sector->heightsec; + if (fakesector && (fakesector == sub->sector || (fakesector->MoreFlags & SECF_IGNOREHEIGHTSEC) == SECF_IGNOREHEIGHTSEC)) + fakesector = nullptr; + + bool fakeflooronly = fakesector && (fakesector->MoreFlags & SECF_FAKEFLOORONLY) == SECF_FAKEFLOORONLY; + + FTextureID picnum; + bool ccw; + sector_t *frontsector; + if (fakesector) + { + // Floor and ceiling texture needs to be swapped sometimes? Why?? :( + + if (viewpoint.Pos.Z < fakesector->floorplane.Zat0()) // In water + { + if (ceiling) + { + picnum = fakesector->GetTexture(sector_t::ceiling); + ceiling = false; + frontsector = fakesector; + ccw = false; + } + else + { + picnum = fakesector->GetTexture(sector_t::floor); + frontsector = sub->sector; + ccw = true; + } + } + else if (viewpoint.Pos.Z >= fakesector->ceilingplane.Zat0() && !fakeflooronly) // In ceiling water + { + if (ceiling) + { + picnum = fakesector->GetTexture(sector_t::ceiling); + frontsector = sub->sector; + ccw = true; + } + else + { + picnum = fakesector->GetTexture(sector_t::floor); + frontsector = fakesector; + ccw = false; + ceiling = true; + } + } + else if (!ceiling) // Water surface + { + picnum = fakesector->GetTexture(sector_t::ceiling); + frontsector = fakesector; + ccw = true; + } + else if (!fakeflooronly) // Ceiling water surface + { + picnum = fakesector->GetTexture(sector_t::floor); + frontsector = fakesector; + ccw = true; + } + else // Upper ceiling + { + picnum = sub->sector->GetTexture(sector_t::ceiling); + ccw = true; + frontsector = sub->sector; + } + } + else + { + picnum = sub->sector->GetTexture(ceiling ? sector_t::ceiling : sector_t::floor); + ccw = true; + frontsector = sub->sector; + } + + FTexture *tex = TexMan(picnum); + if (tex->UseType == FTexture::TEX_Null) + return; + + bool isSky = picnum == skyflatnum; + + UVTransform transform(ceiling ? frontsector->planes[sector_t::ceiling].xform : frontsector->planes[sector_t::floor].xform, tex); + + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->SlopePlaneGlobVis(foggy) * 48.0f; + args.uniforms.light = (uint32_t)(frontsector->lightlevel / 255.0f * 256.0f); + if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + args.uniforms.light = 256; + args.uniforms.flags = 0; + args.uniforms.subsectorDepth = isSky ? RenderPolyScene::SkySubsectorDepth : subsectorDepth; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(sub->numlines); + if (!vertices) + return; + + if (ceiling) + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[sub->numlines - 1 - i] = PlaneVertex(line->v1, isSky ? skyHeight : frontsector->ceilingplane.ZatPoint(line->v1), transform); + } + } + else + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[i] = PlaneVertex(line->v1, isSky ? skyHeight : frontsector->floorplane.ZatPoint(line->v1), transform); + } + } + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = sub->numlines; + args.mode = TriangleDrawMode::Fan; + args.ccw = ccw; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue + 1; + args.SetColormap(frontsector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + + if (!isSky) + { + if (!portal) + { + args.SetTexture(tex); + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); + } + else + { + args.stencilwritevalue = polyportal->StencilValue; + args.writeColor = false; + args.writeSubsector = false; + PolyTriangleDrawer::draw(args); + polyportal->Shape.push_back({ args.vinput, args.vcount, args.ccw, subsectorDepth }); + polyportal->Segments.insert(polyportal->Segments.end(), portalSegments.begin(), portalSegments.end()); + } + } + else + { + if (portal) + { + args.stencilwritevalue = polyportal->StencilValue; + polyportal->Shape.push_back({ args.vinput, args.vcount, args.ccw, subsectorDepth }); + polyportal->Segments.insert(polyportal->Segments.end(), portalSegments.begin(), portalSegments.end()); + } + else + { + args.stencilwritevalue = 255; + } + + args.writeColor = false; + args.writeSubsector = false; + PolyTriangleDrawer::draw(args); + + for (uint32_t i = 0; i < sub->numlines; i++) + { + TriVertex *wallvert = PolyVertexBuffer::GetVertices(4); + if (!wallvert) + return; + + seg_t *line = &sub->firstline[i]; + + double skyBottomz1 = frontsector->ceilingplane.ZatPoint(line->v1); + double skyBottomz2 = frontsector->ceilingplane.ZatPoint(line->v2); + if (line->backsector) + { + sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector; + + double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1); + double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1); + double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2); + double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2); + + double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1); + double backfloorz1 = backsector->floorplane.ZatPoint(line->v1); + double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2); + double backfloorz2 = backsector->floorplane.ZatPoint(line->v2); + + double topceilz1 = frontceilz1; + double topceilz2 = frontceilz2; + double topfloorz1 = MIN(backceilz1, frontceilz1); + double topfloorz2 = MIN(backceilz2, frontceilz2); + double bottomceilz1 = MAX(frontfloorz1, backfloorz1); + double bottomceilz2 = MAX(frontfloorz2, backfloorz2); + double middleceilz1 = topfloorz1; + double middleceilz2 = topfloorz2; + double middlefloorz1 = MIN(bottomceilz1, middleceilz1); + double middlefloorz2 = MIN(bottomceilz2, middleceilz2); + + bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum; + + bool closedSector = backceilz1 == backfloorz1 && backceilz2 == backfloorz2; + if (ceiling && bothSkyCeiling && closedSector) + { + skyBottomz1 = middlefloorz1; + skyBottomz2 = middlefloorz2; + } + else if (bothSkyCeiling) + { + continue; + } + } + + if (ceiling) + { + wallvert[0] = PlaneVertex(line->v1, skyHeight, transform); + wallvert[1] = PlaneVertex(line->v2, skyHeight, transform); + wallvert[2] = PlaneVertex(line->v2, skyBottomz2, transform); + wallvert[3] = PlaneVertex(line->v1, skyBottomz1, transform); + } + else + { + wallvert[0] = PlaneVertex(line->v1, frontsector->floorplane.ZatPoint(line->v1), transform); + wallvert[1] = PlaneVertex(line->v2, frontsector->floorplane.ZatPoint(line->v2), transform); + wallvert[2] = PlaneVertex(line->v2, skyHeight, transform); + wallvert[3] = PlaneVertex(line->v1, skyHeight, transform); + } + + args.vinput = wallvert; + args.vcount = 4; + PolyTriangleDrawer::draw(args); + + if (portal) + { + polyportal->Shape.push_back({ args.vinput, args.vcount, args.ccw, subsectorDepth }); + polyportal->Segments.insert(polyportal->Segments.end(), portalSegments.begin(), portalSegments.end()); + } + } + } +} + +TriVertex RenderPolyPlane::PlaneVertex(vertex_t *v1, double height, const UVTransform &transform) +{ + TriVertex v; + v.x = (float)v1->fPos().X; + v.y = (float)v1->fPos().Y; + v.z = (float)height; + v.w = 1.0f; + v.varying[0] = transform.GetU(v.x, v.y); + v.varying[1] = transform.GetV(v.x, v.y); + return v; +} + +RenderPolyPlane::UVTransform::UVTransform(const FTransform &transform, FTexture *tex) +{ + if (tex) + { + xscale = (float)(transform.xScale * tex->Scale.X / tex->GetWidth()); + yscale = (float)(transform.yScale * tex->Scale.Y / tex->GetHeight()); + + double planeang = (transform.Angle + transform.baseAngle).Radians(); + cosine = (float)cos(planeang); + sine = (float)sin(planeang); + + xOffs = (float)transform.xOffs; + yOffs = (float)transform.yOffs; + } + else + { + xscale = 1.0f / 64.0f; + yscale = 1.0f / 64.0f; + cosine = 1.0f; + sine = 0.0f; + xOffs = 0.0f; + yOffs = 0.0f; + } +} + +float RenderPolyPlane::UVTransform::GetU(float x, float y) const +{ + return (xOffs + x * cosine - y * sine) * xscale; +} + +float RenderPolyPlane::UVTransform::GetV(float x, float y) const +{ + return (yOffs - x * sine - y * cosine) * yscale; +} diff --git a/src/polyrenderer/scene/poly_plane.h b/src/polyrenderer/scene/poly_plane.h new file mode 100644 index 0000000000..bf844dcc27 --- /dev/null +++ b/src/polyrenderer/scene/poly_plane.h @@ -0,0 +1,54 @@ +/* +** Handling drawing a plane (ceiling, floor) +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class PolyDrawSectorPortal; +class PolyCull; +class Vec4f; + +class RenderPolyPlane +{ +public: + static void RenderPlanes(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector> §orPortals); + +private: + struct UVTransform + { + UVTransform(const FTransform &transform, FTexture *tex); + + float GetU(float x, float y) const; + float GetV(float x, float y) const; + + float xscale; + float yscale; + float cosine; + float sine; + float xOffs, yOffs; + }; + + void Render3DFloor(const TriMatrix &worldToClip, const Vec4f &clipPlane, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, F3DFloor *fakefloor); + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector> §orPortals); + TriVertex PlaneVertex(vertex_t *v1, double height, const UVTransform &transform); +}; diff --git a/src/polyrenderer/scene/poly_playersprite.cpp b/src/polyrenderer/scene/poly_playersprite.cpp new file mode 100644 index 0000000000..eb40b05d2a --- /dev/null +++ b/src/polyrenderer/scene/poly_playersprite.cpp @@ -0,0 +1,448 @@ +/* +** Handling drawing a player sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_playersprite.h" +#include "polyrenderer/poly_renderer.h" +#include "d_player.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" + +EXTERN_CVAR(Bool, r_drawplayersprites) +EXTERN_CVAR(Bool, r_deathcamera) +EXTERN_CVAR(Bool, st_scale) +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) +EXTERN_CVAR(Bool, r_shadercolormaps) + +void RenderPolyPlayerSprites::Render() +{ + // This code cannot be moved directly to RenderRemainingSprites because the engine + // draws the canvas textures between this call and the final call to RenderRemainingSprites.. + + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + + if (!r_drawplayersprites || + !viewpoint.camera || + !viewpoint.camera->player || + (players[consoleplayer].cheats & CF_CHASECAM) || + (r_deathcamera && viewpoint.camera->health <= 0)) + return; + + float bobx, boby; + P_BobWeapon(viewpoint.camera->player, &bobx, &boby, viewpoint.TicFrac); + + // Interpolate the main weapon layer once so as to be able to add it to other layers. + double wx, wy; + DPSprite *weapon = viewpoint.camera->player->FindPSprite(PSP_WEAPON); + if (weapon) + { + if (weapon->firstTic) + { + wx = weapon->x; + wy = weapon->y; + } + else + { + wx = weapon->oldx + (weapon->x - weapon->oldx) * viewpoint.TicFrac; + wy = weapon->oldy + (weapon->y - weapon->oldy) * viewpoint.TicFrac; + } + } + else + { + wx = 0; + wy = 0; + } + + for (DPSprite *sprite = viewpoint.camera->player->psprites; sprite != nullptr; sprite = sprite->GetNext()) + { + // [RH] Don't draw the targeter's crosshair if the player already has a crosshair set. + // It's possible this psprite's caller is now null but the layer itself hasn't been destroyed + // because it didn't tick yet (if we typed 'take all' while in the console for example). + // In this case let's simply not draw it to avoid crashing. + if ((sprite->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && sprite->GetCaller() != nullptr) + { + RenderSprite(sprite, viewpoint.camera, bobx, boby, wx, wy, viewpoint.TicFrac); + } + } +} + +void RenderPolyPlayerSprites::RenderRemainingSprites() +{ + for (auto &sprite : ScreenSprites) + sprite.Render(); + ScreenSprites.clear(); +} + +void RenderPolyPlayerSprites::RenderSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac) +{ + // decide which patch to use + if ((unsigned)sprite->GetSprite() >= (unsigned)sprites.Size()) + { + DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite number %i\n", sprite->GetSprite()); + return; + } + + spritedef_t *def = &sprites[sprite->GetSprite()]; + if (sprite->GetFrame() >= def->numframes) + { + DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite frame %i : %i\n", sprite->GetSprite(), sprite->GetFrame()); + return; + } + + spriteframe_t *frame = &SpriteFrames[def->spriteframes + sprite->GetFrame()]; + FTextureID picnum = frame->Texture[0]; + bool flip = (frame->Flip & 1) != 0; + + FTexture *tex = TexMan(picnum); + if (tex->UseType == FTexture::TEX_Null) + return; + + // Can't interpolate the first tic. + if (sprite->firstTic) + { + sprite->firstTic = false; + sprite->oldx = sprite->x; + sprite->oldy = sprite->y; + } + + double sx = sprite->oldx + (sprite->x - sprite->oldx) * ticfrac; + double sy = sprite->oldy + (sprite->y - sprite->oldy) * ticfrac; + + if (sprite->Flags & PSPF_ADDBOB) + { + sx += bobx; + sy += boby; + } + + if (sprite->Flags & PSPF_ADDWEAPON && sprite->GetID() != PSP_WEAPON) + { + sx += wx; + sy += wy; + } + + auto viewport = PolyRenderer::Instance()->Thread.Viewport.get(); + const auto &viewpoint = viewport->viewpoint; + + double pspritexscale = PolyRenderer::Instance()->Thread.Viewport->viewwindow.centerxwide / 160.0; + double pspriteyscale = pspritexscale * viewport->YaspectMul; + double pspritexiscale = 1 / pspritexscale; + + // calculate edges of the shape + double tx = sx - BaseXCenter; + + tx -= tex->GetScaledLeftOffset(); + int x1 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); + + // off the right side + if (x1 > viewwidth) + return; + + tx += tex->GetScaledWidth(); + int x2 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); + + // off the left side + if (x2 <= 0) + return; + + double texturemid = (BaseYCenter - sy) * tex->Scale.Y + tex->TopOffset; + + // Adjust PSprite for fullscreen views + if (viewpoint.camera->player && (viewport->RenderTarget != screen || viewheight == viewport->RenderTarget->GetHeight() || (viewport->RenderTarget->GetWidth() > (BaseXCenter * 2) && !st_scale))) + { + AWeapon *weapon = dyn_cast(sprite->GetCaller()); + if (weapon != nullptr && weapon->YAdjust != 0) + { + if (viewport->RenderTarget != screen || viewheight == viewport->RenderTarget->GetHeight()) + { + texturemid -= weapon->YAdjust; + } + else + { + texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust; + } + } + } + + // Move the weapon down for 1280x1024. + if (sprite->GetID() < PSP_TARGETCENTER) + { + texturemid -= AspectPspriteOffset(PolyRenderer::Instance()->Thread.Viewport->viewwindow.WidescreenRatio); + } + + int clipped_x1 = MAX(x1, 0); + int clipped_x2 = MIN(x2, viewwidth); + double xscale = pspritexscale / tex->Scale.X; + double yscale = pspriteyscale / tex->Scale.Y; + uint32_t translation = 0; // [RH] Use default colors + + double xiscale, startfrac; + if (flip) + { + xiscale = -pspritexiscale * tex->Scale.X; + startfrac = 1; + } + else + { + xiscale = pspritexiscale * tex->Scale.X; + startfrac = 0; + } + + if (clipped_x1 > x1) + startfrac += xiscale * (clipped_x1 - x1); + + bool noaccel = false; + + FDynamicColormap *basecolormap = viewpoint.sector->ColorMap; + FDynamicColormap *colormap_to_use = basecolormap; + + int ColormapNum = 0; + FSWColormap *BaseColormap = basecolormap; + float Alpha = 0; + FRenderStyle RenderStyle; + RenderStyle = STYLE_Normal; + + bool foggy = false; + int actualextralight = foggy ? 0 : viewpoint.extralight << 4; + int spriteshade = swrenderer::LightVisibility::LightLevelToShade(owner->Sector->lightlevel + actualextralight, foggy); + double minz = double((2048 * 4) / double(1 << 20)); + ColormapNum = GETPALOOKUP(swrenderer::LightVisibility::Instance()->SpriteGlobVis(foggy) / minz, spriteshade); + + if (sprite->GetID() < PSP_TARGETCENTER) + { + Alpha = float(owner->Alpha); + RenderStyle = owner->RenderStyle; + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + INTBOOL invertcolormap = (RenderStyle.Flags & STYLEF_InvertOverlay); + + if (RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + FDynamicColormap *mybasecolormap = basecolormap; + + if (RenderStyle.Flags & STYLEF_FadeToBlack) + { + if (invertcolormap) + { // Fade to white + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255, 255, 255), mybasecolormap->Desaturate); + invertcolormap = false; + } + else + { // Fade to black + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0, 0, 0), mybasecolormap->Desaturate); + } + } + + /* + if (swrenderer::realfixedcolormap != nullptr && (!swrenderer::r_swtruecolor || (r_shadercolormaps && screen->Accel2D))) + { // fixed color + BaseColormap = swrenderer::realfixedcolormap; + ColormapNum = 0; + } + else + { + if (invertcolormap) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); + } + if (swrenderer::fixedlightlev >= 0) + { + BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : mybasecolormap; + ColormapNum = swrenderer::fixedlightlev >> COLORMAPSHIFT; + } + else if (!foggy && sprite->GetState()->GetFullbright()) + { // full bright + BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : mybasecolormap; // [RH] use basecolormap + ColormapNum = 0; + } + else + { // local light + BaseColormap = mybasecolormap; + ColormapNum = GETPALOOKUP(0, spriteshade); + } + } + */ + + if (viewpoint.camera->Inventory != nullptr) + { + visstyle_t visstyle; + visstyle.Alpha = Alpha; + visstyle.RenderStyle = STYLE_Count; + visstyle.Invert = false; + + viewpoint.camera->Inventory->AlterWeaponSprite(&visstyle); + + Alpha = visstyle.Alpha; + + if (visstyle.RenderStyle != STYLE_Count) + { + RenderStyle = visstyle.RenderStyle; + } + + if (visstyle.Invert) + { + BaseColormap = &SpecialColormaps[INVERSECOLORMAP]; + ColormapNum = 0; + if (BaseColormap->Maps < mybasecolormap->Maps || BaseColormap->Maps >= mybasecolormap->Maps + NUMCOLORMAPS * 256) + { + noaccel = true; + } + } + } + + // If we're drawing with a special colormap, but shaders for them are disabled, do + // not accelerate. + if (!r_shadercolormaps && (BaseColormap >= &SpecialColormaps[0] && + BaseColormap <= &SpecialColormaps.Last())) + { + noaccel = true; + } + // If drawing with a BOOM colormap, disable acceleration. + if (mybasecolormap == &NormalLight && NormalLight.Maps != realcolormaps.Maps) + { + noaccel = true; + } + // If the main colormap has fixed lights, and this sprite is being drawn with that + // colormap, disable acceleration so that the lights can remain fixed. + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + if (!noaccel && cameraLight->ShaderColormap() == nullptr && + NormalLightHasFixedLights && mybasecolormap == &NormalLight && + tex->UseBasePalette()) + { + noaccel = true; + } + // [SP] If emulating GZDoom fullbright, disable acceleration + if (r_fullbrightignoresectorcolor && cameraLight->FixedLightLevel() >= 0) + mybasecolormap = &FullNormalLight; + if (r_fullbrightignoresectorcolor && !foggy && sprite->GetState()->GetFullbright()) + mybasecolormap = &FullNormalLight; + colormap_to_use = mybasecolormap; + } + + // Check for hardware-assisted 2D. If it's available, and this sprite is not + // fuzzy, don't draw it until after the switch to 2D mode. + if (!noaccel && PolyRenderer::Instance()->Thread.Viewport->RenderTarget == screen && (DFrameBuffer *)screen->Accel2D) + { + FRenderStyle style = RenderStyle; + style.CheckFuzz(); + if (style.BlendOp != STYLEOP_Fuzz) + { + PolyScreenSprite screenSprite; + screenSprite.Pic = tex; + screenSprite.X1 = viewwindowx + x1; + screenSprite.Y1 = viewwindowy + viewheight / 2 - texturemid * yscale - 0.5; + screenSprite.Width = tex->GetWidth() * xscale; + screenSprite.Height = tex->GetHeight() * yscale; + screenSprite.Translation = TranslationToTable(translation); + //screenSprite.Translation = translation; + screenSprite.Flip = xiscale < 0; + screenSprite.Alpha = Alpha; + screenSprite.RenderStyle = RenderStyle; + screenSprite.BaseColormap = BaseColormap; + screenSprite.ColormapNum = ColormapNum; + screenSprite.Colormap = colormap_to_use; + ScreenSprites.push_back(screenSprite); + return; + } + } + + // To do: draw sprite same way as R_DrawVisSprite(vis) here + + // Draw the fuzzy weapon: + FRenderStyle style = RenderStyle; + style.CheckFuzz(); + if (style.BlendOp == STYLEOP_Fuzz) + { + RenderStyle = LegacyRenderStyles[STYLE_Shadow]; + + PolyScreenSprite screenSprite; + screenSprite.Pic = tex; + screenSprite.X1 = viewwindowx + x1; + screenSprite.Y1 = viewwindowy + viewheight / 2 - texturemid * yscale - 0.5; + screenSprite.Width = tex->GetWidth() * xscale; + screenSprite.Height = tex->GetHeight() * yscale; + screenSprite.Translation = TranslationToTable(translation); + screenSprite.Flip = xiscale < 0; + screenSprite.Alpha = Alpha; + screenSprite.RenderStyle = RenderStyle; + screenSprite.BaseColormap = BaseColormap; + screenSprite.ColormapNum = ColormapNum; + screenSprite.Colormap = colormap_to_use; + ScreenSprites.push_back(screenSprite); + } +} + +void PolyScreenSprite::Render() +{ + FSpecialColormap *special = nullptr; + FColormapStyle colormapstyle; + PalEntry overlay = 0; + bool usecolormapstyle = false; + if (BaseColormap >= &SpecialColormaps[0] && + BaseColormap < &SpecialColormaps[SpecialColormaps.Size()]) + { + special = static_cast(BaseColormap); + } + else if (Colormap->Color == PalEntry(255, 255, 255) && + Colormap->Desaturate == 0) + { + overlay = Colormap->Fade; + overlay.a = uint8_t(ColormapNum * 255 / NUMCOLORMAPS); + } + else + { + usecolormapstyle = true; + colormapstyle.Color = Colormap->Color; + colormapstyle.Fade = Colormap->Fade; + colormapstyle.Desaturate = Colormap->Desaturate; + colormapstyle.FadeLevel = ColormapNum / float(NUMCOLORMAPS); + } + + screen->DrawTexture(Pic, + X1, + Y1, + DTA_DestWidthF, Width, + DTA_DestHeightF, Height, + DTA_TranslationIndex, Translation, + DTA_FlipX, Flip, + DTA_TopOffset, 0, + DTA_LeftOffset, 0, + DTA_ClipLeft, viewwindowx, + DTA_ClipTop, viewwindowy, + DTA_ClipRight, viewwindowx + viewwidth, + DTA_ClipBottom, viewwindowy + viewheight, + DTA_Alpha, Alpha, + DTA_RenderStyle, RenderStyle, + DTA_FillColor, FillColor, + DTA_SpecialColormap, special, + DTA_ColorOverlay, overlay.d, + DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : nullptr, + TAG_DONE); +} diff --git a/src/polyrenderer/scene/poly_playersprite.h b/src/polyrenderer/scene/poly_playersprite.h new file mode 100644 index 0000000000..97c6e61183 --- /dev/null +++ b/src/polyrenderer/scene/poly_playersprite.h @@ -0,0 +1,65 @@ +/* +** Handling drawing a player sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_defs.h" + +class PolyScreenSprite; +class DPSprite; +struct FSWColormap; + +class RenderPolyPlayerSprites +{ +public: + void Render(); + void RenderRemainingSprites(); + +private: + void RenderSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac); + + const int BaseXCenter = 160; + const int BaseYCenter = 100; + + std::vector ScreenSprites; +}; + +// DScreen accelerated sprite to be rendered +class PolyScreenSprite +{ +public: + void Render(); + + FTexture *Pic = nullptr; + double X1 = 0.0; + double Y1 = 0.0; + double Width = 0.0; + double Height = 0.0; + FRemapTable *Translation = nullptr; + bool Flip = false; + float Alpha = 1; + FRenderStyle RenderStyle; + FSWColormap *BaseColormap = nullptr; + int ColormapNum = 0; + uint32_t FillColor = 0; + FDynamicColormap *Colormap = nullptr; +}; diff --git a/src/polyrenderer/scene/poly_portal.cpp b/src/polyrenderer/scene/poly_portal.cpp new file mode 100644 index 0000000000..d897936467 --- /dev/null +++ b/src/polyrenderer/scene/poly_portal.cpp @@ -0,0 +1,311 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "p_maputl.h" +#include "sbar.h" +#include "g_levellocals.h" +#include "r_data/r_translate.h" +#include "poly_portal.h" +#include "polyrenderer/poly_renderer.h" +#include "swrenderer/scene/r_light.h" +#include "gl/data/gl_data.h" + +///////////////////////////////////////////////////////////////////////////// + +PolyDrawSectorPortal::PolyDrawSectorPortal(FSectorPortal *portal, bool ceiling) : Portal(portal), Ceiling(ceiling) +{ + StencilValue = PolyRenderer::Instance()->GetNextStencilValue(); +} + +void PolyDrawSectorPortal::Render(int portalDepth) +{ + if (Portal->mType == PORTS_HORIZON || Portal->mType == PORTS_PLANE) + return; + + SaveGlobals(); + + // To do: get this information from PolyRenderer instead of duplicating the code.. + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + const auto &viewwindow = PolyRenderer::Instance()->Thread.Viewport->viewwindow; + double radPitch = viewpoint.Angles.Pitch.Normalized180().Radians(); + double angx = cos(radPitch); + double angy = sin(radPitch) * glset.pixelstretch; + double alen = sqrt(angx*angx + angy*angy); + float adjustedPitch = (float)asin(angy / alen); + float adjustedViewAngle = (float)(viewpoint.Angles.Yaw - 90).Radians(); + float ratio = viewwindow.WidescreenRatio; + float fovratio = (viewwindow.WidescreenRatio >= 1.3f) ? 1.333333f : ratio; + float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(viewpoint.FieldOfView.Radians() / 2) / fovratio)).Degrees); + TriMatrix worldToView = + TriMatrix::rotate(adjustedPitch, 1.0f, 0.0f, 0.0f) * + TriMatrix::rotate(adjustedViewAngle, 0.0f, -1.0f, 0.0f) * + TriMatrix::scale(1.0f, glset.pixelstretch, 1.0f) * + TriMatrix::swapYZ() * + TriMatrix::translate((float)-viewpoint.Pos.X, (float)-viewpoint.Pos.Y, (float)-viewpoint.Pos.Z); + TriMatrix worldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; + + RenderPortal.SetViewpoint(worldToClip, PortalPlane, StencilValue); + RenderPortal.SetPortalSegments(Segments); + RenderPortal.Render(portalDepth); + + RestoreGlobals(); +} + +void PolyDrawSectorPortal::RenderTranslucent(int portalDepth) +{ + if (Portal->mType == PORTS_HORIZON || Portal->mType == PORTS_PLANE) + return; + + SaveGlobals(); + + RenderPortal.RenderTranslucent(portalDepth); + + RestoreGlobals(); +} + +void PolyDrawSectorPortal::SaveGlobals() +{ + auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + const auto &viewwindow = PolyRenderer::Instance()->Thread.Viewport->viewwindow; + + savedextralight = viewpoint.extralight; + savedpos = viewpoint.Pos; + savedangles = viewpoint.Angles; + savedvisibility = swrenderer::LightVisibility::Instance()->GetVisibility(); + savedcamera = viewpoint.camera; + savedsector = viewpoint.sector; + + if (Portal->mType == PORTS_SKYVIEWPOINT) + { + // Don't let gun flashes brighten the sky box + AActor *sky = Portal->mSkybox; + viewpoint.extralight = 0; + swrenderer::LightVisibility::Instance()->SetVisibility(PolyRenderer::Instance()->Thread.Viewport.get(), sky->args[0] * 0.25f); + viewpoint.Pos = sky->InterpolatedPosition(viewpoint.TicFrac); + viewpoint.Angles.Yaw = savedangles.Yaw + (sky->PrevAngles.Yaw + deltaangle(sky->PrevAngles.Yaw, sky->Angles.Yaw) * viewpoint.TicFrac); + } + else //if (Portal->mType == PORTS_STACKEDSECTORTHING || Portal->mType == PORTS_PORTAL || Portal->mType == PORTS_LINKEDPORTAL) + { + //extralight = pl->extralight; + //swrenderer::R_SetVisibility(pl->visibility); + viewpoint.Pos.X += Portal->mDisplacement.X; + viewpoint.Pos.Y += Portal->mDisplacement.Y; + } + + viewpoint.camera = nullptr; + viewpoint.sector = Portal->mDestination; + R_SetViewAngle(viewpoint, viewwindow); + + Portal->mFlags |= PORTSF_INSKYBOX; + if (Portal->mPartner > 0) level.sectorPortals[Portal->mPartner].mFlags |= PORTSF_INSKYBOX; +} + +void PolyDrawSectorPortal::RestoreGlobals() +{ + Portal->mFlags &= ~PORTSF_INSKYBOX; + if (Portal->mPartner > 0) level.sectorPortals[Portal->mPartner].mFlags &= ~PORTSF_INSKYBOX; + + auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + const auto &viewwindow = PolyRenderer::Instance()->Thread.Viewport->viewwindow; + + viewpoint.camera = savedcamera; + viewpoint.sector = savedsector; + viewpoint.Pos = savedpos; + swrenderer::LightVisibility::Instance()->SetVisibility(PolyRenderer::Instance()->Thread.Viewport.get(), savedvisibility); + viewpoint.extralight = savedextralight; + viewpoint.Angles = savedangles; + R_SetViewAngle(viewpoint, viewwindow); +} + +///////////////////////////////////////////////////////////////////////////// + +PolyDrawLinePortal::PolyDrawLinePortal(FLinePortal *portal) : Portal(portal) +{ + StencilValue = PolyRenderer::Instance()->GetNextStencilValue(); +} + +PolyDrawLinePortal::PolyDrawLinePortal(line_t *mirror) : Mirror(mirror) +{ + StencilValue = PolyRenderer::Instance()->GetNextStencilValue(); +} + +void PolyDrawLinePortal::Render(int portalDepth) +{ + SaveGlobals(); + + // To do: get this information from PolyRenderer instead of duplicating the code.. + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + const auto &viewwindow = PolyRenderer::Instance()->Thread.Viewport->viewwindow; + double radPitch = viewpoint.Angles.Pitch.Normalized180().Radians(); + double angx = cos(radPitch); + double angy = sin(radPitch) * glset.pixelstretch; + double alen = sqrt(angx*angx + angy*angy); + float adjustedPitch = (float)asin(angy / alen); + float adjustedViewAngle = (float)(viewpoint.Angles.Yaw - 90).Radians(); + float ratio = viewwindow.WidescreenRatio; + float fovratio = (viewwindow.WidescreenRatio >= 1.3f) ? 1.333333f : ratio; + float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(viewpoint.FieldOfView.Radians() / 2) / fovratio)).Degrees); + TriMatrix worldToView = + TriMatrix::rotate(adjustedPitch, 1.0f, 0.0f, 0.0f) * + TriMatrix::rotate(adjustedViewAngle, 0.0f, -1.0f, 0.0f) * + TriMatrix::scale(1.0f, glset.pixelstretch, 1.0f) * + TriMatrix::swapYZ() * + TriMatrix::translate((float)-viewpoint.Pos.X, (float)-viewpoint.Pos.Y, (float)-viewpoint.Pos.Z); + if (Mirror) + worldToView = TriMatrix::scale(-1.0f, 1.0f, 1.0f) * worldToView; + TriMatrix worldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; + + // Calculate plane clipping + line_t *clipLine = Portal ? Portal->mDestination : Mirror; + DVector2 planePos = clipLine->v1->fPos(); + DVector2 planeNormal = (clipLine->v2->fPos() - clipLine->v1->fPos()).Rotated90CW(); + planeNormal.MakeUnit(); + double planeD = -(planeNormal | (planePos + planeNormal * 0.001)); + Vec4f portalPlane((float)planeNormal.X, (float)planeNormal.Y, 0.0f, (float)planeD); + + RenderPortal.SetViewpoint(worldToClip, portalPlane, StencilValue); + RenderPortal.SetPortalSegments(Segments); + RenderPortal.Render(portalDepth); + + RestoreGlobals(); +} + +void PolyDrawLinePortal::RenderTranslucent(int portalDepth) +{ + SaveGlobals(); + RenderPortal.RenderTranslucent(portalDepth); + RestoreGlobals(); +} + +void PolyDrawLinePortal::SaveGlobals() +{ + auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + const auto &viewwindow = PolyRenderer::Instance()->Thread.Viewport->viewwindow; + + savedextralight = viewpoint.extralight; + savedpos = viewpoint.Pos; + savedangles = viewpoint.Angles; + savedcamera = viewpoint.camera; + savedsector = viewpoint.sector; + savedinvisibility = viewpoint.camera ? (viewpoint.camera->renderflags & RF_INVISIBLE) == RF_INVISIBLE : false; + savedViewPath[0] = viewpoint.Path[0]; + savedViewPath[1] = viewpoint.Path[1]; + + if (Mirror) + { + DAngle startang = viewpoint.Angles.Yaw; + DVector3 startpos = viewpoint.Pos; + + vertex_t *v1 = Mirror->v1; + + // Reflect the current view behind the mirror. + if (Mirror->Delta().X == 0) + { // vertical mirror + viewpoint.Pos.X = v1->fX() - startpos.X + v1->fX(); + } + else if (Mirror->Delta().Y == 0) + { // horizontal mirror + viewpoint.Pos.Y = v1->fY() - startpos.Y + v1->fY(); + } + else + { // any mirror + vertex_t *v2 = Mirror->v2; + + double dx = v2->fX() - v1->fX(); + double dy = v2->fY() - v1->fY(); + double x1 = v1->fX(); + double y1 = v1->fY(); + double x = startpos.X; + double y = startpos.Y; + + // the above two cases catch len == 0 + double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); + + viewpoint.Pos.X = (x1 + r * dx) * 2 - x; + viewpoint.Pos.Y = (y1 + r * dy) * 2 - y; + } + viewpoint.Angles.Yaw = Mirror->Delta().Angle() * 2 - startang; + + if (viewpoint.camera) + viewpoint.camera->renderflags &= ~RF_INVISIBLE; + } + else + { + auto src = Portal->mOrigin; + auto dst = Portal->mDestination; + + P_TranslatePortalXY(src, viewpoint.Pos.X, viewpoint.Pos.Y); + P_TranslatePortalZ(src, viewpoint.Pos.Z); + P_TranslatePortalAngle(src, viewpoint.Angles.Yaw); + P_TranslatePortalXY(src, viewpoint.Path[0].X, viewpoint.Path[0].Y); + P_TranslatePortalXY(src, viewpoint.Path[1].X, viewpoint.Path[1].Y); + + if (!viewpoint.showviewer && viewpoint.camera && P_PointOnLineSidePrecise(viewpoint.Path[0], dst) != P_PointOnLineSidePrecise(viewpoint.Path[1], dst)) + { + double distp = (viewpoint.Path[0] - viewpoint.Path[1]).Length(); + if (distp > EQUAL_EPSILON) + { + double dist1 = (viewpoint.Pos - viewpoint.Path[0]).Length(); + double dist2 = (viewpoint.Pos - viewpoint.Path[1]).Length(); + + if (dist1 + dist2 < distp + 1) + { + viewpoint.camera->renderflags |= RF_INVISIBLE; + } + } + } + } + + //camera = nullptr; + //viewsector = R_PointInSubsector(ViewPos)->sector; + R_SetViewAngle(viewpoint, viewwindow); + + if (Mirror) + PolyTriangleDrawer::toggle_mirror(); +} + +void PolyDrawLinePortal::RestoreGlobals() +{ + auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + const auto &viewwindow = PolyRenderer::Instance()->Thread.Viewport->viewwindow; + if (viewpoint.camera) + { + if (savedinvisibility) + viewpoint.camera->renderflags |= RF_INVISIBLE; + else + viewpoint.camera->renderflags &= ~RF_INVISIBLE; + } + viewpoint.camera = savedcamera; + viewpoint.sector = savedsector; + viewpoint.Pos = savedpos; + viewpoint.extralight = savedextralight; + viewpoint.Angles = savedangles; + viewpoint.Path[0] = savedViewPath[0]; + viewpoint.Path[1] = savedViewPath[1]; + R_SetViewAngle(viewpoint, viewwindow); + + if (Mirror) + PolyTriangleDrawer::toggle_mirror(); +} diff --git a/src/polyrenderer/scene/poly_portal.h b/src/polyrenderer/scene/poly_portal.h new file mode 100644 index 0000000000..3f8d526426 --- /dev/null +++ b/src/polyrenderer/scene/poly_portal.h @@ -0,0 +1,100 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "poly_scene.h" + +struct PolyPortalVertexRange +{ + PolyPortalVertexRange(const TriVertex *vertices, int count, bool ccw, uint32_t subsectorDepth) : Vertices(vertices), Count(count), Ccw(ccw), SubsectorDepth(subsectorDepth) { } + const TriVertex *Vertices; + int Count; + bool Ccw; + uint32_t SubsectorDepth; +}; + +class PolyPortalSegment +{ +public: + PolyPortalSegment(int x1, int x2) : X1(x1), X2(x2) { } + int X1, X2; +}; + +class PolyDrawSectorPortal +{ +public: + PolyDrawSectorPortal(FSectorPortal *portal, bool ceiling); + + void Render(int portalDepth); + void RenderTranslucent(int portalDepth); + + FSectorPortal *Portal = nullptr; + uint32_t StencilValue = 0; + std::vector Shape; + std::vector Segments; + Vec4f PortalPlane = Vec4f(0.0f); + +private: + void SaveGlobals(); + void RestoreGlobals(); + + bool Ceiling; + RenderPolyScene RenderPortal; + + int savedextralight; + DVector3 savedpos; + DRotator savedangles; + double savedvisibility; + AActor *savedcamera; + sector_t *savedsector; +}; + +class PolyDrawLinePortal +{ +public: + PolyDrawLinePortal(FLinePortal *portal); + PolyDrawLinePortal(line_t *mirror); + + void Render(int portalDepth); + void RenderTranslucent(int portalDepth); + + FLinePortal *Portal = nullptr; + line_t *Mirror = nullptr; + uint32_t StencilValue = 0; + std::vector Shape; + std::vector Segments; + +private: + void SaveGlobals(); + void RestoreGlobals(); + + RenderPolyScene RenderPortal; + + int savedextralight; + DVector3 savedpos; + DRotator savedangles; + AActor *savedcamera; + sector_t *savedsector; + bool savedinvisibility; + DVector3 savedViewPath[2]; +}; diff --git a/src/polyrenderer/scene/poly_scene.cpp b/src/polyrenderer/scene/poly_scene.cpp new file mode 100644 index 0000000000..50d1143d23 --- /dev/null +++ b/src/polyrenderer/scene/poly_scene.cpp @@ -0,0 +1,371 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "p_maputl.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "polyrenderer/scene/poly_scene.h" +#include "polyrenderer/poly_renderer.h" +#include "gl/data/gl_data.h" +#include "swrenderer/scene/r_light.h" + +CVAR(Bool, r_debug_cull, 0, 0) +EXTERN_CVAR(Int, r_portal_recursions) + +///////////////////////////////////////////////////////////////////////////// + +RenderPolyScene::RenderPolyScene() +{ +} + +RenderPolyScene::~RenderPolyScene() +{ +} + +void RenderPolyScene::SetViewpoint(const TriMatrix &worldToClip, const Vec4f &portalPlane, uint32_t stencilValue) +{ + WorldToClip = worldToClip; + StencilValue = stencilValue; + PortalPlane = portalPlane; +} + +void RenderPolyScene::SetPortalSegments(const std::vector &segments) +{ + Cull.ClearSolidSegments(); + for (const auto &segment : segments) + { + Cull.MarkSegmentCulled(segment.X1, segment.X2); + } + Cull.InvertSegments(); + PortalSegmentsAdded = true; +} + +void RenderPolyScene::Render(int portalDepth) +{ + ClearBuffers(); + if (!PortalSegmentsAdded) + Cull.ClearSolidSegments(); + Cull.CullScene(WorldToClip, PortalPlane); + Cull.ClearSolidSegments(); + RenderSectors(); + RenderPortals(portalDepth); +} + +void RenderPolyScene::ClearBuffers() +{ + SeenSectors.clear(); + SubsectorDepths.clear(); + TranslucentObjects.clear(); + SectorPortals.clear(); + LinePortals.clear(); + NextSubsectorDepth = 0; +} + +void RenderPolyScene::RenderSectors() +{ + if (r_debug_cull) + { + for (auto it = Cull.PvsSectors.rbegin(); it != Cull.PvsSectors.rend(); ++it) + RenderSubsector(*it); + } + else + { + for (auto it = Cull.PvsSectors.begin(); it != Cull.PvsSectors.end(); ++it) + RenderSubsector(*it); + } +} + +void RenderPolyScene::RenderSubsector(subsector_t *sub) +{ + sector_t *frontsector = sub->sector; + frontsector->MoreFlags |= SECF_DRAWN; + + uint32_t subsectorDepth = NextSubsectorDepth++; + + if (sub->sector->CenterFloor() != sub->sector->CenterCeiling()) + { + RenderPolyPlane::RenderPlanes(WorldToClip, PortalPlane, Cull, sub, subsectorDepth, StencilValue, Cull.MaxCeilingHeight, Cull.MinFloorHeight, SectorPortals); + } + + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + if (line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) + { + RenderLine(sub, line, frontsector, subsectorDepth); + } + } + + bool mainBSP = ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors); + if (mainBSP) + { + int subsectorIndex = (int)(sub - subsectors); + for (int i = ParticlesInSubsec[subsectorIndex]; i != NO_PARTICLE; i = Particles[i].snext) + { + particle_t *particle = Particles + i; + TranslucentObjects.push_back({ particle, sub, subsectorDepth }); + } + } + + SeenSectors.insert(sub->sector); + SubsectorDepths[sub] = subsectorDepth; +} + +void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right) +{ + if (numnodes == 0) + { + subsector_t *sub = subsectors; + auto it = SubsectorDepths.find(sub); + if (it != SubsectorDepths.end()) + TranslucentObjects.push_back({ thing, sub, it->second, sortDistance, 0.0f, 1.0f }); + } + else + { + RenderSprite(thing, sortDistance, left, right, 0.0, 1.0, nodes + numnodes - 1); // The head node is the last node output. + } +} + +void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node) +{ + while (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + + DVector2 planePos(FIXED2DBL(bsp->x), FIXED2DBL(bsp->y)); + DVector2 planeNormal = DVector2(FIXED2DBL(-bsp->dy), FIXED2DBL(bsp->dx)); + double planeD = planeNormal | planePos; + + int sideLeft = (left | planeNormal) > planeD; + int sideRight = (right | planeNormal) > planeD; + + if (sideLeft != sideRight) + { + double dotLeft = planeNormal | left; + double dotRight = planeNormal | right; + double t = (planeD - dotLeft) / (dotRight - dotLeft); + + DVector2 mid = left * (1.0 - t) + right * t; + double tmid = t1 * (1.0 - t) + t2 * t; + + RenderSprite(thing, sortDistance, mid, right, tmid, t2, bsp->children[sideRight]); + right = mid; + t2 = tmid; + } + node = bsp->children[sideLeft]; + } + + subsector_t *sub = (subsector_t *)((uint8_t *)node - 1); + + auto it = SubsectorDepths.find(sub); + if (it != SubsectorDepths.end()) + TranslucentObjects.push_back({ thing, sub, it->second, sortDistance, (float)t1, (float)t2 }); +} + +void RenderPolyScene::RenderLine(subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth) +{ + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + + // Reject lines not facing viewer + DVector2 pt1 = line->v1->fPos() - viewpoint.Pos; + DVector2 pt2 = line->v2->fPos() - viewpoint.Pos; + if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) + return; + + // Cull wall if not visible + int sx1, sx2; + LineSegmentRange segmentRange = Cull.GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2); + if (segmentRange == LineSegmentRange::NotVisible || (segmentRange == LineSegmentRange::HasSegment && Cull.IsSegmentCulled(sx1, sx2))) + return; + + // Tell automap we saw this + if (!PolyRenderer::Instance()->DontMapLines && line->linedef && segmentRange != LineSegmentRange::AlwaysVisible) + { + line->linedef->flags |= ML_MAPPED; + sub->flags |= SSECF_DRAWN; + } + + // Render 3D floor sides + if (line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) + { + for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++) + { + F3DFloor *fakeFloor = line->backsector->e->XFloor.ffloors[i]; + if (!(fakeFloor->flags & FF_EXISTS)) continue; + if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; + if (!fakeFloor->model) continue; + RenderPolyWall::Render3DFloorLine(WorldToClip, PortalPlane, Cull, line, frontsector, subsectorDepth, StencilValue, fakeFloor, TranslucentObjects); + } + } + + // Render wall, and update culling info if its an occlusion blocker + if (RenderPolyWall::RenderLine(WorldToClip, PortalPlane, Cull, line, frontsector, subsectorDepth, StencilValue, TranslucentObjects, LinePortals)) + { + if (segmentRange == LineSegmentRange::HasSegment) + Cull.MarkSegmentCulled(sx1, sx2); + } +} + +void RenderPolyScene::RenderPortals(int portalDepth) +{ + bool foggy = false; + if (portalDepth < r_portal_recursions) + { + for (auto &portal : SectorPortals) + portal->Render(portalDepth + 1); + + for (auto &portal : LinePortals) + portal->Render(portalDepth + 1); + } + else // Fill with black + { + PolyDrawArgs args; + args.objectToClip = &WorldToClip; + args.mode = TriangleDrawMode::Fan; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + args.uniforms.color = 0; + args.uniforms.light = 256; + args.uniforms.flags = TriUniforms::fixed_light; + args.SetClipPlane(PortalPlane.x, PortalPlane.y, PortalPlane.z, PortalPlane.w); + + for (auto &portal : SectorPortals) + { + args.stenciltestvalue = portal->StencilValue; + args.stencilwritevalue = portal->StencilValue + 1; + for (const auto &verts : portal->Shape) + { + args.vinput = verts.Vertices; + args.vcount = verts.Count; + args.ccw = verts.Ccw; + args.uniforms.subsectorDepth = verts.SubsectorDepth; + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); + } + } + + for (auto &portal : LinePortals) + { + args.stenciltestvalue = portal->StencilValue; + args.stencilwritevalue = portal->StencilValue + 1; + for (const auto &verts : portal->Shape) + { + args.vinput = verts.Vertices; + args.vcount = verts.Count; + args.ccw = verts.Ccw; + args.uniforms.subsectorDepth = verts.SubsectorDepth; + PolyTriangleDrawer::draw(args); + } + } + } +} + +void RenderPolyScene::RenderTranslucent(int portalDepth) +{ + if (portalDepth < r_portal_recursions) + { + for (auto it = SectorPortals.rbegin(); it != SectorPortals.rend(); ++it) + { + auto &portal = *it; + portal->RenderTranslucent(portalDepth + 1); + + PolyDrawArgs args; + args.objectToClip = &WorldToClip; + args.mode = TriangleDrawMode::Fan; + args.stenciltestvalue = portal->StencilValue + 1; + args.stencilwritevalue = StencilValue + 1; + args.SetClipPlane(PortalPlane.x, PortalPlane.y, PortalPlane.z, PortalPlane.w); + for (const auto &verts : portal->Shape) + { + args.vinput = verts.Vertices; + args.vcount = verts.Count; + args.ccw = verts.Ccw; + args.uniforms.subsectorDepth = verts.SubsectorDepth; + args.writeColor = false; + PolyTriangleDrawer::draw(args); + } + } + + for (auto it = LinePortals.rbegin(); it != LinePortals.rend(); ++it) + { + auto &portal = *it; + portal->RenderTranslucent(portalDepth + 1); + + PolyDrawArgs args; + args.objectToClip = &WorldToClip; + args.mode = TriangleDrawMode::Fan; + args.stenciltestvalue = portal->StencilValue + 1; + args.stencilwritevalue = StencilValue + 1; + args.SetClipPlane(PortalPlane.x, PortalPlane.y, PortalPlane.z, PortalPlane.w); + for (const auto &verts : portal->Shape) + { + args.vinput = verts.Vertices; + args.vcount = verts.Count; + args.ccw = verts.Ccw; + args.uniforms.subsectorDepth = verts.SubsectorDepth; + args.writeColor = false; + PolyTriangleDrawer::draw(args); + } + } + } + + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + for (sector_t *sector : SeenSectors) + { + for (AActor *thing = sector->thinglist; thing != nullptr; thing = thing->snext) + { + DVector2 left, right; + if (!RenderPolySprite::GetLine(thing, left, right)) + continue; + double distanceSquared = (thing->Pos() - viewpoint.Pos).LengthSquared(); + RenderSprite(thing, distanceSquared, left, right); + } + } + + std::stable_sort(TranslucentObjects.begin(), TranslucentObjects.end()); + + for (auto it = TranslucentObjects.rbegin(); it != TranslucentObjects.rend(); ++it) + { + auto &obj = *it; + if (obj.particle) + { + RenderPolyParticle spr; + spr.Render(WorldToClip, PortalPlane, obj.particle, obj.sub, obj.subsectorDepth, StencilValue + 1); + } + else if (!obj.thing) + { + obj.wall.Render(WorldToClip, PortalPlane, Cull); + } + else if ((obj.thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) + { + RenderPolyWallSprite wallspr; + wallspr.Render(WorldToClip, PortalPlane, obj.thing, obj.sub, obj.subsectorDepth, StencilValue + 1); + } + else + { + RenderPolySprite spr; + spr.Render(WorldToClip, PortalPlane, obj.thing, obj.sub, obj.subsectorDepth, StencilValue + 1, obj.SpriteLeft, obj.SpriteRight); + } + } +} diff --git a/src/polyrenderer/scene/poly_scene.h b/src/polyrenderer/scene/poly_scene.h new file mode 100644 index 0000000000..ba6fd5eea4 --- /dev/null +++ b/src/polyrenderer/scene/poly_scene.h @@ -0,0 +1,105 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include +#include +#include +#include +#include "doomdata.h" +#include "r_utility.h" +#include "polyrenderer/drawers/poly_triangle.h" +#include "polyrenderer/math/poly_intersection.h" +#include "poly_wall.h" +#include "poly_sprite.h" +#include "poly_wallsprite.h" +#include "poly_playersprite.h" +#include "poly_particle.h" +#include "poly_plane.h" +#include "poly_cull.h" +#include +#include + +class PolyTranslucentObject +{ +public: + PolyTranslucentObject(particle_t *particle, subsector_t *sub, uint32_t subsectorDepth) : particle(particle), sub(sub), subsectorDepth(subsectorDepth) { } + PolyTranslucentObject(AActor *thing, subsector_t *sub, uint32_t subsectorDepth, double dist, float t1, float t2) : thing(thing), sub(sub), subsectorDepth(subsectorDepth), DistanceSquared(dist), SpriteLeft(t1), SpriteRight(t2) { } + PolyTranslucentObject(RenderPolyWall wall) : wall(wall), subsectorDepth(wall.SubsectorDepth), DistanceSquared(1.e6) { } + + bool operator<(const PolyTranslucentObject &other) const + { + return subsectorDepth != other.subsectorDepth ? subsectorDepth < other.subsectorDepth : DistanceSquared < other.DistanceSquared; + } + + particle_t *particle = nullptr; + AActor *thing = nullptr; + subsector_t *sub = nullptr; + + RenderPolyWall wall; + + uint32_t subsectorDepth = 0; + double DistanceSquared = 0.0; + + float SpriteLeft = 0.0f, SpriteRight = 1.0f; +}; + +class PolyDrawSectorPortal; +class PolyDrawLinePortal; +class PolyPortalSegment; + +// Renders everything from a specific viewpoint +class RenderPolyScene +{ +public: + RenderPolyScene(); + ~RenderPolyScene(); + void SetViewpoint(const TriMatrix &worldToClip, const Vec4f &portalPlane, uint32_t stencilValue); + void SetPortalSegments(const std::vector &segments); + void Render(int portalDepth); + void RenderTranslucent(int portalDepth); + + static const uint32_t SkySubsectorDepth = 0x7fffffff; + +private: + void ClearBuffers(); + void RenderPortals(int portalDepth); + void RenderSectors(); + void RenderSubsector(subsector_t *sub); + void RenderLine(subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth); + void RenderSprite(AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right); + void RenderSprite(AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node); + + TriMatrix WorldToClip; + Vec4f PortalPlane; + uint32_t StencilValue = 0; + PolyCull Cull; + uint32_t NextSubsectorDepth = 0; + std::set SeenSectors; + std::unordered_map SubsectorDepths; + std::vector TranslucentObjects; + + std::vector> SectorPortals; + std::vector> LinePortals; + bool PortalSegmentsAdded = false; +}; diff --git a/src/polyrenderer/scene/poly_sky.cpp b/src/polyrenderer/scene/poly_sky.cpp new file mode 100644 index 0000000000..ac5647f737 --- /dev/null +++ b/src/polyrenderer/scene/poly_sky.cpp @@ -0,0 +1,188 @@ +/* +** Sky dome rendering +** Copyright(C) 2003-2016 Christoph Oelckers +** All rights reserved. +** +** 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 3 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, see http:**www.gnu.org/licenses/ +** +** Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky. +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_sky.h" +#include "poly_portal.h" +#include "r_sky.h" // for skyflatnum +#include "g_levellocals.h" +#include "swrenderer/scene/r_light.h" + +PolySkyDome::PolySkyDome() +{ + CreateDome(); +} + +void PolySkyDome::Render(const TriMatrix &worldToClip) +{ + FTextureID sky1tex, sky2tex; + bool foggy = false; + if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) + sky1tex = sky2texture; + else + sky1tex = sky1texture; + sky2tex = sky2texture; + + FTexture *frontskytex = TexMan(sky1tex, true); + FTexture *backskytex = nullptr; + if (level.flags & LEVEL_DOUBLESKY) + backskytex = TexMan(sky2tex, true); + + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + TriMatrix objectToWorld = TriMatrix::translate((float)viewpoint.Pos.X, (float)viewpoint.Pos.Y, (float)viewpoint.Pos.Z); + objectToClip = worldToClip * objectToWorld; + + int rc = mRows + 1; + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + args.uniforms.light = 256; + args.uniforms.flags = 0; + args.uniforms.subsectorDepth = RenderPolyScene::SkySubsectorDepth; + args.objectToClip = &objectToClip; + args.stenciltestvalue = 255; + args.stencilwritevalue = 1; + args.SetColormap(&NormalLight); + args.SetClipPlane(0.0f, 0.0f, 0.0f, 0.0f); + + RenderCapColorRow(args, frontskytex, 0, false); + RenderCapColorRow(args, frontskytex, rc, true); + + args.SetTexture(frontskytex); + + uint32_t topcapcolor = frontskytex->GetSkyCapColor(false); + uint32_t bottomcapcolor = frontskytex->GetSkyCapColor(true); + + for (int i = 1; i <= mRows; i++) + { + RenderRow(args, i, topcapcolor); + RenderRow(args, rc + i, bottomcapcolor); + } +} + +void PolySkyDome::RenderRow(PolyDrawArgs &args, int row, uint32_t capcolor) +{ + args.vinput = &mVertices[mPrimStart[row]]; + args.vcount = mPrimStart[row + 1] - mPrimStart[row]; + args.mode = TriangleDrawMode::Strip; + args.ccw = false; + args.uniforms.color = capcolor; + args.blendmode = TriBlendMode::Skycap; + PolyTriangleDrawer::draw(args); +} + +void PolySkyDome::RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap) +{ + uint32_t solid = skytex->GetSkyCapColor(bottomCap); + if (!PolyRenderer::Instance()->Thread.Viewport->RenderTarget->IsBgra()) + solid = RGB32k.RGB[(RPART(solid) >> 3)][(GPART(solid) >> 3)][(BPART(solid) >> 3)]; + + args.vinput = &mVertices[mPrimStart[row]]; + args.vcount = mPrimStart[row + 1] - mPrimStart[row]; + args.mode = TriangleDrawMode::Fan; + args.ccw = bottomCap; + args.uniforms.color = solid; + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); +} + +void PolySkyDome::CreateDome() +{ + mColumns = 16;// 128; + mRows = 4; + CreateSkyHemisphere(false); + CreateSkyHemisphere(true); + mPrimStart.Push(mVertices.Size()); +} + +void PolySkyDome::CreateSkyHemisphere(bool zflip) +{ + int r, c; + + mPrimStart.Push(mVertices.Size()); + + for (c = 0; c < mColumns; c++) + { + SkyVertex(1, c, zflip); + } + + // 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 + zflip, c, zflip); + SkyVertex(r + 1 - zflip, c, zflip); + } + } +} + +TriVertex PolySkyDome::SetVertexXYZ(float xx, float yy, float zz, float uu, float vv) +{ + TriVertex v; + v.x = xx; + v.y = zz; + v.z = yy; + v.w = 1.0f; + v.varying[0] = uu; + v.varying[1] = vv; + return v; +} + +void PolySkyDome::SkyVertex(int r, int c, bool zflip) +{ + static const FAngle maxSideAngle = 60.f; + static const float scale = 10000.; + + FAngle topAngle = (c / (float)mColumns * 360.f); + FAngle sideAngle = maxSideAngle * (float)(mRows - r) / (float)mRows; + float height = sideAngle.Sin(); + float realRadius = scale * sideAngle.Cos(); + FVector2 pos = topAngle.ToVector(realRadius); + float z = (!zflip) ? scale * height : -scale * height; + + float u, v; + + // And the texture coordinates. + if (!zflip) // Flipped Y is for the lower hemisphere. + { + u = (-c / (float)mColumns); + v = (r / (float)mRows); + } + else + { + u = (-c / (float)mColumns); + v = 1.0f + ((mRows - r) / (float)mRows); + } + + if (r != 4) z += 300; + + // And finally the vertex. + TriVertex vert; + vert = SetVertexXYZ(-pos.X, z - 1.f, pos.Y, u * 4.0f, v * 1.2f - 0.5f); + mVertices.Push(vert); +} diff --git a/src/polyrenderer/scene/poly_sky.h b/src/polyrenderer/scene/poly_sky.h new file mode 100644 index 0000000000..1a8cd8ef21 --- /dev/null +++ b/src/polyrenderer/scene/poly_sky.h @@ -0,0 +1,45 @@ +/* +** Sky dome rendering +** Copyright(C) 2003-2016 Christoph Oelckers +** All rights reserved. +** +** 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 3 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, see http:**www.gnu.org/licenses/ +** +** Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky. +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class PolySkyDome +{ +public: + PolySkyDome(); + void Render(const TriMatrix &worldToClip); + +private: + TArray mVertices; + TArray mPrimStart; + int mRows, mColumns; + TriMatrix objectToClip; + + void SkyVertex(int r, int c, bool yflip); + void CreateSkyHemisphere(bool zflip); + void CreateDome(); + void RenderRow(PolyDrawArgs &args, int row, uint32_t capcolor); + void RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap); + + TriVertex SetVertexXYZ(float xx, float yy, float zz, float uu = 0, float vv = 0); +}; diff --git a/src/polyrenderer/scene/poly_sprite.cpp b/src/polyrenderer/scene/poly_sprite.cpp new file mode 100644 index 0000000000..dbc4387c5c --- /dev/null +++ b/src/polyrenderer/scene/poly_sprite.cpp @@ -0,0 +1,442 @@ +/* +** Handling drawing a sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_sprite.h" +#include "polyrenderer/poly_renderer.h" +#include "polyrenderer/math/poly_intersection.h" +#include "swrenderer/scene/r_light.h" + +EXTERN_CVAR(Float, transsouls) +EXTERN_CVAR(Int, r_drawfuzz) + +bool RenderPolySprite::GetLine(AActor *thing, DVector2 &left, DVector2 &right) +{ + if (IsThingCulled(thing)) + return false; + + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac); + + bool flipTextureX = false; + FTexture *tex = GetSpriteTexture(thing, flipTextureX); + if (tex == nullptr) + return false; + + DVector2 spriteScale = thing->Scale; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + + if (flipTextureX) + pos.X -= (tex->GetWidth() - tex->LeftOffset) * thingxscalemul; + else + pos.X -= tex->LeftOffset * thingxscalemul; + + double spriteHalfWidth = thingxscalemul * tex->GetWidth() * 0.5; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + pos.X += spriteHalfWidth; + + left = DVector2(pos.X - viewpoint.Sin * spriteHalfWidth, pos.Y + viewpoint.Cos * spriteHalfWidth); + right = DVector2(pos.X + viewpoint.Sin * spriteHalfWidth, pos.Y - viewpoint.Cos * spriteHalfWidth); + return true; +} + +void RenderPolySprite::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, float t1, float t2) +{ + DVector2 line[2]; + if (!GetLine(thing, line[0], line[1])) + return; + + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac); + pos.Z += thing->GetBobOffset(viewpoint.TicFrac); + + bool flipTextureX = false; + FTexture *tex = GetSpriteTexture(thing, flipTextureX); + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + return; + + DVector2 spriteScale = thing->Scale; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + + if (flipTextureX) + pos.X -= (tex->GetWidth() - tex->LeftOffset) * thingxscalemul; + else + pos.X -= tex->LeftOffset * thingxscalemul; + + //pos.Z -= tex->TopOffset * thingyscalemul; + pos.Z -= (tex->GetHeight() - tex->TopOffset) * thingyscalemul + thing->Floorclip; + + double spriteHalfWidth = thingxscalemul * tex->GetWidth() * 0.5; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + pos.X += spriteHalfWidth; + + //double depth = 1.0; + //visstyle_t visstyle = GetSpriteVisStyle(thing, depth); + // Rumor has it that AlterWeaponSprite needs to be called with visstyle passed in somewhere around here.. + //R_SetColorMapLight(visstyle.BaseColormap, 0, visstyle.ColormapNum << FRACBITS); + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + bool foggy = false; + int actualextralight = foggy ? 0 : viewpoint.extralight << 4; + + std::pair offsets[4] = + { + { t1, 1.0f }, + { t2, 1.0f }, + { t2, 0.0f }, + { t1, 0.0f }, + }; + + DVector2 points[2] = + { + line[0] * (1.0 - t1) + line[1] * t1, + line[0] * (1.0 - t2) + line[1] * t2 + }; + + for (int i = 0; i < 4; i++) + { + auto &p = (i == 0 || i == 3) ? points[0] : points[1]; + + vertices[i].x = (float)p.X; + vertices[i].y = (float)p.Y; + vertices[i].z = (float)(pos.Z + spriteHeight * offsets[i].second); + vertices[i].w = 1.0f; + vertices[i].varying[0] = (float)(offsets[i].first * tex->Scale.X); + vertices[i].varying[1] = (float)((1.0f - offsets[i].second) * tex->Scale.Y); + if (flipTextureX) + vertices[i].varying[0] = 1.0f - vertices[i].varying[0]; + } + + bool fullbrightSprite = ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT)); + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->SpriteGlobVis(foggy); + args.uniforms.flags = 0; + if (fullbrightSprite || cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + args.uniforms.light = 256; + args.uniforms.flags |= TriUniforms::fixed_light; + } + else + { + args.uniforms.light = (uint32_t)((thing->Sector->lightlevel + actualextralight) / 255.0f * 256.0f); + } + args.uniforms.subsectorDepth = subsectorDepth; + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue; + args.SetTexture(tex, thing->Translation); + args.SetColormap(sub->sector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + + TriBlendMode blendmode; + + if (thing->RenderStyle == LegacyRenderStyles[STYLE_Normal] || + (r_drawfuzz == 0 && thing->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy])) + { + args.uniforms.destalpha = 0; + args.uniforms.srcalpha = 256; + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Add] && fullbrightSprite && thing->Alpha == 1.0 && args.translation == nullptr) + { + args.uniforms.destalpha = 256; + args.uniforms.srcalpha = 256; + blendmode = TriBlendMode::AddSrcColorOneMinusSrcColor; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Add]) + { + args.uniforms.destalpha = (uint32_t)(1.0 * 256); + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Subtract]) + { + args.uniforms.destalpha = (uint32_t)(1.0 * 256); + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + blendmode = args.translation ? TriBlendMode::TranslateRevSub : TriBlendMode::RevSub; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_SoulTrans]) + { + args.uniforms.destalpha = (uint32_t)(256 - transsouls * 256); + args.uniforms.srcalpha = (uint32_t)(transsouls * 256); + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Fuzzy] || + (r_drawfuzz == 2 && thing->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy])) + { // NYI - Fuzzy - for now, just a copy of "Shadow" + args.uniforms.destalpha = 160; + args.uniforms.srcalpha = 0; + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Shadow] || + (r_drawfuzz == 1 && thing->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy])) + { + args.uniforms.destalpha = 160; + args.uniforms.srcalpha = 0; + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_TranslucentStencil]) + { + args.uniforms.destalpha = (uint32_t)(256 - thing->Alpha * 256); + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + args.uniforms.color = 0xff000000 | thing->fillcolor; + blendmode = TriBlendMode::Stencil; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_AddStencil]) + { + args.uniforms.destalpha = 256; + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + args.uniforms.color = 0xff000000 | thing->fillcolor; + blendmode = TriBlendMode::Stencil; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Shaded]) + { + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + args.uniforms.destalpha = 256 - args.uniforms.srcalpha; + args.uniforms.color = 0; + blendmode = TriBlendMode::Shaded; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_AddShaded]) + { + args.uniforms.destalpha = 256; + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + args.uniforms.color = 0; + blendmode = TriBlendMode::Shaded; + } + else + { + args.uniforms.destalpha = (uint32_t)(256 - thing->Alpha * 256); + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + + if (blendmode == TriBlendMode::Shaded) + { + args.SetTexture(tex, thing->Translation, true); + } + + if (!PolyRenderer::Instance()->Thread.Viewport->RenderTarget->IsBgra()) + { + uint32_t r = (args.uniforms.color >> 16) & 0xff; + uint32_t g = (args.uniforms.color >> 8) & 0xff; + uint32_t b = args.uniforms.color & 0xff; + args.uniforms.color = RGB32k.RGB[r >> 3][g >> 3][b >> 3]; + + if (blendmode == TriBlendMode::Sub) // Sub crashes in pal mode for some weird reason. + blendmode = TriBlendMode::Add; + } + + args.subsectorTest = true; + args.writeSubsector = false; + args.writeStencil = false; + args.blendmode = blendmode; + PolyTriangleDrawer::draw(args); +} + +bool RenderPolySprite::IsThingCulled(AActor *thing) +{ + FIntCVar *cvar = thing->GetClass()->distancecheck; + if (cvar != nullptr && *cvar >= 0) + { + double dist = (thing->Pos() - PolyRenderer::Instance()->Thread.Viewport->viewpoint.Pos).LengthSquared(); + double check = (double)**cvar; + if (dist >= check * check) + return true; + } + + // Don't waste time projecting sprites that are definitely not visible. + if (thing == nullptr || + (thing->renderflags & RF_INVISIBLE) || + !thing->RenderStyle.IsVisible(thing->Alpha) || + !thing->IsVisibleToPlayer()) + { + return true; + } + + return false; +} + +#if 0 +visstyle_t RenderPolySprite::GetSpriteVisStyle(AActor *thing, double z) +{ + visstyle_t visstyle; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + int spriteshade = LIGHT2SHADE(thing->Sector->lightlevel + actualextralight); + + FRenderStyle RenderStyle; + RenderStyle = thing->RenderStyle; + float Alpha = float(thing->Alpha); + int ColormapNum = 0; + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + bool invertcolormap = (RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + + if (RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + FDynamicColormap *mybasecolormap = thing->Sector->ColorMap; + + // Sprites that are added to the scene must fade to black. + if (RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); + } + + if (RenderStyle.Flags & STYLEF_FadeToBlack) + { + if (invertcolormap) + { // Fade to white + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255, 255, 255), mybasecolormap->Desaturate); + invertcolormap = false; + } + else + { // Fade to black + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0, 0, 0), mybasecolormap->Desaturate); + } + } + + // get light level + if (swrenderer::fixedcolormap != nullptr) + { // fixed map + BaseColormap = swrenderer::fixedcolormap; + ColormapNum = 0; + } + else + { + if (invertcolormap) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); + } + if (swrenderer::fixedlightlev >= 0) + { + BaseColormap = mybasecolormap; + ColormapNum = swrenderer::fixedlightlev >> COLORMAPSHIFT; + } + else if (!foggy && ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT))) + { // full bright + BaseColormap = mybasecolormap; + ColormapNum = 0; + } + else + { // diminished light + double minz = double((2048 * 4) / double(1 << 20)); + ColormapNum = GETPALOOKUP(swrenderer::r_SpriteVisibility / MAX(z, minz), spriteshade); + BaseColormap = mybasecolormap; + } + } + + return visstyle; +} +#endif + +FTexture *RenderPolySprite::GetSpriteTexture(AActor *thing, /*out*/ bool &flipX) +{ + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + flipX = false; + if (thing->picnum.isValid()) + { + FTexture *tex = TexMan(thing->picnum); + if (tex->UseType == FTexture::TEX_Null) + { + return nullptr; + } + + if (tex->Rotations != 0xFFFF) + { + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[tex->Rotations]; + DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac); + pos.Z += thing->GetBobOffset(viewpoint.TicFrac); + DAngle ang = (pos - viewpoint.Pos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + flipX = (sprframe->Flip & (1 << rot)) != 0; + tex = TexMan[sprframe->Texture[rot]]; // Do not animate the rotation + } + return tex; + } + else + { + // decide which texture to use for the sprite + int spritenum = thing->sprite; + if (spritenum >= (signed)sprites.Size() || spritenum < 0) + return nullptr; + + spritedef_t *sprdef = &sprites[spritenum]; + if (thing->frame >= sprdef->numframes) + { + // If there are no frames at all for this sprite, don't draw it. + return nullptr; + } + else + { + //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; + DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac); + pos.Z += thing->GetBobOffset(viewpoint.TicFrac); + DAngle ang = (pos - viewpoint.Pos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + flipX = (sprframe->Flip & (1 << rot)) != 0; + return TexMan[sprframe->Texture[rot]]; // Do not animate the rotation + } + } +} diff --git a/src/polyrenderer/scene/poly_sprite.h b/src/polyrenderer/scene/poly_sprite.h new file mode 100644 index 0000000000..a61c7f4b5f --- /dev/null +++ b/src/polyrenderer/scene/poly_sprite.h @@ -0,0 +1,40 @@ +/* +** Handling drawing a sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class Vec4f; + +class RenderPolySprite +{ +public: + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, float t1, float t2); + + static bool GetLine(AActor *thing, DVector2 &left, DVector2 &right); + static bool IsThingCulled(AActor *thing); + static FTexture *GetSpriteTexture(AActor *thing, /*out*/ bool &flipX); + +private: + //visstyle_t GetSpriteVisStyle(AActor *thing, double z); +}; diff --git a/src/polyrenderer/scene/poly_wall.cpp b/src/polyrenderer/scene/poly_wall.cpp new file mode 100644 index 0000000000..6defccc8d7 --- /dev/null +++ b/src/polyrenderer/scene/poly_wall.cpp @@ -0,0 +1,483 @@ +/* +** Handling drawing a wall +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_wall.h" +#include "poly_decal.h" +#include "polyrenderer/poly_renderer.h" +#include "r_sky.h" +#include "swrenderer/scene/r_light.h" +#include "g_levellocals.h" + +EXTERN_CVAR(Bool, r_drawmirrors) + +bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals) +{ + PolyDrawLinePortal *polyportal = nullptr; + if (line->backsector == nullptr && line->linedef && line->sidedef == line->linedef->sidedef[0] && (line->linedef->special == Line_Mirror && r_drawmirrors)) + { + if (PolyRenderer::Instance()->InsertSeenMirror(line->linedef)) + { + linePortals.push_back(std::make_unique(line->linedef)); + polyportal = linePortals.back().get(); + } + } + else if (line->linedef && line->linedef->isVisualPortal()) + { + FLinePortal *portal = line->linedef->getPortal(); + if (PolyRenderer::Instance()->InsertSeenLinePortal(portal)) + { + for (auto &p : linePortals) + { + if (p->Portal == portal) // To do: what other criterias do we need to check for? + { + polyportal = p.get(); + break; + } + } + if (!polyportal) + { + linePortals.push_back(std::make_unique(portal)); + polyportal = linePortals.back().get(); + } + } + } + + RenderPolyWall wall; + wall.LineSeg = line; + wall.Line = line->linedef; + wall.Side = line->sidedef; + wall.Colormap = frontsector->ColorMap; + wall.Masked = false; + wall.SubsectorDepth = subsectorDepth; + wall.StencilValue = stencilValue; + + double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1); + double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1); + double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2); + double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2); + + if (line->backsector == nullptr) + { + if (line->sidedef) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2); + wall.TopZ = frontceilz1; + wall.BottomZ = frontfloorz1; + wall.UnpeggedCeil = frontceilz1; + wall.Texpart = side_t::mid; + wall.Polyportal = polyportal; + wall.Render(worldToClip, clipPlane, cull); + return true; + } + } + else + { + sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector; + + double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1); + double backfloorz1 = backsector->floorplane.ZatPoint(line->v1); + double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2); + double backfloorz2 = backsector->floorplane.ZatPoint(line->v2); + + double topceilz1 = frontceilz1; + double topceilz2 = frontceilz2; + double topfloorz1 = MIN(backceilz1, frontceilz1); + double topfloorz2 = MIN(backceilz2, frontceilz2); + double bottomceilz1 = MAX(frontfloorz1, backfloorz1); + double bottomceilz2 = MAX(frontfloorz2, backfloorz2); + double bottomfloorz1 = frontfloorz1; + double bottomfloorz2 = frontfloorz2; + double middleceilz1 = topfloorz1; + double middleceilz2 = topfloorz2; + double middlefloorz1 = MIN(bottomceilz1, middleceilz1); + double middlefloorz2 = MIN(bottomceilz2, middleceilz2); + + bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum; + + if ((topceilz1 > topfloorz1 || topceilz2 > topfloorz2) && line->sidedef && !bothSkyCeiling) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), topceilz1, topfloorz1, topceilz2, topfloorz2); + wall.TopZ = topceilz1; + wall.BottomZ = topfloorz1; + wall.UnpeggedCeil = topceilz1; + wall.Texpart = side_t::top; + wall.Render(worldToClip, clipPlane, cull); + } + + if ((bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && line->sidedef) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), bottomceilz1, bottomfloorz1, bottomceilz2, bottomfloorz2); + wall.TopZ = bottomceilz1; + wall.BottomZ = bottomfloorz2; + wall.UnpeggedCeil = topceilz1; + wall.Texpart = side_t::bottom; + wall.Render(worldToClip, clipPlane, cull); + } + + if (line->sidedef) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), middleceilz1, middlefloorz1, middleceilz2, middlefloorz2); + wall.TopZ = middleceilz1; + wall.BottomZ = middlefloorz1; + wall.UnpeggedCeil = topceilz1; + wall.Texpart = side_t::mid; + wall.Masked = true; + + FTexture *midtex = TexMan(line->sidedef->GetTexture(side_t::mid), true); + if (midtex && midtex->UseType != FTexture::TEX_Null) + translucentWallsOutput.push_back({ wall }); + + if (polyportal) + { + wall.Polyportal = polyportal; + wall.Render(worldToClip, clipPlane, cull); + } + } + } + return polyportal != nullptr; +} + +void RenderPolyWall::Render3DFloorLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector &translucentWallsOutput) +{ + double frontceilz1 = fakeFloor->top.plane->ZatPoint(line->v1); + double frontfloorz1 = fakeFloor->bottom.plane->ZatPoint(line->v1); + double frontceilz2 = fakeFloor->top.plane->ZatPoint(line->v2); + double frontfloorz2 = fakeFloor->bottom.plane->ZatPoint(line->v2); + + RenderPolyWall wall; + wall.LineSeg = line; + wall.Line = fakeFloor->master; + wall.Side = fakeFloor->master->sidedef[0]; + wall.Colormap = frontsector->ColorMap; + wall.Masked = false; + wall.SubsectorDepth = subsectorDepth; + wall.StencilValue = stencilValue; + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2); + wall.TopZ = frontceilz1; + wall.BottomZ = frontfloorz1; + wall.UnpeggedCeil = frontceilz1; + wall.Texpart = side_t::mid; + wall.Render(worldToClip, clipPlane, cull); +} + +void RenderPolyWall::SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2) +{ + this->v1 = v1; + this->v2 = v2; + this->ceil1 = ceil1; + this->floor1 = floor1; + this->ceil2 = ceil2; + this->floor2 = floor2; +} + +void RenderPolyWall::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull) +{ + bool foggy = false; + FTexture *tex = GetTexture(); + if (!tex && !Polyportal) + return; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + vertices[0].x = (float)v1.X; + vertices[0].y = (float)v1.Y; + vertices[0].z = (float)ceil1; + vertices[0].w = 1.0f; + + vertices[1].x = (float)v2.X; + vertices[1].y = (float)v2.Y; + vertices[1].z = (float)ceil2; + vertices[1].w = 1.0f; + + vertices[2].x = (float)v2.X; + vertices[2].y = (float)v2.Y; + vertices[2].z = (float)floor2; + vertices[2].w = 1.0f; + + vertices[3].x = (float)v1.X; + vertices[3].y = (float)v1.Y; + vertices[3].z = (float)floor1; + vertices[3].w = 1.0f; + + if (tex) + { + PolyWallTextureCoords texcoords(tex, LineSeg, Line, Side, Texpart, TopZ, BottomZ, UnpeggedCeil); + vertices[0].varying[0] = (float)texcoords.u1; + vertices[0].varying[1] = (float)texcoords.v1; + vertices[1].varying[0] = (float)texcoords.u2; + vertices[1].varying[1] = (float)texcoords.v1; + vertices[2].varying[0] = (float)texcoords.u2; + vertices[2].varying[1] = (float)texcoords.v2; + vertices[3].varying[0] = (float)texcoords.u1; + vertices[3].varying[1] = (float)texcoords.v2; + } + + // Masked walls clamp to the 0-1 range (no texture repeat) + if (Masked) + { + ClampHeight(vertices[0], vertices[3]); + ClampHeight(vertices[1], vertices[2]); + } + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + args.uniforms.light = (uint32_t)(GetLightLevel() / 255.0f * 256.0f); + args.uniforms.flags = 0; + args.uniforms.subsectorDepth = SubsectorDepth; + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = StencilValue; + args.stencilwritevalue = StencilValue + 1; + if (tex) + args.SetTexture(tex); + args.SetColormap(Line->frontsector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + + //if (Side && Side->lighthead) + // args.uniforms.light = 255; // Make walls touched by a light fullbright! + + if (Polyportal) + { + args.stencilwritevalue = Polyportal->StencilValue; + args.writeColor = false; + args.writeSubsector = false; + PolyTriangleDrawer::draw(args); + Polyportal->Shape.push_back({ args.vinput, args.vcount, args.ccw, args.uniforms.subsectorDepth }); + + int sx1, sx2; + LineSegmentRange range = cull.GetSegmentRangeForLine(v1.X, v1.Y, v2.X, v2.Y, sx1, sx2); + if (range == LineSegmentRange::HasSegment) + Polyportal->Segments.push_back({ sx1, sx2 }); + } + else if (!Masked) + { + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); + } + else + { + args.uniforms.destalpha = (Line->flags & ML_ADDTRANS) ? 256 : (uint32_t)(256 - Line->alpha * 256); + args.uniforms.srcalpha = (uint32_t)(Line->alpha * 256); + args.subsectorTest = true; + args.writeSubsector = false; + args.writeStencil = false; + if (args.uniforms.destalpha == 0 && args.uniforms.srcalpha == 256) + args.blendmode = TriBlendMode::AlphaBlend; + else + args.blendmode = TriBlendMode::Add; + PolyTriangleDrawer::draw(args); + } + + RenderPolyDecal::RenderWallDecals(worldToClip, clipPlane, LineSeg, SubsectorDepth, StencilValue); +} + +void RenderPolyWall::ClampHeight(TriVertex &v1, TriVertex &v2) +{ + float top = v1.z; + float bottom = v2.z; + float texv1 = v1.varying[1]; + float texv2 = v2.varying[1]; + float delta = (texv2 - texv1); + + float t1 = texv1 < 0.0f ? -texv1 / delta : 0.0f; + float t2 = texv2 > 1.0f ? (1.0f - texv1) / delta : 1.0f; + float inv_t1 = 1.0f - t1; + float inv_t2 = 1.0f - t2; + + v1.z = top * inv_t1 + bottom * t1; + v1.varying[1] = texv1 * inv_t1 + texv2 * t1; + + v2.z = top * inv_t2 + bottom * t2; + v2.varying[1] = texv1 * inv_t2 + texv2 * t2; +} + +FTexture *RenderPolyWall::GetTexture() +{ + FTexture *tex = TexMan(Side->GetTexture(Texpart), true); + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + { + // Mapping error. Doom floodfills this with a plane. + // This code doesn't do that, but at least it uses the "right" texture.. + + if (Line && Line->backsector && Line->sidedef[0] == Side) + { + if (Texpart == side_t::top) + tex = TexMan(Line->backsector->GetTexture(sector_t::ceiling), true); + else if (Texpart == side_t::bottom) + tex = TexMan(Line->backsector->GetTexture(sector_t::floor), true); + } + if (Line && Line->backsector && Line->sidedef[1] == Side) + { + if (Texpart == side_t::top) + tex = TexMan(Line->frontsector->GetTexture(sector_t::ceiling), true); + else if (Texpart == side_t::bottom) + tex = TexMan(Line->frontsector->GetTexture(sector_t::floor), true); + } + + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + return nullptr; + } + return tex; +} + +int RenderPolyWall::GetLightLevel() +{ + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + return 255; + } + else + { + bool foggy = false; + int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Thread.Viewport->viewpoint.extralight << 4; + return clamp(Side->GetLightLevel(foggy, LineSeg->frontsector->lightlevel) + actualextralight, 0, 255); + } +} + +///////////////////////////////////////////////////////////////////////////// + +PolyWallTextureCoords::PolyWallTextureCoords(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil) +{ + CalcU(tex, lineseg, line, side, texpart); + CalcV(tex, line, side, texpart, topz, bottomz, unpeggedceil); +} + +void PolyWallTextureCoords::CalcU(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart) +{ + double lineLength = side->TexelLength; + double lineStart = 0.0; + + bool entireSegment = ((lineseg->v1 == line->v1) && (lineseg->v2 == line->v2)) || ((lineseg->v2 == line->v1) && (lineseg->v1 == line->v2)); + if (!entireSegment) + { + lineLength = (lineseg->v2->fPos() - lineseg->v1->fPos()).Length(); + lineStart = (lineseg->v1->fPos() - line->v1->fPos()).Length(); + } + + int texWidth = tex->GetWidth(); + double uscale = side->GetTextureXScale(texpart) * tex->Scale.X; + u1 = lineStart + side->GetTextureXOffset(texpart); + u2 = u1 + lineLength; + u1 *= uscale; + u2 *= uscale; + u1 /= texWidth; + u2 /= texWidth; +} + +void PolyWallTextureCoords::CalcV(FTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil) +{ + double vscale = side->GetTextureYScale(texpart) * tex->Scale.Y; + + double yoffset = side->GetTextureYOffset(texpart); + if (tex->bWorldPanning) + yoffset *= vscale; + + switch (texpart) + { + default: + case side_t::mid: + CalcVMidPart(tex, line, side, topz, bottomz, vscale, yoffset); + break; + case side_t::top: + CalcVTopPart(tex, line, side, topz, bottomz, vscale, yoffset); + break; + case side_t::bottom: + CalcVBottomPart(tex, line, side, topz, bottomz, unpeggedceil, vscale, yoffset); + break; + } + + int texHeight = tex->GetHeight(); + v1 /= texHeight; + v2 /= texHeight; +} + +void PolyWallTextureCoords::CalcVTopPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset) +{ + bool pegged = (line->flags & ML_DONTPEGTOP) == 0; + if (pegged) // bottom to top + { + int texHeight = tex->GetHeight(); + v1 = -yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + v1 = texHeight - v1; + v2 = texHeight - v2; + std::swap(v1, v2); + } + else // top to bottom + { + v1 = yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } +} + +void PolyWallTextureCoords::CalcVMidPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset) +{ + bool pegged = (line->flags & ML_DONTPEGBOTTOM) == 0; + if (pegged) // top to bottom + { + v1 = yoffset * vscale; + v2 = (yoffset + (topz - bottomz)) * vscale; + } + else // bottom to top + { + int texHeight = tex->GetHeight(); + v1 = texHeight - (-yoffset + (topz - bottomz)) * vscale; + v2 = texHeight + yoffset * vscale; + } +} + +void PolyWallTextureCoords::CalcVBottomPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset) +{ + bool pegged = (line->flags & ML_DONTPEGBOTTOM) == 0; + if (pegged) // top to bottom + { + v1 = yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } + else + { + v1 = yoffset + (unpeggedceil - topz); + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } +} diff --git a/src/polyrenderer/scene/poly_wall.h b/src/polyrenderer/scene/poly_wall.h new file mode 100644 index 0000000000..014110a400 --- /dev/null +++ b/src/polyrenderer/scene/poly_wall.h @@ -0,0 +1,82 @@ +/* +** Handling drawing a wall +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class PolyTranslucentObject; +class PolyDrawLinePortal; +class PolyCull; +class Vec4f; + +class RenderPolyWall +{ +public: + static bool RenderLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals); + static void Render3DFloorLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector &translucentWallsOutput); + + void SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2); + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull); + + DVector2 v1; + DVector2 v2; + double ceil1 = 0.0; + double floor1 = 0.0; + double ceil2 = 0.0; + double floor2 = 0.0; + + const seg_t *LineSeg = nullptr; + const line_t *Line = nullptr; + const side_t *Side = nullptr; + side_t::ETexpart Texpart = side_t::mid; + double TopZ = 0.0; + double BottomZ = 0.0; + double UnpeggedCeil = 0.0; + FSWColormap *Colormap = nullptr; + bool Masked = false; + uint32_t SubsectorDepth = 0; + uint32_t StencilValue = 0; + PolyDrawLinePortal *Polyportal = nullptr; + +private: + void ClampHeight(TriVertex &v1, TriVertex &v2); + FTexture *GetTexture(); + int GetLightLevel(); +}; + +// Texture coordinates for a wall +class PolyWallTextureCoords +{ +public: + PolyWallTextureCoords(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); + + double u1, u2; + double v1, v2; + +private: + void CalcU(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart); + void CalcV(FTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); + void CalcVTopPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset); + void CalcVMidPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset); + void CalcVBottomPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset); +}; diff --git a/src/polyrenderer/scene/poly_wallsprite.cpp b/src/polyrenderer/scene/poly_wallsprite.cpp new file mode 100644 index 0000000000..02ed751f1f --- /dev/null +++ b/src/polyrenderer/scene/poly_wallsprite.cpp @@ -0,0 +1,133 @@ +/* +** Handling drawing a sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_wallsprite.h" +#include "polyrenderer/poly_renderer.h" +#include "swrenderer/scene/r_light.h" + +void RenderPolyWallSprite::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue) +{ + if (RenderPolySprite::IsThingCulled(thing)) + return; + + const auto &viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac); + pos.Z += thing->GetBobOffset(viewpoint.TicFrac); + + bool flipTextureX = false; + FTexture *tex = RenderPolySprite::GetSpriteTexture(thing, flipTextureX); + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + return; + + DVector2 spriteScale = thing->Scale; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + DAngle ang = thing->Angles.Yaw + 90; + double angcos = ang.Cos(); + double angsin = ang.Sin(); + + // Determine left and right edges of sprite. The sprite's angle is its normal, + // so the edges are 90 degrees each side of it. + double x2 = tex->GetScaledWidth() * spriteScale.X; + double x1 = tex->GetScaledLeftOffset() * spriteScale.X; + DVector2 left, right; + left.X = pos.X - x1 * angcos; + left.Y = pos.Y - x1 * angsin; + right.X = left.X + x2 * angcos; + right.Y = left.Y + x2 * angsin; + + //int scaled_to = tex->GetScaledTopOffset(); + //int scaled_bo = scaled_to - tex->GetScaledHeight(); + //gzt = pos.Z + scale.Y * scaled_to; + //gzb = pos.Z + scale.Y * scaled_bo; + + DVector2 points[2] = { left, right }; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + bool foggy = false; + int actualextralight = foggy ? 0 : viewpoint.extralight << 4; + + std::pair offsets[4] = + { + { 0.0f, 1.0f }, + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + { 0.0f, 0.0f }, + }; + + for (int i = 0; i < 4; i++) + { + auto &p = (i == 0 || i == 3) ? points[0] : points[1]; + + vertices[i].x = (float)p.X; + vertices[i].y = (float)p.Y; + vertices[i].z = (float)(pos.Z + spriteHeight * offsets[i].second); + vertices[i].w = 1.0f; + vertices[i].varying[0] = (float)(offsets[i].first * tex->Scale.X); + vertices[i].varying[1] = (float)((1.0f - offsets[i].second) * tex->Scale.Y); + if (flipTextureX) + vertices[i].varying[0] = 1.0f - vertices[i].varying[0]; + } + + bool fullbrightSprite = ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT)); + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + if (fullbrightSprite || cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + args.uniforms.light = 256; + args.uniforms.flags = TriUniforms::fixed_light; + } + else + { + args.uniforms.light = (uint32_t)((thing->Sector->lightlevel + actualextralight) / 255.0f * 256.0f); + args.uniforms.flags = 0; + } + args.uniforms.subsectorDepth = subsectorDepth; + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue; + args.SetTexture(tex); + args.SetColormap(sub->sector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + args.subsectorTest = true; + args.writeSubsector = false; + args.writeStencil = false; + args.blendmode = TriBlendMode::AlphaBlend; + PolyTriangleDrawer::draw(args); +} diff --git a/src/polyrenderer/scene/poly_wallsprite.h b/src/polyrenderer/scene/poly_wallsprite.h new file mode 100644 index 0000000000..75a550748e --- /dev/null +++ b/src/polyrenderer/scene/poly_wallsprite.h @@ -0,0 +1,33 @@ +/* +** Handling drawing a wall sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class Vec4f; + +class RenderPolyWallSprite +{ +public: + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue); +}; diff --git a/src/posix/cocoa/critsec.cpp b/src/posix/cocoa/critsec.cpp index cbf1124913..b9de5e7985 100644 --- a/src/posix/cocoa/critsec.cpp +++ b/src/posix/cocoa/critsec.cpp @@ -33,9 +33,25 @@ #include "critsec.h" +#include + +class FInternalCriticalSection +{ +public: + FInternalCriticalSection(); + ~FInternalCriticalSection(); + + void Enter(); + void Leave(); + +private: + pthread_mutex_t m_mutex; + +}; + // TODO: add error handling -FCriticalSection::FCriticalSection() +FInternalCriticalSection::FInternalCriticalSection() { pthread_mutexattr_t attributes; pthread_mutexattr_init(&attributes); @@ -46,17 +62,38 @@ FCriticalSection::FCriticalSection() pthread_mutexattr_destroy(&attributes); } -FCriticalSection::~FCriticalSection() +FInternalCriticalSection::~FInternalCriticalSection() { pthread_mutex_destroy(&m_mutex); } -void FCriticalSection::Enter() +void FInternalCriticalSection::Enter() { pthread_mutex_lock(&m_mutex); } -void FCriticalSection::Leave() +void FInternalCriticalSection::Leave() { pthread_mutex_unlock(&m_mutex); } + + +FInternalCriticalSection *CreateCriticalSection() +{ + return new FInternalCriticalSection(); +} + +void DeleteCriticalSection(FInternalCriticalSection *c) +{ + delete c; +} + +void EnterCriticalSection(FInternalCriticalSection *c) +{ + c->Enter(); +} + +void LeaveCriticalSection(FInternalCriticalSection *c) +{ + c->Leave(); +} diff --git a/src/posix/cocoa/critsec.h b/src/posix/cocoa/critsec.h deleted file mode 100644 index 7940dfe328..0000000000 --- a/src/posix/cocoa/critsec.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - ** critsec.h - ** - **--------------------------------------------------------------------------- - ** Copyright 2014 Alexey Lysiuk - ** 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 CRITSEC_H -#define CRITSEC_H - -#include - -class FCriticalSection -{ -public: - FCriticalSection(); - ~FCriticalSection(); - - void Enter(); - void Leave(); - -private: - pthread_mutex_t m_mutex; - -}; - -#endif diff --git a/src/posix/cocoa/i_system.mm b/src/posix/cocoa/i_system.mm index 35b71c64b8..a9cb708096 100644 --- a/src/posix/cocoa/i_system.mm +++ b/src/posix/cocoa/i_system.mm @@ -38,7 +38,7 @@ #include #include -#include "d_ticcmd.h" +#include "d_protocol.h" #include "doomdef.h" #include "doomerrors.h" #include "doomstat.h" @@ -53,7 +53,7 @@ EXTERN_CVAR(String, language) -DWORD LanguageIDs[4]; +uint32_t LanguageIDs[4]; int (*I_GetTime)(bool saveMS); @@ -88,7 +88,7 @@ void SetLanguageIDs() { size_t langlen = strlen(language); - DWORD lang = (langlen < 2 || langlen > 3) + uint32_t lang = (langlen < 2 || langlen > 3) ? MAKE_ID('e', 'n', 'u', '\0') : MAKE_ID(language[0], language[1], language[2], '\0'); diff --git a/src/posix/cocoa/i_timer.cpp b/src/posix/cocoa/i_timer.cpp index 5141f8750c..85a0ae8ce7 100644 --- a/src/posix/cocoa/i_timer.cpp +++ b/src/posix/cocoa/i_timer.cpp @@ -184,7 +184,7 @@ unsigned int I_FPSTime() } -double I_GetTimeFrac(uint32* ms) +double I_GetTimeFrac(uint32_t* ms) { const uint32_t now = I_MSTime(); diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 7c6f71db7c..906fc413df 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -48,7 +48,7 @@ #include "m_argv.h" #include "m_png.h" #include "r_renderer.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" #include "st_console.h" #include "stats.h" #include "textures.h" @@ -114,11 +114,23 @@ @end +DFrameBuffer *CreateGLSWFrameBuffer(int width, int height, bool bgra, bool fullscreen); EXTERN_CVAR(Bool, ticker ) EXTERN_CVAR(Bool, vid_vsync) EXTERN_CVAR(Bool, vid_hidpi) +CUSTOM_CVAR(Bool, swtruecolor, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + // Strictly speaking this doesn't require a mode switch, but it is the easiest + // way to force a CreateFramebuffer call without a lot of refactoring. + extern int NewWidth, NewHeight, NewBits, DisplayBits; + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; +} + CUSTOM_CVAR(Bool, fullscreen, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { extern int NewWidth, NewHeight, NewBits, DisplayBits; @@ -251,7 +263,7 @@ public: virtual EDisplayType GetDisplayType() { return DISPLAY_Both; } virtual void SetWindowedScale(float scale); - virtual DFrameBuffer* CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer* old); + virtual DFrameBuffer* CreateFrameBuffer(int width, int height, bool bgra, bool fs, DFrameBuffer* old); virtual void StartModeIterator(int bits, bool fullscreen); virtual bool NextMode(int* width, int* height, bool* letterbox); @@ -293,7 +305,7 @@ private: class CocoaFrameBuffer : public DFrameBuffer { public: - CocoaFrameBuffer(int width, int height, bool fullscreen); + CocoaFrameBuffer(int width, int height, bool bgra, bool fullscreen); ~CocoaFrameBuffer(); virtual bool Lock(bool buffer); @@ -496,7 +508,7 @@ NSOpenGLPixelFormat* CreatePixelFormat(const OpenGLProfile profile) attributes[i++] = NSOpenGLPFAAllowOfflineRenderers; } - if (NSAppKitVersionNumber >= AppKit10_7 && OpenGLProfile::Core == profile && 1 == vid_renderer) + if (NSAppKitVersionNumber >= AppKit10_7 && OpenGLProfile::Core == profile) { NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersion3_2Core; const char* const glversion = Args->CheckValue("-glversion"); @@ -606,14 +618,14 @@ bool CocoaVideo::NextMode(int* const width, int* const height, bool* const lette return false; } -DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, const bool fullscreen, DFrameBuffer* const old) +DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, const bool bgra, const bool fullscreen, DFrameBuffer* const old) { PalEntry flashColor = 0; int flashAmount = 0; if (NULL != old) { - if (width == m_width && height == m_height) + if (width == m_width && height == m_height && bgra == old->IsBgra()) { SetMode(width, height, fullscreen, vid_hidpi); return old; @@ -638,7 +650,8 @@ DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, c } else { - fb = new CocoaFrameBuffer(width, height, fullscreen); + //fb = new CocoaFrameBuffer(width, height, bgra, fullscreen); + fb = CreateGLSWFrameBuffer(width, height, bgra, fullscreen); } fb->SetFlash(flashColor, flashAmount); @@ -862,8 +875,8 @@ CocoaVideo* CocoaVideo::GetInstance() // --------------------------------------------------------------------------- -CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool fullscreen) -: DFrameBuffer(width, height) +CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool bgra, bool fullscreen) +: DFrameBuffer(width, height, bgra) , m_needPaletteUpdate(false) , m_gamma(0.0f) , m_needGammaUpdate(false) @@ -965,8 +978,15 @@ void CocoaFrameBuffer::Update() FlipCycles.Reset(); BlitCycles.Clock(); - GPfx.Convert(MemBuffer, Pitch, m_pixelBuffer, Width * BYTES_PER_PIXEL, - Width, Height, FRACUNIT, FRACUNIT, 0, 0); + if (IsBgra()) + { + CopyWithGammaBgra(m_pixelBuffer, Width * BYTES_PER_PIXEL, m_gammaTable[0], m_gammaTable[1], m_gammaTable[2], m_flashColor, m_flashAmount); + } + else + { + GPfx.Convert(MemBuffer, Pitch, m_pixelBuffer, Width * BYTES_PER_PIXEL, + Width, Height, FRACUNIT, FRACUNIT, 0, 0); + } FlipCycles.Clock(); Flip(); @@ -1098,8 +1118,10 @@ void CocoaFrameBuffer::Flip() static const GLenum format = GL_ABGR_EXT; #endif // __LITTLE_ENDIAN__ - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, - Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); + if (IsBgra()) + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, Width, Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, m_pixelBuffer); + else + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); glBegin(GL_QUADS); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); @@ -1122,8 +1144,8 @@ void CocoaFrameBuffer::Flip() // --------------------------------------------------------------------------- -SDLGLFB::SDLGLFB(void*, const int width, const int height, int, int, const bool fullscreen) -: DFrameBuffer(width, height) +SDLGLFB::SDLGLFB(void*, const int width, const int height, int, int, const bool fullscreen, bool bgra) +: DFrameBuffer(width, height, bgra) , m_lock(-1) , m_isUpdatePending(false) { @@ -1329,7 +1351,7 @@ void I_CreateRenderer() DFrameBuffer* I_SetMode(int &width, int &height, DFrameBuffer* old) { - return Video->CreateFrameBuffer(width, height, fullscreen, old); + return Video->CreateFrameBuffer(width, height, swtruecolor, fullscreen, old); } bool I_CheckResolution(const int width, const int height, const int bits) @@ -1354,7 +1376,7 @@ void I_ClosestResolution(int *width, int *height, int bits) int twidth, theight; int cwidth = 0, cheight = 0; int iteration; - DWORD closest = DWORD(-1); + uint32_t closest = uint32_t(-1); for (iteration = 0; iteration < 2; ++iteration) { @@ -1372,7 +1394,7 @@ void I_ClosestResolution(int *width, int *height, int bits) continue; } - const DWORD dist = (twidth - *width) * (twidth - *width) + const uint32_t dist = (twidth - *width) * (twidth - *width) + (theight - *height) * (theight - *height); if (dist < closest) @@ -1383,7 +1405,7 @@ void I_ClosestResolution(int *width, int *height, int bits) } } - if (closest != DWORD(-1)) + if (closest != uint32_t(-1)) { *width = cwidth; *height = cheight; diff --git a/src/posix/cocoa/sdlglvideo.h b/src/posix/cocoa/sdlglvideo.h index 180598692b..b0076f11bd 100644 --- a/src/posix/cocoa/sdlglvideo.h +++ b/src/posix/cocoa/sdlglvideo.h @@ -52,7 +52,7 @@ class SDLGLFB : public DFrameBuffer { public: // This must have the same parameters as the Windows version, even if they are not used! - SDLGLFB(void *hMonitor, int width, int height, int, int, bool fullscreen); + SDLGLFB(void *hMonitor, int width, int height, int, int, bool fullscreen, bool bgra); ~SDLGLFB(); virtual bool Lock(bool buffered = true); diff --git a/src/posix/cocoa/st_console.mm b/src/posix/cocoa/st_console.mm index b037fe6dd5..67f906c691 100644 --- a/src/posix/cocoa/st_console.mm +++ b/src/posix/cocoa/st_console.mm @@ -40,7 +40,7 @@ #include "version.h" -static NSColor* RGB(const BYTE red, const BYTE green, const BYTE blue) +static NSColor* RGB(const uint8_t red, const uint8_t green, const uint8_t blue) { return [NSColor colorWithCalibratedRed:red / 255.0f green:green / 255.0f @@ -53,7 +53,7 @@ static NSColor* RGB(const PalEntry& color) return RGB(color.r, color.g, color.b); } -static NSColor* RGB(const DWORD color) +static NSColor* RGB(const uint32_t color) { return RGB(PalEntry(color)); } @@ -250,7 +250,7 @@ void FConsoleWindow::AddText(const char* message) if (TEXTCOLOR_ESCAPE == *message) { - const BYTE* colorID = reinterpret_cast(message) + 1; + const uint8_t* colorID = reinterpret_cast(message) + 1; if ('\0' == *colorID) { break; diff --git a/src/posix/hardware.h b/src/posix/hardware.h index 618941fe59..3c06cb6c6d 100644 --- a/src/posix/hardware.h +++ b/src/posix/hardware.h @@ -74,7 +74,7 @@ class IVideo virtual EDisplayType GetDisplayType () = 0; virtual void SetWindowedScale (float scale) = 0; - virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old) = 0; + virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old) = 0; virtual void StartModeIterator (int bits, bool fs) = 0; virtual bool NextMode (int *width, int *height, bool *letterbox) = 0; diff --git a/src/posix/i_input.h b/src/posix/i_input.h index 07ee1115d2..e69de29bb2 100644 --- a/src/posix/i_input.h +++ b/src/posix/i_input.h @@ -1,10 +0,0 @@ -#ifndef __I_INPUT_H__ -#define __I_INPUT_H__ - -void I_PutInClipboard (const char *str); -FString I_GetFromClipboard (bool use_primary_selection); -void I_SetMouseCapture(); -void I_ReleaseMouseCapture(); - -#endif - diff --git a/src/posix/i_system.h b/src/posix/i_system.h index 3754d29e76..2110007cfd 100644 --- a/src/posix/i_system.h +++ b/src/posix/i_system.h @@ -43,7 +43,7 @@ enum LANGIDX_SysPreferred, LANGIDX_SysDefault }; -extern DWORD LanguageIDs[4]; +extern uint32_t LanguageIDs[4]; extern void SetLanguageIDs (); // Called by DoomMain. @@ -62,7 +62,7 @@ extern int (*I_WaitForTic) (int); // tic will never arrive (unless it's the current one). extern void (*I_FreezeTime) (bool frozen); -double I_GetTimeFrac (uint32 *ms); +double I_GetTimeFrac (uint32_t *ms); // Return a seed value for the RNG. unsigned int I_MakeRNGSeed(); diff --git a/src/posix/sdl/critsec.h b/src/posix/sdl/critsec.cpp similarity index 65% rename from src/posix/sdl/critsec.h rename to src/posix/sdl/critsec.cpp index a3d6210af4..8de14c8df5 100644 --- a/src/posix/sdl/critsec.h +++ b/src/posix/sdl/critsec.cpp @@ -2,17 +2,14 @@ // object similar to a mutex but optimized for access by threads belonging to // only one process, hence the class name.) -#ifndef CRITSEC_H -#define CRITSEC_H - #include "SDL.h" #include "SDL_thread.h" #include "i_system.h" -class FCriticalSection +class FInternalCriticalSection { public: - FCriticalSection() + FInternalCriticalSection() { CritSec = SDL_CreateMutex(); if (CritSec == NULL) @@ -20,7 +17,7 @@ public: I_FatalError("Failed to create a critical section mutex."); } } - ~FCriticalSection() + ~FInternalCriticalSection() { if (CritSec != NULL) { @@ -45,4 +42,22 @@ private: SDL_mutex *CritSec; }; -#endif +FInternalCriticalSection *CreateCriticalSection() +{ + return new FInternalCriticalSection(); +} + +void DeleteCriticalSection(FInternalCriticalSection *c) +{ + delete c; +} + +void EnterCriticalSection(FInternalCriticalSection *c) +{ + c->Enter(); +} + +void LeaveCriticalSection(FInternalCriticalSection *c) +{ + c->Leave(); +} diff --git a/src/posix/sdl/hardware.cpp b/src/posix/sdl/hardware.cpp index f4ac13fc5e..e512adb736 100644 --- a/src/posix/sdl/hardware.cpp +++ b/src/posix/sdl/hardware.cpp @@ -49,10 +49,11 @@ #include "m_argv.h" #include "sdlglvideo.h" #include "r_renderer.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" EXTERN_CVAR (Bool, ticker) EXTERN_CVAR (Bool, fullscreen) +EXTERN_CVAR (Bool, swtruecolor) EXTERN_CVAR (Float, vid_winscale) IVideo *Video; @@ -119,8 +120,7 @@ void I_InitGraphics () ticker.SetGenericRepDefault (val, CVAR_Bool); //currentrenderer = vid_renderer; - if (currentrenderer==1) Video = new SDLGLVideo(0); - else Video = new SDLVideo (0); + Video = new SDLGLVideo(0); if (Video == NULL) I_FatalError ("Failed to initialize display"); @@ -166,7 +166,7 @@ DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old) fs = fullscreen; break; } - DFrameBuffer *res = Video->CreateFrameBuffer (width, height, fs, old); + DFrameBuffer *res = Video->CreateFrameBuffer (width, height, swtruecolor, fs, old); /* Right now, CreateFrameBuffer cannot return NULL if (res == NULL) @@ -195,7 +195,7 @@ void I_ClosestResolution (int *width, int *height, int bits) int twidth, theight; int cwidth = 0, cheight = 0; int iteration; - DWORD closest = 4294967295u; + uint32_t closest = 4294967295u; for (iteration = 0; iteration < 2; iteration++) { @@ -208,7 +208,7 @@ void I_ClosestResolution (int *width, int *height, int bits) if (iteration == 0 && (twidth < *width || theight < *height)) continue; - DWORD dist = (twidth - *width) * (twidth - *width) + uint32_t dist = (twidth - *width) * (twidth - *width) + (theight - *height) * (theight - *height); if (dist < closest) @@ -320,6 +320,16 @@ CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) extern int NewWidth, NewHeight, NewBits, DisplayBits; +CUSTOM_CVAR(Bool, swtruecolor, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ + // Strictly speaking this doesn't require a mode switch, but it is the easiest + // way to force a CreateFramebuffer call without a lot of refactoring. + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; +} + CUSTOM_CVAR (Bool, fullscreen, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { NewWidth = screen->GetWidth(); diff --git a/src/posix/sdl/i_gui.cpp b/src/posix/sdl/i_gui.cpp index 37a3b1062f..b757ef2538 100644 --- a/src/posix/sdl/i_gui.cpp +++ b/src/posix/sdl/i_gui.cpp @@ -26,7 +26,7 @@ bool I_SetCursor(FTexture *cursorpic) cursorSurface = SDL_CreateRGBSurface (0, 32, 32, 32, MAKEARGB(0,255,0,0), MAKEARGB(0,0,255,0), MAKEARGB(0,0,0,255), MAKEARGB(255,0,0,0)); SDL_LockSurface(cursorSurface); - BYTE buffer[32*32*4]; + uint8_t buffer[32*32*4]; memset(buffer, 0, 32*32*4); FBitmap bmp(buffer, 32*4, 32, 32); cursorpic->CopyTrueColorPixels(&bmp, 0, 0); diff --git a/src/posix/sdl/i_joystick.cpp b/src/posix/sdl/i_joystick.cpp index d99caeca9d..0f1b62ac49 100644 --- a/src/posix/sdl/i_joystick.cpp +++ b/src/posix/sdl/i_joystick.cpp @@ -139,7 +139,7 @@ public: void ProcessInput() { - BYTE buttonstate; + uint8_t buttonstate; for (int i = 0; i < NumAxes; ++i) { @@ -206,7 +206,7 @@ protected: float Multiplier; EJoyAxis GameAxis; double Value; - BYTE ButtonValue; + uint8_t ButtonValue; }; static const EJoyAxis DefaultAxes[5]; diff --git a/src/posix/sdl/i_main.cpp b/src/posix/sdl/i_main.cpp index 90b11ef25d..7fe5aeb083 100644 --- a/src/posix/sdl/i_main.cpp +++ b/src/posix/sdl/i_main.cpp @@ -170,10 +170,10 @@ static int DoomSpecificInfo (char *buffer, char *end) } else { - p += snprintf (buffer+p, size-p, "\n\nviewx = %f", ViewPos.X); - p += snprintf (buffer+p, size-p, "\nviewy = %f", ViewPos.Y); - p += snprintf (buffer+p, size-p, "\nviewz = %f", ViewPos.Z); - p += snprintf (buffer+p, size-p, "\nviewangle = %f", ViewAngle.Degrees); + p += snprintf (buffer+p, size-p, "\n\nviewx = %f", r_viewpoint.Pos.X); + p += snprintf (buffer+p, size-p, "\nviewy = %f", r_viewpoint.Pos.Y); + p += snprintf (buffer+p, size-p, "\nviewz = %f", r_viewpoint.Pos.Z); + p += snprintf (buffer+p, size-p, "\nviewangle = %f", r_viewpoint.Angles.Yaw.Degrees); } } buffer[p++] = '\n'; diff --git a/src/posix/sdl/i_system.cpp b/src/posix/sdl/i_system.cpp index 8e4e63c4fc..e00f137a7a 100644 --- a/src/posix/sdl/i_system.cpp +++ b/src/posix/sdl/i_system.cpp @@ -84,7 +84,7 @@ int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad); int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad); #endif -DWORD LanguageIDs[4]; +uint32_t LanguageIDs[4]; int (*I_GetTime) (bool saveMS); int (*I_WaitForTic) (int); @@ -123,7 +123,7 @@ void SetLanguageIDs () { size_t langlen = strlen(language); - DWORD lang = (langlen < 2 || langlen > 3) ? + uint32_t lang = (langlen < 2 || langlen > 3) ? MAKE_ID('e','n','u','\0') : MAKE_ID(language[0],language[1],language[2],'\0'); diff --git a/src/posix/sdl/i_timer.cpp b/src/posix/sdl/i_timer.cpp index 84108f3b77..aad0192828 100644 --- a/src/posix/sdl/i_timer.cpp +++ b/src/posix/sdl/i_timer.cpp @@ -13,14 +13,14 @@ #include "templates.h" -static DWORD TicStart; -static DWORD BaseTime; +static uint32_t TicStart; +static uint32_t BaseTime; static int TicFrozen; // Signal based timer. static Semaphore timerWait; static int tics; -static DWORD sig_start; +static uint32_t sig_start; void I_SelectTimer(); @@ -54,7 +54,7 @@ int I_GetTimePolled (bool saveMS) return TicFrozen; } - DWORD tm = SDL_GetTicks(); + uint32_t tm = SDL_GetTicks(); if (saveMS) { @@ -177,9 +177,9 @@ void I_SelectTimer() } // Returns the fractional amount of a tic passed since the most recent tic -double I_GetTimeFrac (uint32 *ms) +double I_GetTimeFrac (uint32_t *ms) { - DWORD now = SDL_GetTicks (); + uint32_t now = SDL_GetTicks (); if (ms) *ms = TicStart + (1000 / TICRATE); if (TicStart == 0) { diff --git a/src/posix/sdl/sdlglvideo.cpp b/src/posix/sdl/sdlglvideo.cpp index 2cd07b2247..12f33da403 100644 --- a/src/posix/sdl/sdlglvideo.cpp +++ b/src/posix/sdl/sdlglvideo.cpp @@ -13,6 +13,7 @@ #include "c_console.h" #include "sdlglvideo.h" +#include "sdlvideo.h" #include "gl/system/gl_system.h" #include "r_defs.h" #include "gl/gl_functions.h" @@ -29,6 +30,7 @@ // TYPES ------------------------------------------------------------------- +IMPLEMENT_CLASS(SDLBaseFB, true, false) IMPLEMENT_CLASS(SDLGLFB, true, false) struct MiniModeInfo @@ -52,12 +54,30 @@ EXTERN_CVAR (Int, vid_renderer) EXTERN_CVAR (Int, vid_maxfps) EXTERN_CVAR (Bool, cl_capfps) +DFrameBuffer *CreateGLSWFrameBuffer(int width, int height, bool bgra, bool fullscreen); + // PUBLIC DATA DEFINITIONS ------------------------------------------------- CUSTOM_CVAR(Bool, gl_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { Printf("This won't take effect until " GAMENAME " is restarted.\n"); } +CUSTOM_CVAR(Bool, vid_glswfb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + Printf("This won't take effect until " GAMENAME " is restarted.\n"); +} + +#ifdef __arm__ +CUSTOM_CVAR(Bool, gl_es, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + Printf("This won't take effect until " GAMENAME " is restarted.\n"); +} +#else +CUSTOM_CVAR(Bool, gl_es, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + Printf("This won't take effect until " GAMENAME " is restarted.\n"); +} +#endif // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -159,7 +179,7 @@ bool SDLGLVideo::NextMode (int *width, int *height, bool *letterbox) return false; } -DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) +DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool bgra, bool fullscreen, DFrameBuffer *old) { static int retry = 0; static int owidth, oheight; @@ -169,15 +189,15 @@ DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscr if (old != NULL) { // Reuse the old framebuffer if its attributes are the same - SDLGLFB *fb = static_cast (old); + SDLBaseFB *fb = static_cast (old); if (fb->Width == width && fb->Height == height) { - bool fsnow = (SDL_GetWindowFlags (fb->Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; + bool fsnow = (SDL_GetWindowFlags (fb->GetSDLWindow()) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; if (fsnow != fullscreen) { - SDL_SetWindowFullscreen (fb->Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + SDL_SetWindowFullscreen (fb->GetSDLWindow(), fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); } return old; } @@ -190,7 +210,22 @@ DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscr // flashAmount = 0; } - SDLGLFB *fb = new OpenGLFrameBuffer (0, width, height, 32, 60, fullscreen); + SDLBaseFB *fb; + if (vid_renderer == 1) + { + fb = new OpenGLFrameBuffer(0, width, height, 32, 60, fullscreen); + } + else if (vid_glswfb == 0) + { + fb = new SDLFB(width, height, bgra, fullscreen, nullptr); + } + else + { + fb = (SDLBaseFB*)CreateGLSWFrameBuffer(width, height, bgra, fullscreen); + if (!fb->IsValid()) + fb = new SDLFB(width, height, bgra, fullscreen, nullptr); + } + retry = 0; // If we could not create the framebuffer, try again with slightly @@ -233,7 +268,7 @@ DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscr } ++retry; - fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); + fb = static_cast(CreateFrameBuffer (width, height, false, fullscreen, NULL)); } // fb->SetFlash (flashColor, flashAmount); @@ -288,6 +323,14 @@ bool SDLGLVideo::SetupPixelFormat(bool allowsoftware, int multisample) } if (gl_debug) SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + + if (gl_es) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } + return true; } @@ -310,8 +353,8 @@ bool SDLGLVideo::InitHardware (bool allowsoftware, int multisample) // FrameBuffer implementation ----------------------------------------------- -SDLGLFB::SDLGLFB (void *, int width, int height, int, int, bool fullscreen) - : DFrameBuffer (width, height) +SDLGLFB::SDLGLFB (void *, int width, int height, int, int, bool fullscreen, bool bgra) + : SDLBaseFB (width, height, bgra) { int i; diff --git a/src/posix/sdl/sdlglvideo.h b/src/posix/sdl/sdlglvideo.h index d157a0c311..de37abbf4b 100644 --- a/src/posix/sdl/sdlglvideo.h +++ b/src/posix/sdl/sdlglvideo.h @@ -21,7 +21,7 @@ class SDLGLVideo : public IVideo EDisplayType GetDisplayType () { return DISPLAY_Both; } void SetWindowedScale (float scale); - DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old); void StartModeIterator (int bits, bool fs); bool NextMode (int *width, int *height, bool *letterbox); @@ -34,12 +34,23 @@ private: int IteratorMode; int IteratorBits; }; -class SDLGLFB : public DFrameBuffer + +class SDLBaseFB : public DFrameBuffer { - DECLARE_CLASS(SDLGLFB, DFrameBuffer) + DECLARE_CLASS(SDLBaseFB, DFrameBuffer) +public: + using DFrameBuffer::DFrameBuffer; + virtual SDL_Window *GetSDLWindow() = 0; + + friend class SDLGLVideo; +}; + +class SDLGLFB : public SDLBaseFB +{ + DECLARE_CLASS(SDLGLFB, SDLBaseFB) public: // this must have the same parameters as the Windows version, even if they are not used! - SDLGLFB (void *hMonitor, int width, int height, int, int, bool fullscreen); + SDLGLFB (void *hMonitor, int width, int height, int, int, bool fullscreen, bool bgra); ~SDLGLFB (); void ForceBuffering (bool force); @@ -61,6 +72,8 @@ public: int GetClientWidth(); int GetClientHeight(); + SDL_Window *GetSDLWindow() override { return Screen; } + protected: bool CanUpdate(); void SetGammaTable(uint16_t *tbl); @@ -68,7 +81,7 @@ protected: void InitializeState(); SDLGLFB () {} - BYTE GammaTable[3][256]; + uint8_t GammaTable[3][256]; bool UpdatePending; SDL_Window *Screen; diff --git a/src/posix/sdl/sdlvideo.cpp b/src/posix/sdl/sdlvideo.cpp index 5902fc03b8..030129fc00 100644 --- a/src/posix/sdl/sdlvideo.cpp +++ b/src/posix/sdl/sdlvideo.cpp @@ -11,7 +11,7 @@ #include "stats.h" #include "v_palette.h" #include "sdlvideo.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" #include "version.h" #include @@ -24,61 +24,6 @@ // TYPES ------------------------------------------------------------------- -class SDLFB : public DFrameBuffer -{ - DECLARE_CLASS(SDLFB, DFrameBuffer) -public: - SDLFB (int width, int height, bool fullscreen, SDL_Window *oldwin); - ~SDLFB (); - - bool Lock (bool buffer); - void Unlock (); - bool Relock (); - void ForceBuffering (bool force); - bool IsValid (); - void Update (); - PalEntry *GetPalette (); - void GetFlashedPalette (PalEntry pal[256]); - void UpdatePalette (); - bool SetGamma (float gamma); - bool SetFlash (PalEntry rgb, int amount); - void GetFlash (PalEntry &rgb, int &amount); - void SetFullscreen (bool fullscreen); - int GetPageCount (); - bool IsFullscreen (); - - friend class SDLVideo; - - virtual void SetVSync (bool vsync); - virtual void ScaleCoordsFromWindow(SWORD &x, SWORD &y); - -private: - PalEntry SourcePalette[256]; - BYTE GammaTable[3][256]; - PalEntry Flash; - int FlashAmount; - float Gamma; - bool UpdatePending; - - SDL_Window *Screen; - SDL_Renderer *Renderer; - union - { - SDL_Texture *Texture; - SDL_Surface *Surface; - }; - - bool UsingRenderer; - bool NeedPalUpdate; - bool NeedGammaUpdate; - bool NotPaletted; - - void UpdateColors (); - void ResetSDLRenderer (); - - SDLFB () {} -}; - IMPLEMENT_CLASS(SDLFB, false, false) struct MiniModeInfo @@ -132,72 +77,6 @@ CUSTOM_CVAR (Float, bgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // PRIVATE DATA DEFINITIONS ------------------------------------------------ -// Dummy screen sizes to pass when windowed -static MiniModeInfo WinModes[] = -{ - { 320, 200 }, - { 320, 240 }, - { 400, 225 }, // 16:9 - { 400, 300 }, - { 480, 270 }, // 16:9 - { 480, 360 }, - { 512, 288 }, // 16:9 - { 512, 384 }, - { 640, 360 }, // 16:9 - { 640, 400 }, - { 640, 480 }, - { 720, 480 }, // 16:10 - { 720, 540 }, - { 800, 450 }, // 16:9 - { 800, 480 }, - { 800, 500 }, // 16:10 - { 800, 600 }, - { 848, 480 }, // 16:9 - { 960, 600 }, // 16:10 - { 960, 720 }, - { 1024, 576 }, // 16:9 - { 1024, 600 }, // 17:10 - { 1024, 640 }, // 16:10 - { 1024, 768 }, - { 1088, 612 }, // 16:9 - { 1152, 648 }, // 16:9 - { 1152, 720 }, // 16:10 - { 1152, 864 }, - { 1280, 540 }, // 21:9 - { 1280, 720 }, // 16:9 - { 1280, 854 }, - { 1280, 800 }, // 16:10 - { 1280, 960 }, - { 1280, 1024 }, // 5:4 - { 1360, 768 }, // 16:9 - { 1366, 768 }, - { 1400, 787 }, // 16:9 - { 1400, 875 }, // 16:10 - { 1400, 1050 }, - { 1440, 900 }, - { 1440, 960 }, - { 1440, 1080 }, - { 1600, 900 }, // 16:9 - { 1600, 1000 }, // 16:10 - { 1600, 1200 }, - { 1680, 1050 }, // 16:10 - { 1920, 1080 }, - { 1920, 1200 }, - { 2048, 1536 }, - { 2560, 1080 }, // 21:9 - { 2560, 1440 }, - { 2560, 1600 }, - { 2560, 2048 }, - { 2880, 1800 }, - { 3200, 1800 }, - { 3440, 1440 }, // 21:9 - { 3840, 2160 }, - { 3840, 2400 }, - { 4096, 2160 }, - { 5120, 2160 }, // 21:9 - { 5120, 2880 } -}; - static cycle_t BlitCycles; static cycle_t SDLFlipCycles; @@ -228,131 +107,10 @@ void ScaleWithAspect (int &w, int &h, int Width, int Height) h = y; } -SDLVideo::SDLVideo (int parm) -{ - IteratorBits = 0; -} - -SDLVideo::~SDLVideo () -{ -} - -void SDLVideo::StartModeIterator (int bits, bool fs) -{ - IteratorMode = 0; - IteratorBits = bits; -} - -bool SDLVideo::NextMode (int *width, int *height, bool *letterbox) -{ - if (IteratorBits != 8) - return false; - - if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) - { - *width = WinModes[IteratorMode].Width; - *height = WinModes[IteratorMode].Height; - ++IteratorMode; - return true; - } - return false; -} - -DFrameBuffer *SDLVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) -{ - static int retry = 0; - static int owidth, oheight; - - PalEntry flashColor; - int flashAmount; - - SDL_Window *oldwin = NULL; - - if (old != NULL) - { // Reuse the old framebuffer if its attributes are the same - SDLFB *fb = static_cast (old); - if (fb->Width == width && - fb->Height == height) - { - bool fsnow = (SDL_GetWindowFlags (fb->Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; - - if (fsnow != fullscreen) - { - fb->SetFullscreen (fullscreen); - } - return old; - } - - oldwin = fb->Screen; - fb->Screen = NULL; - - old->GetFlash (flashColor, flashAmount); - old->ObjectFlags |= OF_YesReallyDelete; - if (screen == old) screen = NULL; - delete old; - } - else - { - flashColor = 0; - flashAmount = 0; - } - - SDLFB *fb = new SDLFB (width, height, fullscreen, oldwin); - - // If we could not create the framebuffer, try again with slightly - // different parameters in this order: - // 1. Try with the closest size - // 2. Try in the opposite screen mode with the original size - // 3. Try in the opposite screen mode with the closest size - // This is a somewhat confusing mass of recursion here. - - while (fb == NULL || !fb->IsValid ()) - { - if (fb != NULL) - { - delete fb; - } - - switch (retry) - { - case 0: - owidth = width; - oheight = height; - case 2: - // Try a different resolution. Hopefully that will work. - I_ClosestResolution (&width, &height, 8); - break; - - case 1: - // Try changing fullscreen mode. Maybe that will work. - width = owidth; - height = oheight; - fullscreen = !fullscreen; - break; - - default: - // I give up! - I_FatalError ("Could not create new screen (%d x %d)", owidth, oheight); - } - - ++retry; - fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); - } - retry = 0; - - fb->SetFlash (flashColor, flashAmount); - - return fb; -} - -void SDLVideo::SetWindowedScale (float scale) -{ -} - // FrameBuffer implementation ----------------------------------------------- -SDLFB::SDLFB (int width, int height, bool fullscreen, SDL_Window *oldwin) - : DFrameBuffer (width, height) +SDLFB::SDLFB (int width, int height, bool bgra, bool fullscreen, SDL_Window *oldwin) + : SDLBaseFB (width, height, bgra) { int i; @@ -495,7 +253,11 @@ void SDLFB::Update () pitch = Surface->pitch; } - if (NotPaletted) + if (Bgra) + { + CopyWithGammaBgra(pixels, pitch, GammaTable[0], GammaTable[1], GammaTable[2], Flash, FlashAmount); + } + else if (NotPaletted) { GPfx.Convert (MemBuffer, Pitch, pixels, pitch, Width, Height, @@ -675,13 +437,20 @@ void SDLFB::ResetSDLRenderer () SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255); Uint32 fmt; - switch(vid_displaybits) + if (Bgra) { - default: fmt = SDL_PIXELFORMAT_ARGB8888; break; - case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; - case 24: fmt = SDL_PIXELFORMAT_RGB888; break; - case 16: fmt = SDL_PIXELFORMAT_RGB565; break; - case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; + fmt = SDL_PIXELFORMAT_ARGB8888; + } + else + { + switch (vid_displaybits) + { + default: fmt = SDL_PIXELFORMAT_ARGB8888; break; + case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; + case 24: fmt = SDL_PIXELFORMAT_RGB888; break; + case 16: fmt = SDL_PIXELFORMAT_RGB565; break; + case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; + } } Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height); diff --git a/src/posix/sdl/sdlvideo.h b/src/posix/sdl/sdlvideo.h index 072167b5a2..001d6c0880 100644 --- a/src/posix/sdl/sdlvideo.h +++ b/src/posix/sdl/sdlvideo.h @@ -1,21 +1,60 @@ #include "hardware.h" #include "v_video.h" +#include "sdlglvideo.h" -class SDLVideo : public IVideo +class SDLFB : public SDLBaseFB { - public: - SDLVideo (int parm); - ~SDLVideo (); + DECLARE_CLASS(SDLFB, SDLBaseFB) +public: + SDLFB(int width, int height, bool bgra, bool fullscreen, SDL_Window *oldwin); + ~SDLFB(); - EDisplayType GetDisplayType () { return DISPLAY_Both; } - void SetWindowedScale (float scale); + bool Lock(bool buffer); + void Unlock(); + bool Relock(); + void ForceBuffering(bool force); + bool IsValid(); + void Update(); + PalEntry *GetPalette(); + void GetFlashedPalette(PalEntry pal[256]); + void UpdatePalette(); + bool SetGamma(float gamma); + bool SetFlash(PalEntry rgb, int amount); + void GetFlash(PalEntry &rgb, int &amount); + void SetFullscreen(bool fullscreen); + int GetPageCount(); + bool IsFullscreen(); - DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + friend class SDLGLVideo; - void StartModeIterator (int bits, bool fs); - bool NextMode (int *width, int *height, bool *letterbox); + virtual void SetVSync(bool vsync); + virtual void ScaleCoordsFromWindow(int16_t &x, int16_t &y); + + SDL_Window *GetSDLWindow() override { return Screen; } private: - int IteratorMode; - int IteratorBits; + PalEntry SourcePalette[256]; + uint8_t GammaTable[3][256]; + PalEntry Flash; + int FlashAmount; + float Gamma; + bool UpdatePending; + + SDL_Window *Screen; + SDL_Renderer *Renderer; + union + { + SDL_Texture *Texture; + SDL_Surface *Surface; + }; + + bool UsingRenderer; + bool NeedPalUpdate; + bool NeedGammaUpdate; + bool NotPaletted; + + void UpdateColors(); + void ResetSDLRenderer(); + + SDLFB() {} }; diff --git a/src/r_3dfloors.cpp b/src/r_3dfloors.cpp deleted file mode 100644 index 87c8af618e..0000000000 --- a/src/r_3dfloors.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* -** r_3dfloors.cpp -** software 3D floors addon -** -** by kgsws -*/ - -#include "templates.h" -#include "doomdef.h" -#include "p_local.h" -#include "c_dispatch.h" -#include "r_local.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "c_cvars.h" -#include "r_3dfloors.h" - -CVAR(Int, r_3dfloors, true, 0); - -namespace swrenderer -{ - -// external variables -int fake3D; -F3DFloor *fakeFloor; -fixed_t fakeHeight; -fixed_t fakeAlpha; -int fakeActive = 0; -double sclipBottom; -double sclipTop; -HeightLevel *height_top = NULL; -HeightLevel *height_cur = NULL; -int CurrentMirror = 0; -int CurrentSkybox = 0; - -// private variables -int height_max = -1; -TArray toplist; -ClipStack *clip_top = NULL; -ClipStack *clip_cur = NULL; - -void R_3D_DeleteHeights() -{ - height_cur = height_top; - while(height_cur) { - height_top = height_cur; - height_cur = height_cur->next; - M_Free(height_top); - } - height_max = -1; - height_top = height_cur = NULL; -} - -void R_3D_AddHeight(secplane_t *add, sector_t *sec) -{ - HeightLevel *near; - HeightLevel *curr; - - double height = add->ZatPoint(ViewPos); - if(height >= sec->CenterCeiling()) return; - if(height <= sec->CenterFloor()) return; - - fakeActive = 1; - - if(height_max >= 0) { - near = height_top; - while(near && near->height < height) near = near->next; - if(near) { - if(near->height == height) return; - curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); - curr->height = height; - curr->prev = near->prev; - curr->next = near; - if(near->prev) near->prev->next = curr; - else height_top = curr; - near->prev = curr; - } else { - curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); - curr->height = height; - curr->prev = height_cur; - curr->next = NULL; - height_cur->next = curr; - height_cur = curr; - } - } else { - height_top = height_cur = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); - height_top->height = height; - height_top->prev = NULL; - height_top->next = NULL; - } - height_max++; -} - -void R_3D_NewClip() -{ - ClipStack *curr; -// extern short floorclip[MAXWIDTH]; -// extern short ceilingclip[MAXWIDTH]; - - curr = (ClipStack*)M_Malloc(sizeof(ClipStack)); - curr->next = 0; - memcpy(curr->floorclip, floorclip, sizeof(short) * MAXWIDTH); - memcpy(curr->ceilingclip, ceilingclip, sizeof(short) * MAXWIDTH); - curr->ffloor = fakeFloor; - assert(fakeFloor->floorclip == NULL); - assert(fakeFloor->ceilingclip == NULL); - fakeFloor->floorclip = curr->floorclip; - fakeFloor->ceilingclip = curr->ceilingclip; - if(clip_top) { - clip_cur->next = curr; - clip_cur = curr; - } else { - clip_top = clip_cur = curr; - } -} - -void R_3D_ResetClip() -{ - clip_cur = clip_top; - while(clip_cur) - { - assert(clip_cur->ffloor->floorclip != NULL); - assert(clip_cur->ffloor->ceilingclip != NULL); - clip_cur->ffloor->ceilingclip = clip_cur->ffloor->floorclip = NULL; - clip_top = clip_cur; - clip_cur = clip_cur->next; - M_Free(clip_top); - } - clip_cur = clip_top = NULL; -} - -void R_3D_EnterSkybox() -{ - HeightStack current; - - current.height_top = height_top; - current.height_cur = height_cur; - current.height_max = height_max; - - toplist.Push(current); - - height_top = NULL; - height_cur = NULL; - height_max = -1; - - CurrentSkybox++; -} - -void R_3D_LeaveSkybox() -{ - HeightStack current; - - current.height_top = NULL; - current.height_cur = NULL; - current.height_max = -1; - - toplist.Pop(current); - - height_top = current.height_top; - height_cur = current.height_cur; - height_max = current.height_max; - - CurrentSkybox--; -} - -} diff --git a/src/r_3dfloors.h b/src/r_3dfloors.h deleted file mode 100644 index a703ae19a4..0000000000 --- a/src/r_3dfloors.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef SOFT_FAKE3D_H -#define SOFT_FAKE3D_H - -#include "p_3dfloors.h" - -EXTERN_CVAR(Int, r_3dfloors); - -namespace swrenderer -{ - -// special types - -struct HeightLevel -{ - double height; - struct HeightLevel *prev; - struct HeightLevel *next; -}; - -struct HeightStack -{ - HeightLevel *height_top; - HeightLevel *height_cur; - int height_max; -}; - -struct ClipStack -{ - short floorclip[MAXWIDTH]; - short ceilingclip[MAXWIDTH]; - F3DFloor *ffloor; - ClipStack *next; -}; - -// external varialbes - -// fake3D flags: -enum -{ - // BSP stage: - FAKE3D_FAKEFLOOR = 1, // fake floor, mark seg as FAKE - FAKE3D_FAKECEILING = 2, // fake ceiling, mark seg as FAKE - FAKE3D_FAKEBACK = 4, // R_AddLine with fake backsector, mark seg as FAKE - FAKE3D_FAKEMASK = 7, - FAKE3D_CLIPBOTFRONT = 8, // use front sector clipping info (bottom) - FAKE3D_CLIPTOPFRONT = 16, // use front sector clipping info (top) - - // sorting stage: - FAKE3D_CLIPBOTTOM = 1, // clip bottom - FAKE3D_CLIPTOP = 2, // clip top - FAKE3D_REFRESHCLIP = 4, // refresh clip info - FAKE3D_DOWN2UP = 8, // rendering from down to up (floors) -}; - -extern int fake3D; -extern F3DFloor *fakeFloor; -extern fixed_t fakeAlpha; -extern int fakeActive; -extern double sclipBottom; -extern double sclipTop; -extern HeightLevel *height_top; -extern HeightLevel *height_cur; -extern int CurrentMirror; -extern int CurrentSkybox; - -// functions -void R_3D_DeleteHeights(); -void R_3D_AddHeight(secplane_t *add, sector_t *sec); -void R_3D_NewClip(); -void R_3D_ResetClip(); -void R_3D_EnterSkybox(); -void R_3D_LeaveSkybox(); - -} - -#endif diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp deleted file mode 100644 index 14a2f153fc..0000000000 --- a/src/r_bsp.cpp +++ /dev/null @@ -1,1400 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// BSP traversal, handling of LineSegs for rendering. -// -//----------------------------------------------------------------------------- - - -#include - -#include "templates.h" - -#include "doomdef.h" - -#include "m_bbox.h" - -#include "i_system.h" -#include "p_lnspec.h" -#include "p_setup.h" - -#include "r_local.h" -#include "r_main.h" -#include "r_plane.h" -#include "r_draw.h" -#include "r_things.h" -#include "r_3dfloors.h" -#include "a_sharedglobal.h" -#include "g_level.h" -#include "p_effect.h" - -// State. -#include "doomstat.h" -#include "r_state.h" -#include "r_bsp.h" -#include "r_segs.h" -#include "v_palette.h" -#include "r_sky.h" -#include "po_man.h" -#include "r_data/colormaps.h" -#include "g_levellocals.h" - -CVAR (Bool, r_drawflat, false, 0) // [RH] Don't texture segs? -EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); - -namespace swrenderer -{ - using namespace drawerargs; - -seg_t* curline; -side_t* sidedef; -line_t* linedef; -sector_t* frontsector; -sector_t* backsector; - -// killough 4/7/98: indicates doors closed wrt automap bugfix: -int doorclosed; - -bool r_fakingunderwater; - -extern bool rw_prepped; -extern bool rw_havehigh, rw_havelow; -extern int rw_floorstat, rw_ceilstat; -extern bool rw_mustmarkfloor, rw_mustmarkceiling; -extern short walltop[MAXWIDTH]; // [RH] record max extents of wall -extern short wallbottom[MAXWIDTH]; -extern short wallupper[MAXWIDTH]; -extern short walllower[MAXWIDTH]; - -double rw_backcz1, rw_backcz2; -double rw_backfz1, rw_backfz2; -double rw_frontcz1, rw_frontcz2; -double rw_frontfz1, rw_frontfz2; - - -size_t MaxDrawSegs; -drawseg_t *drawsegs; -drawseg_t* firstdrawseg; -drawseg_t* ds_p; - -size_t FirstInterestingDrawseg; -TArray InterestingDrawsegs; - -FWallCoords WallC; -FWallTmapVals WallT; - -static BYTE FakeSide; - -int WindowLeft, WindowRight; -WORD MirrorFlags; -TArray WallPortals(1000); // note: this array needs to go away as reallocation can cause crashes. - - -subsector_t *InSubsector; - - - -void R_StoreWallRange (int start, int stop); - -// -// R_ClearDrawSegs -// -void R_ClearDrawSegs (void) -{ - if (drawsegs == NULL) - { - MaxDrawSegs = 256; // [RH] Default. Increased as needed. - firstdrawseg = drawsegs = (drawseg_t *)M_Malloc (MaxDrawSegs * sizeof(drawseg_t)); - } - FirstInterestingDrawseg = 0; - InterestingDrawsegs.Clear (); - ds_p = drawsegs; -} - - - -// -// ClipWallSegment -// Clips the given range of columns -// and includes it in the new clip list. -// -// -// 1/11/98 killough: Since a type "short" is sufficient, we -// should use it, since smaller arrays fit better in cache. -// - -struct cliprange_t -{ - short first, last; // killough -}; - - -// newend is one past the last valid seg -static cliprange_t *newend; -static cliprange_t solidsegs[MAXWIDTH/2+2]; - - - -//========================================================================== -// -// R_ClipWallSegment -// -// Clips the given range of columns, possibly including it in the clip list. -// Handles both windows (e.g. LineDefs with upper and lower textures) and -// solid walls (e.g. single sided LineDefs [middle texture]) that entirely -// block the view. -// -//========================================================================== - -bool R_ClipWallSegment (int first, int last, bool solid) -{ - cliprange_t *next, *start; - int i, j; - bool res = false; - - // Find the first range that touches the range - // (adjacent pixels are touching). - start = solidsegs; - while (start->last < first) - start++; - - if (first < start->first) - { - res = true; - if (last <= start->first) - { - // Post is entirely visible (above start). - R_StoreWallRange (first, last); - if (fake3D & FAKE3D_FAKEMASK) - { - return true; - } - - // Insert a new clippost for solid walls. - if (solid) - { - if (last == start->first) - { - start->first = first; - } - else - { - next = newend; - newend++; - while (next != start) - { - *next = *(next-1); - next--; - } - next->first = first; - next->last = last; - } - } - return true; - } - - // There is a fragment above *start. - R_StoreWallRange (first, start->first); - - // Adjust the clip size for solid walls - if (solid && !(fake3D & FAKE3D_FAKEMASK)) - { - start->first = first; - } - } - - // Bottom contained in start? - if (last <= start->last) - return res; - - next = start; - while (last >= (next+1)->first) - { - // There is a fragment between two posts. - R_StoreWallRange (next->last, (next+1)->first); - next++; - - if (last <= next->last) - { - // Bottom is contained in next. - last = next->last; - goto crunch; - } - } - - // There is a fragment after *next. - R_StoreWallRange (next->last, last); - -crunch: - if (fake3D & FAKE3D_FAKEMASK) - { - return true; - } - if (solid) - { - // Adjust the clip size. - start->last = last; - - if (next != start) - { - // Remove start+1 to next from the clip list, - // because start now covers their area. - for (i = 1, j = (int)(newend - next); j > 0; i++, j--) - { - start[i] = next[i]; - } - newend = start+i; - } - } - return true; -} - -bool R_CheckClipWallSegment (int first, int last) -{ - cliprange_t *start; - - // Find the first range that touches the range - // (adjacent pixels are touching). - start = solidsegs; - while (start->last < first) - start++; - - if (first < start->first) - { - return true; - } - - // Bottom contained in start? - if (last > start->last) - { - return true; - } - - return false; -} - - - -// -// R_ClearClipSegs -// -void R_ClearClipSegs (short left, short right) -{ - solidsegs[0].first = -0x7fff; // new short limit -- killough - solidsegs[0].last = left; - solidsegs[1].first = right; - solidsegs[1].last = 0x7fff; // new short limit -- killough - newend = solidsegs+2; -} - - -// -// killough 3/7/98: Hack floor/ceiling heights for deep water etc. -// -// If player's view height is underneath fake floor, lower the -// drawn ceiling to be just under the floor height, and replace -// the drawn floor and ceiling textures, and light level, with -// the control sector's. -// -// Similar for ceiling, only reflected. -// -// killough 4/11/98, 4/13/98: fix bugs, add 'back' parameter -// - -sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, - int *floorlightlevel, int *ceilinglightlevel, - bool back) -{ - // [RH] allow per-plane lighting - if (floorlightlevel != NULL) - { - *floorlightlevel = sec->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = sec->GetCeilingLight (); - } - - FakeSide = FAKED_Center; - - const sector_t *s = sec->GetHeightSec(); - if (s != NULL) - { - sector_t *heightsec = viewsector->heightsec; - bool underwater = r_fakingunderwater || - (heightsec && heightsec->floorplane.PointOnSide(ViewPos) <= 0); - bool doorunderwater = false; - int diffTex = (s->MoreFlags & SECF_CLIPFAKEPLANES); - - // Replace sector being drawn with a copy to be hacked - *tempsec = *sec; - - // Replace floor and ceiling height with control sector's heights. - if (diffTex) - { - if (s->floorplane.CopyPlaneIfValid (&tempsec->floorplane, &sec->ceilingplane)) - { - tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); - } - else if (s->MoreFlags & SECF_FAKEFLOORONLY) - { - if (underwater) - { - tempsec->ColorMap = s->ColorMap; - if (!(s->MoreFlags & SECF_NOFAKELIGHT)) - { - tempsec->lightlevel = s->lightlevel; - - if (floorlightlevel != NULL) - { - *floorlightlevel = s->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = s->GetCeilingLight (); - } - } - FakeSide = FAKED_BelowFloor; - return tempsec; - } - return sec; - } - } - else - { - tempsec->floorplane = s->floorplane; - } - - if (!(s->MoreFlags & SECF_FAKEFLOORONLY)) - { - if (diffTex) - { - if (s->ceilingplane.CopyPlaneIfValid (&tempsec->ceilingplane, &sec->floorplane)) - { - tempsec->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false); - } - } - else - { - tempsec->ceilingplane = s->ceilingplane; - } - } - - double refceilz = s->ceilingplane.ZatPoint(ViewPos); - double orgceilz = sec->ceilingplane.ZatPoint(ViewPos); - -#if 1 - // [RH] Allow viewing underwater areas through doors/windows that - // are underwater but not in a water sector themselves. - // Only works if you cannot see the top surface of any deep water - // sectors at the same time. - if (back && !r_fakingunderwater && curline->frontsector->heightsec == NULL) - { - if (rw_frontcz1 <= s->floorplane.ZatPoint(curline->v1) && - rw_frontcz2 <= s->floorplane.ZatPoint(curline->v2)) - { - // Check that the window is actually visible - for (int z = WallC.sx1; z < WallC.sx2; ++z) - { - if (floorclip[z] > ceilingclip[z]) - { - doorunderwater = true; - r_fakingunderwater = true; - break; - } - } - } - } -#endif - - if (underwater || doorunderwater) - { - tempsec->floorplane = sec->floorplane; - tempsec->ceilingplane = s->floorplane; - tempsec->ceilingplane.FlipVert (); - tempsec->ceilingplane.ChangeHeight(-1 / 65536.); - tempsec->ColorMap = s->ColorMap; - } - - // killough 11/98: prevent sudden light changes from non-water sectors: - if ((underwater && !back) || doorunderwater) - { // head-below-floor hack - tempsec->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false); - tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; - - tempsec->ceilingplane = s->floorplane; - tempsec->ceilingplane.FlipVert (); - tempsec->ceilingplane.ChangeHeight (-1 / 65536.); - if (s->GetTexture(sector_t::ceiling) == skyflatnum) - { - tempsec->floorplane = tempsec->ceilingplane; - tempsec->floorplane.FlipVert (); - tempsec->floorplane.ChangeHeight (+1 / 65536.); - tempsec->SetTexture(sector_t::ceiling, tempsec->GetTexture(sector_t::floor), false); - tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform; - } - else - { - tempsec->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false); - tempsec->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform; - } - - if (!(s->MoreFlags & SECF_NOFAKELIGHT)) - { - tempsec->lightlevel = s->lightlevel; - - if (floorlightlevel != NULL) - { - *floorlightlevel = s->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = s->GetCeilingLight (); - } - } - FakeSide = FAKED_BelowFloor; - } - else if (heightsec && heightsec->ceilingplane.PointOnSide(ViewPos) <= 0 && - orgceilz > refceilz && !(s->MoreFlags & SECF_FAKEFLOORONLY)) - { // Above-ceiling hack - tempsec->ceilingplane = s->ceilingplane; - tempsec->floorplane = s->ceilingplane; - tempsec->floorplane.FlipVert (); - tempsec->floorplane.ChangeHeight (+1 / 65536.); - tempsec->ColorMap = s->ColorMap; - tempsec->ColorMap = s->ColorMap; - - tempsec->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false); - tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false); - tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform; - - if (s->GetTexture(sector_t::floor) != skyflatnum) - { - tempsec->ceilingplane = sec->ceilingplane; - tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); - tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; - } - - if (!(s->MoreFlags & SECF_NOFAKELIGHT)) - { - tempsec->lightlevel = s->lightlevel; - - if (floorlightlevel != NULL) - { - *floorlightlevel = s->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = s->GetCeilingLight (); - } - } - FakeSide = FAKED_AboveCeiling; - } - sec = tempsec; // Use other sector - } - return sec; -} - - -bool R_SkyboxCompare(sector_t *frontsector, sector_t *backsector) -{ - FSectorPortal *frontc = frontsector->GetPortal(sector_t::ceiling); - FSectorPortal *frontf = frontsector->GetPortal(sector_t::floor); - FSectorPortal *backc = backsector->GetPortal(sector_t::ceiling); - FSectorPortal *backf = backsector->GetPortal(sector_t::floor); - - // return true if any of the planes has a linedef-based portal (unless both sides have the same one. - // Ideally this should also check thing based portals but the omission of this check had been abused to hell and back for those. - // (Note: This may require a compatibility option if some maps ran into this for line based portals as well.) - if (!frontc->MergeAllowed()) return (frontc != backc); - if (!frontf->MergeAllowed()) return (frontf != backf); - if (!backc->MergeAllowed()) return true; - if (!backf->MergeAllowed()) return true; - return false; -} - -// -// R_AddLine -// Clips the given segment -// and adds any visible pieces to the line list. -// - -void R_AddLine (seg_t *line) -{ - static sector_t tempsec; // killough 3/8/98: ceiling/water hack - bool solid; - DVector2 pt1, pt2; - - curline = line; - - // [RH] Color if not texturing line - dc_color = (((int)(line - segs) * 8) + 4) & 255; - - pt1 = line->v1->fPos() - ViewPos; - pt2 = line->v2->fPos() - ViewPos; - - // Reject lines not facing viewer - if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) - return; - - if (WallC.Init(pt1, pt2, 32.0 / (1 << 12))) - return; - - if (WallC.sx1 >= WindowRight || WallC.sx2 <= WindowLeft) - return; - - if (line->linedef == NULL) - { - if (R_CheckClipWallSegment (WallC.sx1, WallC.sx2)) - { - InSubsector->flags |= SSECF_DRAWN; - } - return; - } - - // reject lines that aren't seen from the portal (if any) - // [ZZ] 10.01.2016: lines inside a skybox shouldn't be clipped, although this imposes some limitations on portals in skyboxes. - if (!CurrentPortalInSkybox && CurrentPortal && P_ClipLineToPortal(line->linedef, CurrentPortal->dst, ViewPos)) - return; - - vertex_t *v1, *v2; - v1 = line->linedef->v1; - v2 = line->linedef->v2; - - if ((v1 == line->v1 && v2 == line->v2) || (v2 == line->v1 && v1 == line->v2)) - { // The seg is the entire wall. - WallT.InitFromWallCoords(&WallC); - } - else - { // The seg is only part of the wall. - if (line->linedef->sidedef[0] != line->sidedef) - { - swapvalues (v1, v2); - } - WallT.InitFromLine(v1->fPos() - ViewPos, v2->fPos() - ViewPos); - } - - if (!(fake3D & FAKE3D_FAKEBACK)) - { - backsector = line->backsector; - } - rw_frontcz1 = frontsector->ceilingplane.ZatPoint(line->v1); - rw_frontfz1 = frontsector->floorplane.ZatPoint(line->v1); - rw_frontcz2 = frontsector->ceilingplane.ZatPoint(line->v2); - rw_frontfz2 = frontsector->floorplane.ZatPoint(line->v2); - - rw_mustmarkfloor = rw_mustmarkceiling = false; - rw_havehigh = rw_havelow = false; - - // Single sided line? - if (backsector == NULL) - { - solid = true; - } - else - { - // kg3D - its fake, no transfer_heights - if (!(fake3D & FAKE3D_FAKEBACK)) - { // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water - backsector = R_FakeFlat (backsector, &tempsec, NULL, NULL, true); - } - doorclosed = 0; // killough 4/16/98 - - rw_backcz1 = backsector->ceilingplane.ZatPoint(line->v1); - rw_backfz1 = backsector->floorplane.ZatPoint(line->v1); - rw_backcz2 = backsector->ceilingplane.ZatPoint(line->v2); - rw_backfz2 = backsector->floorplane.ZatPoint(line->v2); - - if (fake3D & FAKE3D_FAKEBACK) - { - if (rw_frontfz1 >= rw_backfz1 && rw_frontfz2 >= rw_backfz2) - { - fake3D |= FAKE3D_CLIPBOTFRONT; - } - if (rw_frontcz1 <= rw_backcz1 && rw_frontcz2 <= rw_backcz2) - { - fake3D |= FAKE3D_CLIPTOPFRONT; - } - } - - // Cannot make these walls solid, because it can result in - // sprite clipping problems for sprites near the wall - if (rw_frontcz1 > rw_backcz1 || rw_frontcz2 > rw_backcz2) - { - rw_havehigh = true; - R_CreateWallSegmentYSloped (wallupper, backsector->ceilingplane, &WallC); - } - if (rw_frontfz1 < rw_backfz1 || rw_frontfz2 < rw_backfz2) - { - rw_havelow = true; - R_CreateWallSegmentYSloped (walllower, backsector->floorplane, &WallC); - } - - // Portal - if (line->linedef->isVisualPortal() && line->sidedef == line->linedef->sidedef[0]) - { - solid = true; - } - // Closed door. - else if ((rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2) || - (rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2)) - { - solid = true; - } - else if ( - // properly render skies (consider door "open" if both ceilings are sky): - (backsector->GetTexture(sector_t::ceiling) != skyflatnum || frontsector->GetTexture(sector_t::ceiling) != skyflatnum) - - // if door is closed because back is shut: - && rw_backcz1 <= rw_backfz1 && rw_backcz2 <= rw_backfz2 - - // preserve a kind of transparent door/lift special effect: - && ((rw_backcz1 >= rw_frontcz1 && rw_backcz2 >= rw_frontcz2) || line->sidedef->GetTexture(side_t::top).isValid()) - && ((rw_backfz1 <= rw_frontfz1 && rw_backfz2 <= rw_frontfz2) || line->sidedef->GetTexture(side_t::bottom).isValid())) - { - // killough 1/18/98 -- This function is used to fix the automap bug which - // showed lines behind closed doors simply because the door had a dropoff. - // - // It assumes that Doom has already ruled out a door being closed because - // of front-back closure (e.g. front floor is taller than back ceiling). - - // This fixes the automap floor height bug -- killough 1/18/98: - // killough 4/7/98: optimize: save result in doorclosed for use in r_segs.c - doorclosed = true; - solid = true; - } - else if (frontsector->ceilingplane != backsector->ceilingplane || - frontsector->floorplane != backsector->floorplane) - { - // Window. - solid = false; - } - else if (R_SkyboxCompare(frontsector, backsector)) - { - solid = false; - } - else if (backsector->lightlevel != frontsector->lightlevel - || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) - || backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling) - || curline->sidedef->GetTexture(side_t::mid).isValid() - - // killough 3/7/98: Take flats offsets into account: - || backsector->planes[sector_t::floor].xform != frontsector->planes[sector_t::floor].xform - || backsector->planes[sector_t::ceiling].xform != frontsector->planes[sector_t::ceiling].xform - - || backsector->GetPlaneLight(sector_t::floor) != frontsector->GetPlaneLight(sector_t::floor) - || backsector->GetPlaneLight(sector_t::ceiling) != frontsector->GetPlaneLight(sector_t::ceiling) - || backsector->GetVisFlags(sector_t::floor) != frontsector->GetVisFlags(sector_t::floor) - || backsector->GetVisFlags(sector_t::ceiling) != frontsector->GetVisFlags(sector_t::ceiling) - - // [RH] Also consider colormaps - || backsector->ColorMap != frontsector->ColorMap - - - - // kg3D - and fake lights - || (frontsector->e && frontsector->e->XFloor.lightlist.Size()) - || (backsector->e && backsector->e->XFloor.lightlist.Size()) - ) - { - solid = false; - } - else - { - // Reject empty lines used for triggers and special events. - // Identical floor and ceiling on both sides, identical light levels - // on both sides, and no middle texture. - - // When using GL nodes, do a clipping test for these lines so we can - // mark their subsectors as visible for automap texturing. - if (hasglnodes && !(InSubsector->flags & SSECF_DRAWN)) - { - if (R_CheckClipWallSegment(WallC.sx1, WallC.sx2)) - { - InSubsector->flags |= SSECF_DRAWN; - } - } - return; - } - } - - rw_prepped = false; - - if (line->linedef->special == Line_Horizon) - { - // Be aware: Line_Horizon does not work properly with sloped planes - fillshort (walltop+WallC.sx1, WallC.sx2 - WallC.sx1, centery); - fillshort (wallbottom+WallC.sx1, WallC.sx2 - WallC.sx1, centery); - } - else - { - rw_ceilstat = R_CreateWallSegmentYSloped (walltop, frontsector->ceilingplane, &WallC); - rw_floorstat = R_CreateWallSegmentYSloped (wallbottom, frontsector->floorplane, &WallC); - - // [RH] treat off-screen walls as solid -#if 0 // Maybe later... - if (!solid) - { - if (rw_ceilstat == 12 && line->sidedef->GetTexture(side_t::top) != 0) - { - rw_mustmarkceiling = true; - solid = true; - } - if (rw_floorstat == 3 && line->sidedef->GetTexture(side_t::bottom) != 0) - { - rw_mustmarkfloor = true; - solid = true; - } - } -#endif - } - - if (R_ClipWallSegment (WallC.sx1, WallC.sx2, solid)) - { - InSubsector->flags |= SSECF_DRAWN; - } -} - -// -// FWallCoords :: Init -// -// Transform and clip coordinates. Returns true if it was clipped away -// -bool FWallCoords::Init(const DVector2 &pt1, const DVector2 &pt2, double too_close) -{ - tleft.X = float(pt1.X * ViewSin - pt1.Y * ViewCos); - tright.X = float(pt2.X * ViewSin - pt2.Y * ViewCos); - - tleft.Y = float(pt1.X * ViewTanCos + pt1.Y * ViewTanSin); - tright.Y = float(pt2.X * ViewTanCos + pt2.Y * ViewTanSin); - - if (MirrorFlags & RF_XFLIP) - { - float t = -tleft.X; - tleft.X = -tright.X; - tright.X = t; - swapvalues(tleft.Y, tright.Y); - } - - if (tleft.X >= -tleft.Y) - { - if (tleft.X > tleft.Y) return true; // left edge is off the right side - if (tleft.Y == 0) return true; - sx1 = xs_RoundToInt(CenterX + tleft.X * CenterX / tleft.Y); - sz1 = tleft.Y; - } - else - { - if (tright.X < -tright.Y) return true; // wall is off the left side - float den = tleft.X - tright.X - tright.Y + tleft.Y; - if (den == 0) return true; - sx1 = 0; - sz1 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X + tleft.Y) / den; - } - - if (sz1 < too_close) - return true; - - if (tright.X <= tright.Y) - { - if (tright.X < -tright.Y) return true; // right edge is off the left side - if (tright.Y == 0) return true; - sx2 = xs_RoundToInt(CenterX + tright.X * CenterX / tright.Y); - sz2 = tright.Y; - } - else - { - if (tleft.X > tleft.Y) return true; // wall is off the right side - float den = tright.Y - tleft.Y - tright.X + tleft.X; - if (den == 0) return true; - sx2 = viewwidth; - sz2 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X - tleft.Y) / den; - } - - if (sz2 < too_close || sx2 <= sx1) - return true; - - return false; -} - -void FWallTmapVals::InitFromWallCoords(const FWallCoords *wallc) -{ - const FVector2 *left = &wallc->tleft; - const FVector2 *right = &wallc->tright; - - if (MirrorFlags & RF_XFLIP) - { - swapvalues(left, right); - } - UoverZorg = left->X * centerx; - UoverZstep = -left->Y; - InvZorg = (left->X - right->X) * centerx; - InvZstep = right->Y - left->Y; -} - -void FWallTmapVals::InitFromLine(const DVector2 &left, const DVector2 &right) -{ // Coordinates should have already had viewx,viewy subtracted - double fullx1 = left.X * ViewSin - left.Y * ViewCos; - double fullx2 = right.X * ViewSin - right.Y * ViewCos; - double fully1 = left.X * ViewTanCos + left.Y * ViewTanSin; - double fully2 = right.X * ViewTanCos + right.Y * ViewTanSin; - - if (MirrorFlags & RF_XFLIP) - { - fullx1 = -fullx1; - fullx2 = -fullx2; - } - - UoverZorg = float(fullx1 * centerx); - UoverZstep = float(-fully1); - InvZorg = float((fullx1 - fullx2) * centerx); - InvZstep = float(fully2 - fully1); -} - -// -// R_CheckBBox -// Checks BSP node/subtree bounding box. -// Returns true if some part of the bbox might be visible. -// -extern "C" const int checkcoord[12][4] = -{ - {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} -}; - - -static bool R_CheckBBox (float *bspcoord) // killough 1/28/98: static -{ - int boxx; - int boxy; - int boxpos; - - double x1, y1, x2, y2; - double rx1, ry1, rx2, ry2; - int sx1, sx2; - - cliprange_t* start; - - // Find the corners of the box - // that define the edges from current viewpoint. - if (ViewPos.X <= bspcoord[BOXLEFT]) - boxx = 0; - else if (ViewPos.X < bspcoord[BOXRIGHT]) - boxx = 1; - else - boxx = 2; - - if (ViewPos.Y >= bspcoord[BOXTOP]) - boxy = 0; - else if (ViewPos.Y > bspcoord[BOXBOTTOM]) - boxy = 1; - else - boxy = 2; - - boxpos = (boxy<<2)+boxx; - if (boxpos == 5) - return true; - - x1 = bspcoord[checkcoord[boxpos][0]] - ViewPos.X; - y1 = bspcoord[checkcoord[boxpos][1]] - ViewPos.Y; - x2 = bspcoord[checkcoord[boxpos][2]] - ViewPos.X; - y2 = bspcoord[checkcoord[boxpos][3]] - ViewPos.Y; - - // check clip list for an open space - - // Sitting on a line? - if (y1 * (x1 - x2) + x1 * (y2 - y1) >= -EQUAL_EPSILON) - return true; - - rx1 = x1 * ViewSin - y1 * ViewCos; - rx2 = x2 * ViewSin - y2 * ViewCos; - ry1 = x1 * ViewTanCos + y1 * ViewTanSin; - ry2 = x2 * ViewTanCos + y2 * ViewTanSin; - - if (MirrorFlags & RF_XFLIP) - { - double t = -rx1; - rx1 = -rx2; - rx2 = t; - swapvalues(ry1, ry2); - } - - if (rx1 >= -ry1) - { - if (rx1 > ry1) return false; // left edge is off the right side - if (ry1 == 0) return false; - sx1 = xs_RoundToInt(CenterX + rx1 * CenterX / ry1); - } - else - { - if (rx2 < -ry2) return false; // wall is off the left side - if (rx1 - rx2 - ry2 + ry1 == 0) return false; // wall does not intersect view volume - sx1 = 0; - } - - if (rx2 <= ry2) - { - if (rx2 < -ry2) return false; // right edge is off the left side - if (ry2 == 0) return false; - sx2 = xs_RoundToInt(CenterX + rx2 * CenterX / ry2); - } - else - { - if (rx1 > ry1) return false; // wall is off the right side - if (ry2 - ry1 - rx2 + rx1 == 0) return false; // wall does not intersect view volume - sx2 = viewwidth; - } - - // Find the first clippost that touches the source post - // (adjacent pixels are touching). - - // Does not cross a pixel. - if (sx2 <= sx1) - return false; - - start = solidsegs; - while (start->last < sx2) - start++; - - if (sx1 >= start->first && sx2 <= start->last) - { - // The clippost contains the new span. - return false; - } - - return true; -} - - -void R_Subsector (subsector_t *sub); -static void R_AddPolyobjs(subsector_t *sub) -{ - if (sub->BSP == NULL || sub->BSP->bDirty) - { - sub->BuildPolyBSP(); - } - if (sub->BSP->Nodes.Size() == 0) - { - R_Subsector(&sub->BSP->Subsectors[0]); - } - else - { - R_RenderBSPNode(&sub->BSP->Nodes.Last()); - } -} - -// kg3D - add fake segs, never rendered -void R_FakeDrawLoop(subsector_t *sub) -{ - int count; - seg_t* line; - - count = sub->numlines; - line = sub->firstline; - - while (count--) - { - if ((line->sidedef) && !(line->sidedef->Flags & WALLF_POLYOBJ)) - { - R_AddLine (line); - } - line++; - } -} - -// -// R_Subsector -// Determine floor/ceiling planes. -// Add sprites of things in sector. -// Draw one or more line segments. -// -void R_Subsector (subsector_t *sub) -{ - int count; - seg_t* line; - sector_t tempsec; // killough 3/7/98: deep water hack - int floorlightlevel; // killough 3/16/98: set floor lightlevel - int ceilinglightlevel; // killough 4/11/98 - bool outersubsector; - int fll, cll, position; - FSectorPortal *portal; - - // kg3D - fake floor stuff - visplane_t *backupfp; - visplane_t *backupcp; - //secplane_t templane; - lightlist_t *light; - - if (InSubsector != NULL) - { // InSubsector is not NULL. This means we are rendering from a mini-BSP. - outersubsector = false; - } - else - { - outersubsector = true; - InSubsector = sub; - } - -#ifdef RANGECHECK - if (outersubsector && sub - subsectors >= (ptrdiff_t)numsubsectors) - I_Error ("R_Subsector: ss %ti with numss = %i", sub - subsectors, numsubsectors); -#endif - - assert(sub->sector != NULL); - - if (sub->polys) - { // Render the polyobjs in the subsector first - R_AddPolyobjs(sub); - if (outersubsector) - { - InSubsector = NULL; - } - return; - } - - frontsector = sub->sector; - frontsector->MoreFlags |= SECF_DRAWN; - count = sub->numlines; - line = sub->firstline; - - // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect - frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, - &ceilinglightlevel, false); // killough 4/11/98 - - fll = floorlightlevel; - cll = ceilinglightlevel; - - // [RH] set foggy flag - foggy = level.fadeto || frontsector->ColorMap->Fade || (level.flags & LEVEL_HASFADETABLE); - r_actualextralight = foggy ? 0 : extralight << 4; - - // kg3D - fake lights - if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(frontsector, &frontsector->ceilingplane, false); - basecolormap = light->extra_colormap; - // If this is the real ceiling, don't discard plane lighting R_FakeFlat() - // accounted for. - if (light->p_lightlevel != &frontsector->lightlevel) - { - ceilinglightlevel = *light->p_lightlevel; - } - } - else - { - basecolormap = (r_fullbrightignoresectorcolor && fixedlightlev >= 0) ? &FullNormalLight : frontsector->ColorMap; - } - - portal = frontsector->ValidatePortal(sector_t::ceiling); - - ceilingplane = frontsector->ceilingplane.PointOnSide(ViewPos) > 0 || - frontsector->GetTexture(sector_t::ceiling) == skyflatnum || - portal != NULL || - (frontsector->heightsec && - !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && - frontsector->heightsec->GetTexture(sector_t::floor) == skyflatnum) ? - R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 - frontsector->GetTexture(sector_t::ceiling), - ceilinglightlevel + r_actualextralight, // killough 4/11/98 - frontsector->GetAlpha(sector_t::ceiling), - !!(frontsector->GetFlags(sector_t::ceiling) & PLANEF_ADDITIVE), - frontsector->planes[sector_t::ceiling].xform, - frontsector->sky, - portal - ) : NULL; - - if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(frontsector, &frontsector->floorplane, false); - basecolormap = light->extra_colormap; - // If this is the real floor, don't discard plane lighting R_FakeFlat() - // accounted for. - if (light->p_lightlevel != &frontsector->lightlevel) - { - floorlightlevel = *light->p_lightlevel; - } - } - else - { - basecolormap = (r_fullbrightignoresectorcolor && fixedlightlev >= 0) ? &FullNormalLight : frontsector->ColorMap; - } - - // killough 3/7/98: Add (x,y) offsets to flats, add deep water check - // killough 3/16/98: add floorlightlevel - // killough 10/98: add support for skies transferred from sidedefs - portal = frontsector->ValidatePortal(sector_t::floor); - - floorplane = frontsector->floorplane.PointOnSide(ViewPos) > 0 || // killough 3/7/98 - frontsector->GetTexture(sector_t::floor) == skyflatnum || - portal != NULL || - (frontsector->heightsec && - !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && - frontsector->heightsec->GetTexture(sector_t::ceiling) == skyflatnum) ? - R_FindPlane(frontsector->floorplane, - frontsector->GetTexture(sector_t::floor), - floorlightlevel + r_actualextralight, // killough 3/16/98 - frontsector->GetAlpha(sector_t::floor), - !!(frontsector->GetFlags(sector_t::floor) & PLANEF_ADDITIVE), - frontsector->planes[sector_t::floor].xform, - frontsector->sky, - portal - ) : NULL; - - // kg3D - fake planes rendering - if (r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size()) - { - backupfp = floorplane; - backupcp = ceilingplane; - // first check all floors - for (int i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++) - { - fakeFloor = frontsector->e->XFloor.ffloors[i]; - if (!(fakeFloor->flags & FF_EXISTS)) continue; - if (!fakeFloor->model) continue; - if (fakeFloor->bottom.plane->isSlope()) continue; - if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES|FF_RENDERSIDES))) - { - R_3D_AddHeight(fakeFloor->top.plane, frontsector); - } - if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; - if (fakeFloor->alpha == 0) continue; - if (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR) continue; - fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE); - if (fakeFloor->validcount != validcount) - { - fakeFloor->validcount = validcount; - R_3D_NewClip(); - } - double fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->centerspot); - if (fakeHeight < ViewPos.Z && - fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)) - { - fake3D = FAKE3D_FAKEFLOOR; - tempsec = *fakeFloor->model; - tempsec.floorplane = *fakeFloor->top.plane; - tempsec.ceilingplane = *fakeFloor->bottom.plane; - if (!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR)) - { - tempsec.SetTexture(sector_t::floor, tempsec.GetTexture(sector_t::ceiling)); - position = sector_t::ceiling; - } else position = sector_t::floor; - frontsector = &tempsec; - - if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(sub->sector, &frontsector->floorplane, false); - basecolormap = light->extra_colormap; - floorlightlevel = *light->p_lightlevel; - } - - ceilingplane = NULL; - floorplane = R_FindPlane(frontsector->floorplane, - frontsector->GetTexture(sector_t::floor), - floorlightlevel + r_actualextralight, // killough 3/16/98 - frontsector->GetAlpha(sector_t::floor), - !!(fakeFloor->flags & FF_ADDITIVETRANS), - frontsector->planes[position].xform, - frontsector->sky, - NULL); - - R_FakeDrawLoop(sub); - fake3D = 0; - frontsector = sub->sector; - } - } - // and now ceilings - for (unsigned int i = 0; i < frontsector->e->XFloor.ffloors.Size(); i++) - { - fakeFloor = frontsector->e->XFloor.ffloors[i]; - if (!(fakeFloor->flags & FF_EXISTS)) continue; - if (!fakeFloor->model) continue; - if (fakeFloor->top.plane->isSlope()) continue; - if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES|FF_RENDERSIDES))) - { - R_3D_AddHeight(fakeFloor->bottom.plane, frontsector); - } - if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; - if (fakeFloor->alpha == 0) continue; - if (!(fakeFloor->flags & FF_THISINSIDE) && (fakeFloor->flags & (FF_SWIMMABLE|FF_INVERTSECTOR)) == (FF_SWIMMABLE|FF_INVERTSECTOR)) continue; - fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE); - - if (fakeFloor->validcount != validcount) - { - fakeFloor->validcount = validcount; - R_3D_NewClip(); - } - double fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot); - if (fakeHeight > ViewPos.Z && - fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)) - { - fake3D = FAKE3D_FAKECEILING; - tempsec = *fakeFloor->model; - tempsec.floorplane = *fakeFloor->top.plane; - tempsec.ceilingplane = *fakeFloor->bottom.plane; - if ((!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR)) || - (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR)) - { - tempsec.SetTexture(sector_t::ceiling, tempsec.GetTexture(sector_t::floor)); - position = sector_t::floor; - } else position = sector_t::ceiling; - frontsector = &tempsec; - - tempsec.ceilingplane.ChangeHeight(-1 / 65536.); - if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(sub->sector, &frontsector->ceilingplane, false); - basecolormap = light->extra_colormap; - ceilinglightlevel = *light->p_lightlevel; - } - tempsec.ceilingplane.ChangeHeight(1 / 65536.); - - floorplane = NULL; - ceilingplane = R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 - frontsector->GetTexture(sector_t::ceiling), - ceilinglightlevel + r_actualextralight, // killough 4/11/98 - frontsector->GetAlpha(sector_t::ceiling), - !!(fakeFloor->flags & FF_ADDITIVETRANS), - frontsector->planes[position].xform, - frontsector->sky, - NULL); - - R_FakeDrawLoop(sub); - fake3D = 0; - frontsector = sub->sector; - } - } - fakeFloor = NULL; - floorplane = backupfp; - ceilingplane = backupcp; - } - - basecolormap = frontsector->ColorMap; - floorlightlevel = fll; - ceilinglightlevel = cll; - - // killough 9/18/98: Fix underwater slowdown, by passing real sector - // instead of fake one. Improve sprite lighting by basing sprite - // lightlevels on floor & ceiling lightlevels in the surrounding area. - // [RH] Handle sprite lighting like Duke 3D: If the ceiling is a sky, sprites are lit by - // it, otherwise they are lit by the floor. - R_AddSprites (sub->sector, frontsector->GetTexture(sector_t::ceiling) == skyflatnum ? - ceilinglightlevel : floorlightlevel, FakeSide); - - // [RH] Add particles - if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors) - { // Only do it for the main BSP. - int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight); - for (WORD i = ParticlesInSubsec[(unsigned int)(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) - { - R_ProjectParticle (Particles + i, subsectors[sub-subsectors].sector, shade, FakeSide); - } - } - - count = sub->numlines; - line = sub->firstline; - - while (count--) - { - if (!outersubsector || line->sidedef == NULL || !(line->sidedef->Flags & WALLF_POLYOBJ)) - { - // kg3D - fake planes bounding calculation - if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) - { - backupfp = floorplane; - backupcp = ceilingplane; - floorplane = NULL; - ceilingplane = NULL; - for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++) - { - fakeFloor = line->backsector->e->XFloor.ffloors[i]; - if (!(fakeFloor->flags & FF_EXISTS)) continue; - if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; - if (!fakeFloor->model) continue; - fake3D = FAKE3D_FAKEBACK; - tempsec = *fakeFloor->model; - tempsec.floorplane = *fakeFloor->top.plane; - tempsec.ceilingplane = *fakeFloor->bottom.plane; - backsector = &tempsec; - if (fakeFloor->validcount != validcount) - { - fakeFloor->validcount = validcount; - R_3D_NewClip(); - } - R_AddLine(line); // fake - } - fakeFloor = NULL; - fake3D = 0; - floorplane = backupfp; - ceilingplane = backupcp; - } - R_AddLine (line); // now real - } - line++; - } - if (outersubsector) - { - InSubsector = NULL; - } -} - -// -// RenderBSPNode -// Renders all subsectors below a given node, traversing subtree recursively. -// Just call with BSP root and -1. -// killough 5/2/98: reformatted, removed tail recursion - -void R_RenderBSPNode (void *node) -{ - if (numnodes == 0) - { - R_Subsector (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 (ViewPos, bsp); - - // Recursively divide front space (toward the viewer). - R_RenderBSPNode (bsp->children[side]); - - // Possibly divide back space (away from the viewer). - side ^= 1; - if (!R_CheckBBox (bsp->bbox[side])) - return; - - node = bsp->children[side]; - } - R_Subsector ((subsector_t *)((BYTE *)node - 1)); -} - -} diff --git a/src/r_bsp.h b/src/r_bsp.h deleted file mode 100644 index e4d70c4cf1..0000000000 --- a/src/r_bsp.h +++ /dev/null @@ -1,127 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Refresh module, BSP traversal and handling. -// -//----------------------------------------------------------------------------- - - -#ifndef __R_BSP__ -#define __R_BSP__ - -#include "tarray.h" -#include -#include "r_defs.h" - -EXTERN_CVAR (Bool, r_drawflat) // [RH] Don't texture segs? - -namespace swrenderer -{ - -// The 3072 below is just an arbitrary value picked to avoid -// drawing lines the player is too close to that would overflow -// the texture calculations. -#define TOO_CLOSE_Z (3072.0 / (1<<12)) - -struct FWallCoords -{ - FVector2 tleft; // coords at left of wall in view space rx1,ry1 - FVector2 tright; // coords at right of wall in view space rx2,ry2 - - float sz1, sz2; // depth at left, right of wall in screen space yb1,yb2 - short sx1, sx2; // x coords at left, right of wall in screen space xb1,xb2 - - bool Init(const DVector2 &pt1, const DVector2 &pt2, double too_close); -}; - -struct FWallTmapVals -{ - float UoverZorg, UoverZstep; - float InvZorg, InvZstep; - - void InitFromWallCoords(const FWallCoords *wallc); - void InitFromLine(const DVector2 &left, const DVector2 &right); -}; - -extern FWallCoords WallC; -extern FWallTmapVals WallT; - -enum -{ - FAKED_Center, - FAKED_BelowFloor, - FAKED_AboveCeiling -}; - -struct drawseg_t -{ - seg_t* curline; - float light, lightstep; - float iscale, iscalestep; - short x1, x2; // Same as sx1 and sx2, but clipped to the drawseg - short sx1, sx2; // left, right of parent seg on screen - float sz1, sz2; // z for left, right of parent seg on screen - float siz1, siz2; // 1/z for left, right of parent seg on screen - float cx, cy, cdx, cdy; - float yscale; - BYTE silhouette; // 0=none, 1=bottom, 2=top, 3=both - BYTE bFogBoundary; - BYTE bFakeBoundary; // for fake walls - int shade; -// Pointers to lists for sprite clipping, -// all three adjusted so [x1] is first value. - ptrdiff_t sprtopclip; // type short - ptrdiff_t sprbottomclip; // type short - ptrdiff_t maskedtexturecol; // type short - ptrdiff_t swall; // type float - int fake; // ident fake drawseg, don't draw and clip sprites -// backups - ptrdiff_t bkup; // sprtopclip backup, for mid and fake textures - FWallTmapVals tmapvals; - int CurrentPortalUniq; // [ZZ] to identify the portal that this drawseg is in. used for sprite clipping. -}; - - -extern seg_t* curline; -extern side_t* sidedef; -extern line_t* linedef; -extern sector_t* frontsector; -extern sector_t* backsector; - -extern drawseg_t *drawsegs; -extern drawseg_t *firstdrawseg; -extern drawseg_t* ds_p; - -extern TArray InterestingDrawsegs; // drawsegs that have something drawn on them -extern size_t FirstInterestingDrawseg; - -extern int WindowLeft, WindowRight; -extern WORD MirrorFlags; - -typedef void (*drawfunc_t) (int start, int stop); - -// BSP? -void R_ClearClipSegs (short left, short right); -void R_ClearDrawSegs (); -void R_RenderBSPNode (void *node); - -// killough 4/13/98: fake floors/ceilings for deep water / fake ceilings: -sector_t *R_FakeFlat(sector_t *, sector_t *, int *, int *, bool); - -} - -#endif diff --git a/src/r_data/colormaps.cpp b/src/r_data/colormaps.cpp index 2d3c0e49d6..d1dfc07e1f 100644 --- a/src/r_data/colormaps.cpp +++ b/src/r_data/colormaps.cpp @@ -54,13 +54,11 @@ #include "r_utility.h" #include "r_renderer.h" -static bool R_CheckForFixedLights(const BYTE *colormaps); +static bool R_CheckForFixedLights(const uint8_t *colormaps); -extern "C" { FDynamicColormap NormalLight; FDynamicColormap FullNormalLight; //[SP] Emulate GZDoom brightness -} bool NormalLightHasFixedLights; @@ -72,14 +70,14 @@ struct FakeCmap }; TArray fakecmaps; -BYTE *realcolormaps; -BYTE *realfbcolormaps; //[SP] For fullbright use +FSWColormap realcolormaps; +FSWColormap realfbcolormaps; //[SP] For fullbright use size_t numfakecmaps; TArray SpecialColormaps; -BYTE DesaturateColormap[31][256]; +uint8_t DesaturateColormap[31][256]; struct FSpecialColormapParameters { @@ -210,7 +208,7 @@ FDynamicColormap *GetSpecialLights (PalEntry color, PalEntry fade, int desaturat if (Renderer->UsesColormap()) { - colormap->Maps = new BYTE[NUMCOLORMAPS*256]; + colormap->Maps = new uint8_t[NUMCOLORMAPS*256]; colormap->BuildLights (); } else colormap->Maps = NULL; @@ -248,7 +246,7 @@ void FDynamicColormap::BuildLights () int l, c; int lr, lg, lb, ld, ild; PalEntry colors[256], basecolors[256]; - BYTE *shade; + uint8_t *shade; if (Maps == NULL) return; @@ -292,7 +290,7 @@ void FDynamicColormap::BuildLights () Fade.r, Fade.g, Fade.b, l * (256 / NUMCOLORMAPS)); shade = Maps + 256*l; - if ((DWORD)Color == MAKERGB(255,255,255)) + if ((uint32_t)Color == MAKERGB(255,255,255)) { // White light, so we can just pick the colors directly for (c = 0; c < 256; c++) { @@ -376,7 +374,7 @@ void FDynamicColormap::RebuildAllLights() { if (cm->Maps == NULL) { - cm->Maps = new BYTE[NUMCOLORMAPS*256]; + cm->Maps = new uint8_t[NUMCOLORMAPS*256]; cm->BuildLights (); } } @@ -394,9 +392,9 @@ void R_SetDefaultColormap (const char *name) if (strnicmp (fakecmaps[0].name, name, 8) != 0) { int lump, i, j; - BYTE map[256]; - BYTE unremap[256]; - BYTE remap[256]; + uint8_t map[256]; + uint8_t unremap[256]; + uint8_t remap[256]; lump = Wads.CheckNumForFullName (name, true, ns_colormaps); if (lump == -1) @@ -410,7 +408,7 @@ void R_SetDefaultColormap (const char *name) foo.Color = 0xFFFFFF; foo.Fade = 0; - foo.Maps = realcolormaps; + foo.Maps = realcolormaps.Maps; foo.Desaturate = 0; foo.Next = NULL; foo.BuildLights (); @@ -432,7 +430,7 @@ void R_SetDefaultColormap (const char *name) remap[0] = 0; for (i = 0; i < NUMCOLORMAPS; ++i) { - BYTE *map2 = &realcolormaps[i*256]; + uint8_t *map2 = &realcolormaps.Maps[i*256]; lumpr.Read (map, 256); for (j = 0; j < 256; ++j) { @@ -456,15 +454,11 @@ void R_DeinitColormaps () { SpecialColormaps.Clear(); fakecmaps.Clear(); - if (realcolormaps != NULL) + delete[] realcolormaps.Maps; + if (realfbcolormaps.Maps) { - delete[] realcolormaps; - realcolormaps = NULL; - } - if (realfbcolormaps != NULL) - { - delete[] realfbcolormaps; - realfbcolormaps = NULL; + delete[] realfbcolormaps.Maps; + realfbcolormaps.Maps = nullptr; } FreeSpecialLights(); } @@ -489,9 +483,9 @@ void R_InitColormaps () cm.blend = 0; fakecmaps.Push(cm); - DWORD NumLumps = Wads.GetNumLumps(); + uint32_t NumLumps = Wads.GetNumLumps(); - for (DWORD i = 0; i < NumLumps; i++) + for (uint32_t i = 0; i < NumLumps; i++) { if (Wads.GetLumpNamespace(i) == ns_colormaps) { @@ -508,12 +502,12 @@ void R_InitColormaps () } } } - realcolormaps = new BYTE[256*NUMCOLORMAPS*fakecmaps.Size()]; + realcolormaps.Maps = new uint8_t[256*NUMCOLORMAPS*fakecmaps.Size()]; R_SetDefaultColormap ("COLORMAP"); if (fakecmaps.Size() > 1) { - BYTE unremap[256], remap[256], mapin[256]; + uint8_t unremap[256], remap[256], mapin[256]; int i; unsigned j; @@ -530,11 +524,11 @@ void R_InitColormaps () { int k, r, g, b; FWadLump lump = Wads.OpenLumpNum (fakecmaps[j].lump); - BYTE *const map = realcolormaps + NUMCOLORMAPS*256*j; + uint8_t *const map = realcolormaps.Maps + NUMCOLORMAPS*256*j; for (k = 0; k < NUMCOLORMAPS; ++k) { - BYTE *map2 = &map[k*256]; + uint8_t *map2 = &map[k*256]; lump.Read (mapin, 256); map2[0] = 0; for (r = 1; r < 256; ++r) @@ -557,19 +551,19 @@ void R_InitColormaps () } // [SP] Create a copy of the colormap - if (!realfbcolormaps) + if (!realfbcolormaps.Maps) { - realfbcolormaps = new BYTE[256*NUMCOLORMAPS*fakecmaps.Size()]; - memcpy(realfbcolormaps, realcolormaps, 256*NUMCOLORMAPS*fakecmaps.Size()); + realfbcolormaps.Maps = new uint8_t[256*NUMCOLORMAPS*fakecmaps.Size()]; + memcpy(realfbcolormaps.Maps, realcolormaps.Maps, 256*NUMCOLORMAPS*fakecmaps.Size()); } NormalLight.Color = PalEntry (255, 255, 255); NormalLight.Fade = 0; - NormalLight.Maps = realcolormaps; + NormalLight.Maps = realcolormaps.Maps; FullNormalLight.Color = PalEntry (255, 255, 255); FullNormalLight.Fade = 0; - FullNormalLight.Maps = realfbcolormaps; - NormalLightHasFixedLights = R_CheckForFixedLights(realcolormaps); + FullNormalLight.Maps = realfbcolormaps.Maps; + NormalLightHasFixedLights = R_CheckForFixedLights(realcolormaps.Maps); numfakecmaps = fakecmaps.Size(); // build default special maps (e.g. invulnerability) @@ -583,7 +577,7 @@ void R_InitColormaps () // desaturated colormaps. These are used for texture composition for(int m = 0; m < 31; m++) { - BYTE *shade = DesaturateColormap[m]; + uint8_t *shade = DesaturateColormap[m]; for (int c = 0; c < 256; c++) { int intensity = (GPalette.BaseColors[c].r * 77 + @@ -607,10 +601,10 @@ void R_InitColormaps () // //========================================================================== -static bool R_CheckForFixedLights(const BYTE *colormaps) +static bool R_CheckForFixedLights(const uint8_t *colormaps) { - const BYTE *lastcolormap = colormaps + (NUMCOLORMAPS - 1) * 256; - BYTE freq[256]; + const uint8_t *lastcolormap = colormaps + (NUMCOLORMAPS - 1) * 256; + uint8_t freq[256]; int i, j; // Count the frequencies of different colors in the final colormap. @@ -627,7 +621,7 @@ static bool R_CheckForFixedLights(const BYTE *colormaps) // final coloramp. for (i = 255; i >= 0; --i) { - BYTE color = lastcolormap[i]; + uint8_t color = lastcolormap[i]; if (freq[color] > 10) // arbitrary number to decide "common" colors { continue; @@ -654,7 +648,7 @@ static bool R_CheckForFixedLights(const BYTE *colormaps) // //========================================================================== -DWORD R_ColormapNumForName (const char *name) +uint32_t R_ColormapNumForName (const char *name) { if (strnicmp (name, "COLORMAP", 8)) { // COLORMAP always returns 0 @@ -678,8 +672,8 @@ DWORD R_ColormapNumForName (const char *name) // //========================================================================== -DWORD R_BlendForColormap (DWORD map) +uint32_t R_BlendForColormap (uint32_t map) { return APART(map) ? map : - map < fakecmaps.Size() ? DWORD(fakecmaps[map].blend) : 0; + map < fakecmaps.Size() ? uint32_t(fakecmaps[map].blend) : 0; } \ No newline at end of file diff --git a/src/r_data/colormaps.h b/src/r_data/colormaps.h index 03f705b2ba..942c0a961a 100644 --- a/src/r_data/colormaps.h +++ b/src/r_data/colormaps.h @@ -1,18 +1,26 @@ #ifndef __RES_CMAP_H #define __RES_CMAP_H +struct FSWColormap; + void R_InitColormaps (); void R_DeinitColormaps (); -DWORD R_ColormapNumForName(const char *name); // killough 4/4/98 +uint32_t R_ColormapNumForName(const char *name); // killough 4/4/98 void R_SetDefaultColormap (const char *name); // [RH] change normal fadetable -DWORD R_BlendForColormap (DWORD map); // [RH] return calculated blend for a colormap -extern BYTE *realcolormaps; // [RH] make the colormaps externally visible +uint32_t R_BlendForColormap (uint32_t map); // [RH] return calculated blend for a colormap +extern FSWColormap realcolormaps; // [RH] make the colormaps externally visible extern size_t numfakecmaps; +struct FSWColormap +{ + uint8_t *Maps = nullptr; + PalEntry Color = 0xffffffff; + PalEntry Fade = 0xff000000; + int Desaturate = 0; +}; - -struct FDynamicColormap +struct FDynamicColormap : FSWColormap { void ChangeFade (PalEntry fadecolor); void ChangeColor (PalEntry lightcolor, int desaturate); @@ -21,10 +29,6 @@ struct FDynamicColormap void BuildLights (); static void RebuildAllLights(); - BYTE *Maps; - PalEntry Color; - PalEntry Fade; - int Desaturate; FDynamicColormap *Next; }; @@ -44,11 +48,16 @@ enum }; -struct FSpecialColormap +struct FSpecialColormap : FSWColormap { + FSpecialColormap() + { + Maps = Colormap; + } + float ColorizeStart[3]; float ColorizeEnd[3]; - BYTE Colormap[256]; + uint8_t Colormap[256]; PalEntry GrayscaleToColor[256]; }; @@ -57,7 +66,7 @@ extern TArray SpecialColormaps; // some utility functions to store special colormaps in powerup blends #define SPECIALCOLORMAP_MASK 0x00b60000 -inline uint32 MakeSpecialColormap(int index) +inline uint32_t MakeSpecialColormap(int index) { assert(index >= 0 && index < 65536); return index | SPECIALCOLORMAP_MASK; @@ -67,12 +76,9 @@ int AddSpecialColormap(float r1, float g1, float b1, float r2, float g2, float b -extern BYTE DesaturateColormap[31][256]; -extern "C" -{ +extern uint8_t DesaturateColormap[31][256]; extern FDynamicColormap NormalLight; extern FDynamicColormap FullNormalLight; -} extern bool NormalLightHasFixedLights; FDynamicColormap *GetSpecialLights (PalEntry lightcolor, PalEntry fadecolor, int desaturate); diff --git a/src/r_data/r_translate.h b/src/r_data/r_translate.h index bd13550cc2..f3c2c55c36 100644 --- a/src/r_data/r_translate.h +++ b/src/r_data/r_translate.h @@ -81,15 +81,15 @@ extern TAutoGrowArray translationtables[NUM_TRANS #define TRANSLATION_MASK ((1<> TRANSLATION_SHIFT; } -inline int GetTranslationIndex(DWORD trans) +inline int GetTranslationIndex(uint32_t trans) { return (trans&TRANSLATION_MASK); } diff --git a/src/r_data/renderstyle.cpp b/src/r_data/renderstyle.cpp index c44b136a09..49628c881a 100644 --- a/src/r_data/renderstyle.cpp +++ b/src/r_data/renderstyle.cpp @@ -35,6 +35,7 @@ #include "templates.h" #include "renderstyle.h" #include "c_cvars.h" +#include "serializer.h" CVAR (Bool, r_drawtrans, true, 0) CVAR (Int, r_drawfuzz, 1, CVAR_ARCHIVE) @@ -191,3 +192,8 @@ void FRenderStyle::CheckFuzz() BlendOp = STYLEOP_Fuzz; } } + +FSerializer &Serialize(FSerializer &arc, const char *key, FRenderStyle &style, FRenderStyle *def) +{ + return arc.Array(key, &style.BlendOp, def ? &def->BlendOp : nullptr, 4); +} diff --git a/src/r_data/renderstyle.h b/src/r_data/renderstyle.h index 947f9afeda..ebc1bd53fb 100644 --- a/src/r_data/renderstyle.h +++ b/src/r_data/renderstyle.h @@ -1,6 +1,4 @@ -#ifndef __R_BLEND_H -#define __R_BLEND_H - +#pragma once /* ** r_blend.h ** Constants and types for specifying texture blending. @@ -121,6 +119,9 @@ enum ERenderFlags // Actors only: Ignore sector fade and fade to black. To fade to white, // combine this with STYLEF_InvertOverlay. STYLEF_FadeToBlack = 64, + + // Force alpha. + STYLEF_ForceAlpha = 128, }; union FRenderStyle @@ -159,4 +160,3 @@ inline FRenderStyle &FRenderStyle::operator= (ERenderStyle legacy) return *this; } -#endif diff --git a/src/r_data/sprites.cpp b/src/r_data/sprites.cpp index d4c0465125..d932e6377b 100644 --- a/src/r_data/sprites.cpp +++ b/src/r_data/sprites.cpp @@ -21,7 +21,7 @@ void gl_InitModels(); // and range check thing_t sprites patches TArray sprites; TArray SpriteFrames; -DWORD NumStdSprites; // The first x sprites that don't belong to skins. +uint32_t NumStdSprites; // The first x sprites that don't belong to skins. struct spriteframewithrotate : public spriteframe_t { @@ -262,7 +262,7 @@ void R_InitSpriteDefs () char Frame; } *vhashes; unsigned int i, j, smax, vmax; - DWORD intname; + uint32_t intname; spriteframewithrotate sprtemp[MAX_SPRITE_FRAMES]; @@ -510,7 +510,7 @@ void R_InitSkins (void) spritedef_t temp; int sndlumps[NUMSKINSOUNDS]; char key[65]; - DWORD intname, crouchname; + uint32_t intname, crouchname; unsigned i; int j, k, base; int lastlump; @@ -574,13 +574,13 @@ void R_InitSkins (void) { for (j = 3; j >= 0; j--) sc.String[j] = toupper (sc.String[j]); - intname = *((DWORD *)sc.String); + intname = *((uint32_t *)sc.String); } else if (0 == stricmp (key, "crouchsprite")) { for (j = 3; j >= 0; j--) sc.String[j] = toupper (sc.String[j]); - crouchname = *((DWORD *)sc.String); + crouchname = *((uint32_t *)sc.String); } else if (0 == stricmp (key, "face")) { @@ -787,7 +787,7 @@ void R_InitSkins (void) for (k = base + 1; Wads.GetLumpNamespace(k) == basens; k++) { char lname[9]; - DWORD lnameint; + uint32_t lnameint; Wads.GetLumpName (lname, k); memcpy(&lnameint, lname, 4); if (lnameint == intname) diff --git a/src/r_data/voxels.cpp b/src/r_data/voxels.cpp index d81220ea8f..3c6f8b1f9a 100644 --- a/src/r_data/voxels.cpp +++ b/src/r_data/voxels.cpp @@ -62,6 +62,7 @@ #include "r_data/colormaps.h" #include "r_data/sprites.h" #include "voxels.h" +#include "info.h" void VOX_AddVoxel(int sprnum, int frame, FVoxelDef *def); @@ -91,10 +92,10 @@ struct VoxelOptions // //========================================================================== -static BYTE *GetVoxelRemap(const BYTE *pal) +static uint8_t *GetVoxelRemap(const uint8_t *pal) { - static BYTE remap[256]; - static BYTE oldpal[768]; + static uint8_t remap[256]; + static uint8_t oldpal[768]; static bool firsttime = true; if (firsttime || memcmp(oldpal, pal, 768) != 0) @@ -105,7 +106,7 @@ static BYTE *GetVoxelRemap(const BYTE *pal) { // The voxel palette uses VGA colors, so we have to expand it // from 6 to 8 bits per component. - remap[i] = BestColor((uint32 *)GPalette.BaseColors, + remap[i] = BestColor((uint32_t *)GPalette.BaseColors, (oldpal[i*3 + 0] << 2) | (oldpal[i*3 + 0] >> 4), (oldpal[i*3 + 1] << 2) | (oldpal[i*3 + 1] >> 4), (oldpal[i*3 + 2] << 2) | (oldpal[i*3 + 2] >> 4)); @@ -142,8 +143,8 @@ static bool CopyVoxelSlabs(kvxslab_t *dest, const kvxslab_t *src, int size) dest->col[j] = src->col[j]; } slabzleng += 3; - src = (kvxslab_t *)((BYTE *)src + slabzleng); - dest = (kvxslab_t *)((BYTE *)dest + slabzleng); + src = (kvxslab_t *)((uint8_t *)src + slabzleng); + dest = (kvxslab_t *)((uint8_t *)dest + slabzleng); size -= slabzleng; } return true; @@ -157,7 +158,7 @@ static bool CopyVoxelSlabs(kvxslab_t *dest, const kvxslab_t *src, int size) // //========================================================================== -static void RemapVoxelSlabs(kvxslab_t *dest, int size, const BYTE *remap) +static void RemapVoxelSlabs(kvxslab_t *dest, int size, const uint8_t *remap) { while (size >= 3) { @@ -168,7 +169,7 @@ static void RemapVoxelSlabs(kvxslab_t *dest, int size, const BYTE *remap) dest->col[j] = remap[dest->col[j]]; } slabzleng += 3; - dest = (kvxslab_t *)((BYTE *)dest + slabzleng); + dest = (kvxslab_t *)((uint8_t *)dest + slabzleng); size -= slabzleng; } } @@ -183,12 +184,12 @@ FVoxel *R_LoadKVX(int lumpnum) { const kvxslab_t *slabs[MAXVOXMIPS]; FVoxel *voxel = new FVoxel; - const BYTE *rawmip; + const uint8_t *rawmip; int mip, maxmipsize; int i, j, n; FMemLump lump = Wads.ReadLump(lumpnum); // FMemLump adds an extra 0 byte to the end. - BYTE *rawvoxel = (BYTE *)lump.GetMem(); + uint8_t *rawvoxel = (uint8_t *)lump.GetMem(); int voxelsize = (int)(lump.GetSize()-1); // Oh, KVX, why couldn't you have a proper header? We'll just go through @@ -229,7 +230,7 @@ FVoxel *R_LoadKVX(int lumpnum) // Allocate slab data space. mipl->OffsetX = new int[(numbytes - 24 + 3) / 4]; mipl->OffsetXY = (short *)(mipl->OffsetX + mipl->SizeX + 1); - mipl->SlabData = (BYTE *)(mipl->OffsetXY + mipl->SizeX * (mipl->SizeY + 1)); + mipl->SlabData = (uint8_t *)(mipl->OffsetXY + mipl->SizeX * (mipl->SizeY + 1)); // Load x offsets. for (i = 0, n = mipl->SizeX; i <= n; ++i) @@ -313,7 +314,7 @@ FVoxel *R_LoadKVX(int lumpnum) } voxel->LumpNum = lumpnum; - voxel->Palette = new BYTE[768]; + voxel->Palette = new uint8_t[768]; memcpy(voxel->Palette, rawvoxel + voxelsize - 768, 768); return voxel; @@ -392,6 +393,54 @@ FVoxel::~FVoxel() if (Palette != NULL) delete [] Palette; } +//========================================================================== +// +// Create true color version of the slab data +// +//========================================================================== + +void FVoxel::CreateBgraSlabData() +{ + uint8_t *palette = Palette; + if (palette == nullptr) + palette = (uint8_t *)GPalette.BaseColors; + + for (int i = 0; i < NumMips; ++i) + { + int size = Mips[i].OffsetX[Mips[i].SizeX]; + if (size <= 0) continue; + + Mips[i].SlabDataBgra.Resize(size); + + kvxslab_t *src = (kvxslab_t*)Mips[i].SlabData; + kvxslab_bgra_t *dest = (kvxslab_bgra_t*)&Mips[i].SlabDataBgra[0]; + + while (size >= 3) + { + dest->backfacecull = src->backfacecull; + dest->ztop = src->ztop; + dest->zleng = src->zleng; + + int slabzleng = src->zleng; + for (int j = 0; j < slabzleng; ++j) + { + int colorIndex = src->col[j]; + + uint32_t red = (palette[colorIndex * 3 + 0] << 2) | (palette[colorIndex * 3 + 0] >> 4); + uint32_t green = (palette[colorIndex * 3 + 1] << 2) | (palette[colorIndex * 3 + 1] >> 4); + uint32_t blue = (palette[colorIndex * 3 + 2] << 2) | (palette[colorIndex * 3 + 2] >> 4); + + dest->col[j] = 0xff000000 | (red << 16) | (green << 8) | blue; + } + slabzleng += 3; + + dest = (kvxslab_bgra_t *)((uint32_t *)dest + slabzleng); + src = (kvxslab_t *)((uint8_t *)src + slabzleng); + size -= slabzleng; + } + } +} + //========================================================================== // // Remap the voxel to the game palette @@ -402,7 +451,7 @@ void FVoxel::Remap() { if (Palette != NULL) { - BYTE *remap = GetVoxelRemap(Palette); + uint8_t *remap = GetVoxelRemap(Palette); for (int i = 0; i < NumMips; ++i) { RemapVoxelSlabs((kvxslab_t *)Mips[i].SlabData, Mips[i].OffsetX[Mips[i].SizeX], remap); @@ -436,7 +485,7 @@ void FVoxel::RemovePalette() // //========================================================================== -static bool VOX_ReadSpriteNames(FScanner &sc, TArray &vsprites) +static bool VOX_ReadSpriteNames(FScanner &sc, TArray &vsprites) { vsprites.Clear(); while (sc.GetString()) @@ -598,7 +647,7 @@ void R_InitVoxels() while ((lump = Wads.FindLump("VOXELDEF", &lastlump)) != -1) { FScanner sc(lump); - TArray vsprites; + TArray vsprites; while (VOX_ReadSpriteNames(sc, vsprites)) { diff --git a/src/r_data/voxels.h b/src/r_data/voxels.h index 85095c52f5..57ae41a1f6 100644 --- a/src/r_data/voxels.h +++ b/src/r_data/voxels.h @@ -9,10 +9,18 @@ struct kvxslab_t { - BYTE ztop; // starting z coordinate of top of slab - BYTE zleng; // # of bytes in the color array - slab height - BYTE backfacecull; // low 6 bits tell which of 6 faces are exposed - BYTE col[1/*zleng*/];// color data from top to bottom + uint8_t ztop; // starting z coordinate of top of slab + uint8_t zleng; // # of bytes in the color array - slab height + uint8_t backfacecull; // low 6 bits tell which of 6 faces are exposed + uint8_t col[1/*zleng*/];// color data from top to bottom +}; + +struct kvxslab_bgra_t +{ + uint32_t ztop; // starting z coordinate of top of slab + uint32_t zleng; // # of bytes in the color array - slab height + uint32_t backfacecull; // low 6 bits tell which of 6 faces are exposed + uint32_t col[1/*zleng*/];// color data from top to bottom }; struct FVoxelMipLevel @@ -26,7 +34,8 @@ struct FVoxelMipLevel DVector3 Pivot; int *OffsetX; short *OffsetXY; - BYTE *SlabData; + uint8_t *SlabData; + TArray SlabDataBgra; }; struct FVoxel @@ -34,11 +43,12 @@ struct FVoxel int LumpNum; int NumMips; int VoxelIndex; // Needed by GZDoom - BYTE *Palette; + uint8_t *Palette; FVoxelMipLevel Mips[MAXVOXMIPS]; FVoxel(); ~FVoxel(); + void CreateBgraSlabData(); void Remap(); void RemovePalette(); }; diff --git a/src/r_defs.h b/src/r_defs.h index ff7d3a8603..c69450e01d 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -25,8 +25,8 @@ #include "doomdef.h" #include "templates.h" -#include "memarena.h" #include "m_bbox.h" +#include "dobjgc.h" // Some more or less basic data types // we depend on. @@ -35,13 +35,15 @@ // We rely on the thinker data struct // to handle sound origins in sectors. // SECTORS do store MObjs anyway. -#include "actor.h" struct FLightNode; struct FGLSection; +class FSerializer; struct FPortal; +struct FSectorPortal; +struct FLinePortal; struct seg_t; - -#include "dthinker.h" +struct sector_t; +class AActor; #define MAXWIDTH 5760 #define MAXHEIGHT 3600 @@ -59,7 +61,6 @@ enum SIL_BOTH }; -namespace swrenderer { extern size_t MaxDrawSegs; } struct FDisplacement; // @@ -352,11 +353,6 @@ public: return (D + normal.X*v->fX() + normal.Y*v->fY()) * negiC; } - double ZatPoint(const AActor *ac) const - { - return (D + normal.X*ac->X() + normal.Y*ac->Y()) * negiC; - } - // Returns the value of z at vertex v if d is equal to dist double ZatPointDist(const vertex_t *v, double dist) { @@ -435,6 +431,7 @@ public: } bool CopyPlaneIfValid (secplane_t *dest, const secplane_t *opp) const; + inline double ZatPoint(const AActor *ac) const; }; @@ -876,31 +873,11 @@ public: Flags &= ~SECF_SPECIALFLAGS; } - bool PortalBlocksView(int plane) - { - if (GetPortalType(plane) != PORTS_LINKEDPORTAL) return false; - return !!(planes[plane].Flags & (PLANEF_NORENDER | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); - } - - bool PortalBlocksSight(int plane) - { - return PLANEF_LINKED != (planes[plane].Flags & (PLANEF_NORENDER | PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED | PLANEF_LINKED)); - } - - bool PortalBlocksMovement(int plane) - { - return PLANEF_LINKED != (planes[plane].Flags & (PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED | PLANEF_LINKED)); - } - - bool PortalBlocksSound(int plane) - { - return PLANEF_LINKED != (planes[plane].Flags & (PLANEF_BLOCKSOUND | PLANEF_DISABLED | PLANEF_OBSTRUCTED | PLANEF_LINKED)); - } - - bool PortalIsLinked(int plane) - { - return (GetPortalType(plane) == PORTS_LINKEDPORTAL); - } + inline bool PortalBlocksView(int plane); + inline bool PortalBlocksSight(int plane); + inline bool PortalBlocksMovement(int plane); + inline bool PortalBlocksSound(int plane); + inline bool PortalIsLinked(int plane); void ClearPortal(int plane) { @@ -937,16 +914,8 @@ public: double HighestCeilingAt(const DVector2 &a, sector_t **resultsec = NULL); double LowestFloorAt(const DVector2 &a, sector_t **resultsec = NULL); - - double HighestCeilingAt(AActor *a, sector_t **resultsec = NULL) - { - return HighestCeilingAt(a->Pos(), resultsec); - } - - double LowestFloorAt(AActor *a, sector_t **resultsec = NULL) - { - return LowestFloorAt(a->Pos(), resultsec); - } + inline double HighestCeilingAt(AActor *a, sector_t **resultsec = NULL); + inline double LowestFloorAt(AActor *a, sector_t **resultsec = NULL); bool isClosed() const { @@ -1284,32 +1253,11 @@ struct line_t FSectorPortal *GetTransferredPortal(); - FLinePortal *getPortal() const - { - return portalindex >= linePortals.Size() ? (FLinePortal*)NULL : &linePortals[portalindex]; - } - - // returns true if the portal is crossable by actors - bool isLinePortal() const - { - return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_PASSABLE); - } - - // returns true if the portal needs to be handled by the renderer - bool isVisualPortal() const - { - return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_VISIBLE); - } - - line_t *getPortalDestination() const - { - return portalindex >= linePortals.Size() ? (line_t*)NULL : linePortals[portalindex].mDestination; - } - - int getPortalAlignment() const - { - return portalindex >= linePortals.Size() ? 0 : linePortals[portalindex].mAlign; - } + inline FLinePortal *getPortal() const; + inline bool isLinePortal() const; + inline bool isVisualPortal() const; + inline line_t *getPortalDestination() const; + inline int getPortalAlignment() const; int Index() const; }; @@ -1477,15 +1425,6 @@ struct FMiniBSP typedef uint8_t lighttable_t; // This could be wider for >8 bit display. -// This encapsulates the fields of vissprite_t that can be altered by AlterWeaponSprite -struct visstyle_t -{ - bool Invert; - float Alpha; - ERenderStyle RenderStyle; -}; - - //---------------------------------------------------------------------------------- // // The playsim can use different nodes than the renderer so this is @@ -1504,40 +1443,6 @@ inline sector_t *P_PointInSector(double X, double Y) return P_PointInSubsector(X, Y)->sector; } -inline DVector3 AActor::PosRelative(int portalgroup) const -{ - return Pos() + Displacements.getOffset(Sector->PortalGroup, portalgroup); -} - -inline DVector3 AActor::PosRelative(const AActor *other) const -{ - return Pos() + Displacements.getOffset(Sector->PortalGroup, other->Sector->PortalGroup); -} - -inline DVector3 AActor::PosRelative(sector_t *sec) const -{ - return Pos() + Displacements.getOffset(Sector->PortalGroup, sec->PortalGroup); -} - -inline DVector3 AActor::PosRelative(line_t *line) const -{ - return Pos() + Displacements.getOffset(Sector->PortalGroup, line->frontsector->PortalGroup); -} - -inline DVector3 PosRelative(const DVector3 &pos, line_t *line, sector_t *refsec = NULL) -{ - return pos + Displacements.getOffset(refsec->PortalGroup, line->frontsector->PortalGroup); -} - - -inline void AActor::ClearInterpolation() -{ - Prev = Pos(); - PrevAngles = Angles; - if (Sector) PrevPortalGroup = Sector->PortalGroup; - else PrevPortalGroup = 0; -} - inline bool FBoundingBox::inRange(const line_t *ld) const { return Left() < ld->bbox[BOXRIGHT] && diff --git a/src/r_draw.cpp b/src/r_draw.cpp deleted file mode 100644 index c4449f73bb..0000000000 --- a/src/r_draw.cpp +++ /dev/null @@ -1,1298 +0,0 @@ -/* -** r_draw.cpp -** -**--------------------------------------------------------------------------- -** Copyright 1998-2016 Randy Heit -** Copyright 2016 Magnus Norddahl -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include - -#include "templates.h" -#include "doomdef.h" -#include "i_system.h" -#include "w_wad.h" -#include "r_local.h" -#include "v_video.h" -#include "doomstat.h" -#include "st_stuff.h" -#include "g_game.h" -#include "g_level.h" -#include "r_data/r_translate.h" -#include "v_palette.h" -#include "r_data/colormaps.h" -#include "r_plane.h" -#include "r_draw.h" -#include "r_draw_pal.h" -#include "r_thread.h" - -namespace swrenderer -{ - // Needed by R_DrawFogBoundary (which probably shouldn't be part of this file) - extern "C" short spanend[MAXHEIGHT]; - extern float rw_light; - extern float rw_lightstep; - extern int wallshade; - - double dc_texturemid; - - int ylookup[MAXHEIGHT]; - uint8_t shadetables[NUMCOLORMAPS * 16 * 256]; - FDynamicColormap ShadeFakeColormap[16]; - uint8_t identitymap[256]; - FDynamicColormap identitycolormap; - int fuzzoffset[FUZZTABLE + 1]; - int fuzzpos; - int fuzzviewheight; - - namespace drawerargs - { - int dc_pitch; - lighttable_t *dc_colormap; - int dc_x; - int dc_yl; - int dc_yh; - fixed_t dc_iscale; - fixed_t dc_texturefrac; - uint32_t dc_textureheight; - int dc_color; - uint32_t dc_srccolor; - uint32_t dc_srccolor_bgra; - uint32_t *dc_srcblend; - uint32_t *dc_destblend; - fixed_t dc_srcalpha; - fixed_t dc_destalpha; - const uint8_t *dc_source; - const uint8_t *dc_source2; - uint32_t dc_texturefracx; - uint8_t *dc_translation; - uint8_t *dc_dest; - uint8_t *dc_destorg; - int dc_destheight; - int dc_count; - uint32_t dc_wall_texturefrac[4]; - uint32_t dc_wall_iscale[4]; - uint8_t *dc_wall_colormap[4]; - fixed_t dc_wall_light[4]; - const uint8_t *dc_wall_source[4]; - const uint8_t *dc_wall_source2[4]; - uint32_t dc_wall_texturefracx[4]; - uint32_t dc_wall_sourceheight[4]; - int dc_wall_fracbits; - int ds_y; - int ds_x1; - int ds_x2; - lighttable_t * ds_colormap; - dsfixed_t ds_light; - dsfixed_t ds_xfrac; - dsfixed_t ds_yfrac; - dsfixed_t ds_xstep; - dsfixed_t ds_ystep; - int ds_xbits; - int ds_ybits; - fixed_t ds_alpha; - double ds_lod; - const uint8_t *ds_source; - int ds_color; - unsigned int dc_tspans[4][MAXHEIGHT]; - unsigned int *dc_ctspan[4]; - unsigned int *horizspan[4]; - } - - void R_InitColumnDrawers() - { - colfunc = basecolfunc = R_DrawColumn; - fuzzcolfunc = R_DrawFuzzColumn; - transcolfunc = R_DrawTranslatedColumn; - spanfunc = R_DrawSpan; - hcolfunc_pre = R_DrawColumnHoriz; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; - } - - void R_InitShadeMaps() - { - int i, j; - // set up shading tables for shaded columns - // 16 colormap sets, progressing from full alpha to minimum visible alpha - - uint8_t *table = shadetables; - - // Full alpha - for (i = 0; i < 16; ++i) - { - ShadeFakeColormap[i].Color = ~0u; - ShadeFakeColormap[i].Desaturate = ~0u; - ShadeFakeColormap[i].Next = NULL; - ShadeFakeColormap[i].Maps = table; - - for (j = 0; j < NUMCOLORMAPS; ++j) - { - int a = (NUMCOLORMAPS - j) * 256 / NUMCOLORMAPS * (16 - i); - for (int k = 0; k < 256; ++k) - { - uint8_t v = (((k + 2) * a) + 256) >> 14; - table[k] = MIN(v, 64); - } - table += 256; - } - } - for (i = 0; i < NUMCOLORMAPS * 16 * 256; ++i) - { - assert(shadetables[i] <= 64); - } - - // Set up a guaranteed identity map - for (i = 0; i < 256; ++i) - { - identitymap[i] = i; - } - identitycolormap.Maps = identitymap; - } - - void R_InitFuzzTable(int fuzzoff) - { - /* - FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, - FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, - FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, - FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, - FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, - FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, - FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF - */ - - static const int8_t fuzzinit[FUZZTABLE] = { - 1,-1, 1,-1, 1, 1,-1, - 1, 1,-1, 1, 1, 1,-1, - 1, 1, 1,-1,-1,-1,-1, - 1,-1,-1, 1, 1, 1, 1,-1, - 1,-1, 1, 1,-1,-1, 1, - 1,-1,-1,-1,-1, 1, 1, - 1, 1,-1, 1, 1,-1, 1 - }; - - for (int i = 0; i < FUZZTABLE; i++) - { - fuzzoffset[i] = fuzzinit[i] * fuzzoff; - } - } - - namespace - { - bool R_SetBlendFunc(int op, fixed_t fglevel, fixed_t bglevel, int flags) - { - using namespace drawerargs; - - // r_drawtrans is a seriously bad thing to turn off. I wonder if I should - // just remove it completely. - if (!r_drawtrans || (op == STYLEOP_Add && fglevel == FRACUNIT && bglevel == 0 && !(flags & STYLEF_InvertSource))) - { - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillColumn; - hcolfunc_post1 = rt_copy1col; - hcolfunc_post4 = rt_copy4cols; - } - else if (dc_translation == NULL) - { - colfunc = basecolfunc; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; - } - else - { - colfunc = transcolfunc; - hcolfunc_post1 = rt_tlate1col; - hcolfunc_post4 = rt_tlate4cols; - } - return true; - } - if (flags & STYLEF_InvertSource) - { - dc_srcblend = Col2RGB8_Inverse[fglevel >> 10]; - dc_destblend = Col2RGB8_LessPrecision[bglevel >> 10]; - dc_srcalpha = fglevel; - dc_destalpha = bglevel; - } - else if (op == STYLEOP_Add && fglevel + bglevel <= FRACUNIT) - { - dc_srcblend = Col2RGB8[fglevel >> 10]; - dc_destblend = Col2RGB8[bglevel >> 10]; - dc_srcalpha = fglevel; - dc_destalpha = bglevel; - } - else - { - dc_srcblend = Col2RGB8_LessPrecision[fglevel >> 10]; - dc_destblend = Col2RGB8_LessPrecision[bglevel >> 10]; - dc_srcalpha = fglevel; - dc_destalpha = bglevel; - } - switch (op) - { - case STYLEOP_Add: - if (fglevel == 0 && bglevel == FRACUNIT) - { - return false; - } - if (fglevel + bglevel <= FRACUNIT) - { // Colors won't overflow when added - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillAddColumn; - hcolfunc_post1 = rt_add1col; - hcolfunc_post4 = rt_add4cols; - } - else if (dc_translation == NULL) - { - colfunc = R_DrawAddColumn; - hcolfunc_post1 = rt_add1col; - hcolfunc_post4 = rt_add4cols; - } - else - { - colfunc = R_DrawTlatedAddColumn; - hcolfunc_post1 = rt_tlateadd1col; - hcolfunc_post4 = rt_tlateadd4cols; - } - } - else - { // Colors might overflow when added - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillAddClampColumn; - hcolfunc_post1 = rt_addclamp1col; - hcolfunc_post4 = rt_addclamp4cols; - } - else if (dc_translation == NULL) - { - colfunc = R_DrawAddClampColumn; - hcolfunc_post1 = rt_addclamp1col; - hcolfunc_post4 = rt_addclamp4cols; - } - else - { - colfunc = R_DrawAddClampTranslatedColumn; - hcolfunc_post1 = rt_tlateaddclamp1col; - hcolfunc_post4 = rt_tlateaddclamp4cols; - } - } - return true; - - case STYLEOP_Sub: - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillSubClampColumn; - hcolfunc_post1 = rt_subclamp1col; - hcolfunc_post4 = rt_subclamp4cols; - } - else if (dc_translation == NULL) - { - colfunc = R_DrawSubClampColumn; - hcolfunc_post1 = rt_subclamp1col; - hcolfunc_post4 = rt_subclamp4cols; - } - else - { - colfunc = R_DrawSubClampTranslatedColumn; - hcolfunc_post1 = rt_tlatesubclamp1col; - hcolfunc_post4 = rt_tlatesubclamp4cols; - } - return true; - - case STYLEOP_RevSub: - if (fglevel == 0 && bglevel == FRACUNIT) - { - return false; - } - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillRevSubClampColumn; - hcolfunc_post1 = rt_subclamp1col; - hcolfunc_post4 = rt_subclamp4cols; - } - else if (dc_translation == NULL) - { - colfunc = R_DrawRevSubClampColumn; - hcolfunc_post1 = rt_revsubclamp1col; - hcolfunc_post4 = rt_revsubclamp4cols; - } - else - { - colfunc = R_DrawRevSubClampTranslatedColumn; - hcolfunc_post1 = rt_tlaterevsubclamp1col; - hcolfunc_post4 = rt_tlaterevsubclamp4cols; - } - return true; - - default: - return false; - } - } - - fixed_t GetAlpha(int type, fixed_t alpha) - { - switch (type) - { - case STYLEALPHA_Zero: return 0; - case STYLEALPHA_One: return OPAQUE; - case STYLEALPHA_Src: return alpha; - case STYLEALPHA_InvSrc: return OPAQUE - alpha; - default: return 0; - } - } - - FDynamicColormap *basecolormapsave; - } - - ESPSResult R_SetPatchStyle(FRenderStyle style, fixed_t alpha, int translation, uint32_t color) - { - using namespace drawerargs; - - fixed_t fglevel, bglevel; - - style.CheckFuzz(); - - if (style.BlendOp == STYLEOP_Shadow) - { - style = LegacyRenderStyles[STYLE_TranslucentStencil]; - alpha = OPAQUE / 3; - color = 0; - } - - if (style.Flags & STYLEF_TransSoulsAlpha) - { - alpha = fixed_t(transsouls * OPAQUE); - } - else if (style.Flags & STYLEF_Alpha1) - { - alpha = OPAQUE; - } - else - { - alpha = clamp(alpha, 0, OPAQUE); - } - - if (translation != -1) - { - dc_translation = NULL; - if (translation != 0) - { - FRemapTable *table = TranslationToTable(translation); - if (table != NULL && !table->Inactive) - { - dc_translation = table->Remap; - } - } - } - basecolormapsave = basecolormap; - hcolfunc_pre = R_DrawColumnHoriz; - - // Check for special modes - if (style.BlendOp == STYLEOP_Fuzz) - { - colfunc = fuzzcolfunc; - return DoDraw0; - } - else if (style == LegacyRenderStyles[STYLE_Shaded]) - { - // Shaded drawer only gets 16 levels of alpha because it saves memory. - if ((alpha >>= 12) == 0) - return DontDraw; - colfunc = R_DrawShadedColumn; - hcolfunc_post1 = rt_shaded1col; - hcolfunc_post4 = rt_shaded4cols; - dc_color = fixedcolormap ? fixedcolormap[APART(color)] : basecolormap->Maps[APART(color)]; - dc_colormap = (basecolormap = &ShadeFakeColormap[16 - alpha])->Maps; - if (fixedlightlev >= 0 && fixedcolormap == NULL) - { - dc_colormap += fixedlightlev; - } - return DoDraw0; - } - - fglevel = GetAlpha(style.SrcAlpha, alpha); - bglevel = GetAlpha(style.DestAlpha, alpha); - - if (style.Flags & STYLEF_ColorIsFixed) - { - uint32_t x = fglevel >> 10; - uint32_t r = RPART(color); - uint32_t g = GPART(color); - uint32_t b = BPART(color); - // dc_color is used by the rt_* routines. It is indexed into dc_srcblend. - dc_color = RGB32k.RGB[r >> 3][g >> 3][b >> 3]; - if (style.Flags & STYLEF_InvertSource) - { - r = 255 - r; - g = 255 - g; - b = 255 - b; - } - uint32_t alpha = clamp(fglevel >> (FRACBITS - 8), 0, 255); - dc_srccolor_bgra = (alpha << 24) | (r << 16) | (g << 8) | b; - // dc_srccolor is used by the R_Fill* routines. It is premultiplied - // with the alpha. - dc_srccolor = ((((r*x) >> 4) << 20) | ((g*x) >> 4) | ((((b)*x) >> 4) << 10)) & 0x3feffbff; - hcolfunc_pre = R_FillColumnHoriz; - R_SetColorMapLight(identitycolormap.Maps, 0, 0); - } - - if (!R_SetBlendFunc(style.BlendOp, fglevel, bglevel, style.Flags)) - { - return DontDraw; - } - return DoDraw0; - } - - ESPSResult R_SetPatchStyle(FRenderStyle style, float alpha, int translation, uint32_t color) - { - return R_SetPatchStyle(style, FLOAT2FIXED(alpha), translation, color); - } - - void R_FinishSetPatchStyle() - { - basecolormap = basecolormapsave; - } - - const uint8_t *R_GetColumn(FTexture *tex, int col) - { - int width; - - // If the texture's width isn't a power of 2, then we need to make it a - // positive offset for proper clamping. - if (col < 0 && (width = tex->GetWidth()) != (1 << tex->WidthBits)) - { - col = width + (col % width); - } - - return tex->GetColumn(col, nullptr); - } - - bool R_GetTransMaskDrawers(void(**drawCol1)(), void(**drawCol4)()) - { - if (colfunc == R_DrawAddColumn) - { - *drawCol1 = R_DrawWallAddCol1; - *drawCol4 = R_DrawWallAddCol4; - return true; - } - if (colfunc == R_DrawAddClampColumn) - { - *drawCol1 = R_DrawWallAddClampCol1; - *drawCol4 = R_DrawWallAddClampCol4; - return true; - } - if (colfunc == R_DrawSubClampColumn) - { - *drawCol1 = R_DrawWallSubClampCol1; - *drawCol4 = R_DrawWallSubClampCol4; - return true; - } - if (colfunc == R_DrawRevSubClampColumn) - { - *drawCol1 = R_DrawWallRevSubClampCol1; - *drawCol4 = R_DrawWallRevSubClampCol4; - return true; - } - return false; - } - - void R_SetColorMapLight(lighttable_t *base_colormap, float light, int shade) - { - using namespace drawerargs; - - dc_colormap = base_colormap + (GETPALOOKUP(light, shade) << COLORMAPSHIFT); - } - - void R_SetColorMapLight(FDynamicColormap *base_colormap, float light, int shade) - { - R_SetColorMapLight(base_colormap->Maps, light, shade); - } - - void R_SetDSColorMapLight(lighttable_t *base_colormap, float light, int shade) - { - using namespace drawerargs; - - ds_colormap = base_colormap + (GETPALOOKUP(light, shade) << COLORMAPSHIFT); - } - - void R_SetDSColorMapLight(FDynamicColormap *base_colormap, float light, int shade) - { - R_SetDSColorMapLight(base_colormap->Maps, light, shade); - } - - void R_SetTranslationMap(lighttable_t *translation) - { - using namespace drawerargs; - - dc_colormap = translation; - } - - void rt_initcols(uint8_t *buffer) - { - using namespace drawerargs; - - for (int y = 3; y >= 0; y--) - horizspan[y] = dc_ctspan[y] = &dc_tspans[y][0]; - - DrawerCommandQueue::QueueCommand(buffer); - } - - void rt_span_coverage(int x, int start, int stop) - { - using namespace drawerargs; - - unsigned int **tspan = &dc_ctspan[x & 3]; - (*tspan)[0] = start; - (*tspan)[1] = stop; - *tspan += 2; - } - - void rt_flip_posts() - { - using namespace drawerargs; - - unsigned int *front = horizspan[dc_x & 3]; - unsigned int *back = dc_ctspan[dc_x & 3] - 2; - - while (front < back) - { - swapvalues(front[0], back[0]); - swapvalues(front[1], back[1]); - front += 2; - back -= 2; - } - } - - void rt_draw4cols(int sx) - { - using namespace drawerargs; - - int x, bad; - unsigned int maxtop, minbot, minnexttop; - - // Place a dummy "span" in each column. These don't get - // drawn. They're just here to avoid special cases in the - // max/min calculations below. - for (x = 0; x < 4; ++x) - { - dc_ctspan[x][0] = screen->GetHeight()+1; - dc_ctspan[x][1] = screen->GetHeight(); - } - - for (;;) - { - // If a column is out of spans, mark it as such - bad = 0; - minnexttop = 0xffffffff; - for (x = 0; x < 4; ++x) - { - if (horizspan[x] >= dc_ctspan[x]) - { - bad |= 1 << x; - } - else if ((horizspan[x]+2)[0] < minnexttop) - { - minnexttop = (horizspan[x]+2)[0]; - } - } - // Once all columns are out of spans, we're done - if (bad == 15) - { - return; - } - - // Find the largest shared area for the spans in each column - maxtop = MAX (MAX (horizspan[0][0], horizspan[1][0]), - MAX (horizspan[2][0], horizspan[3][0])); - minbot = MIN (MIN (horizspan[0][1], horizspan[1][1]), - MIN (horizspan[2][1], horizspan[3][1])); - - // If there is no shared area with these spans, draw each span - // individually and advance to the next spans until we reach a shared area. - // However, only draw spans down to the highest span in the next set of - // spans. If we allow the entire height of a span to be drawn, it could - // prevent any more shared areas from being drawn in these four columns. - // - // Example: Suppose we have the following arrangement: - // A CD - // A CD - // B D - // B D - // aB D - // aBcD - // aBcD - // aBc - // - // If we draw the entire height of the spans, we end up drawing this first: - // A CD - // A CD - // B D - // B D - // B D - // B D - // B D - // B D - // B - // - // This leaves only the "a" and "c" columns to be drawn, and they are not - // part of a shared area, but if we can include B and D with them, we can - // get a shared area. So we cut off everything in the first set just - // above the "a" column and end up drawing this first: - // A CD - // A CD - // B D - // B D - // - // Then the next time through, we have the following arrangement with an - // easily shared area to draw: - // aB D - // aBcD - // aBcD - // aBc - if (bad != 0 || maxtop > minbot) - { - int drawcount = 0; - for (x = 0; x < 4; ++x) - { - if (!(bad & 1)) - { - if (horizspan[x][1] < minnexttop) - { - hcolfunc_post1 (x, sx+x, horizspan[x][0], horizspan[x][1]); - horizspan[x] += 2; - drawcount++; - } - else if (minnexttop > horizspan[x][0]) - { - hcolfunc_post1 (x, sx+x, horizspan[x][0], minnexttop-1); - horizspan[x][0] = minnexttop; - drawcount++; - } - } - bad >>= 1; - } - // Drawcount *should* always be non-zero. The reality is that some situations - // can make this not true. Unfortunately, I'm not sure what those situations are. - if (drawcount == 0) - { - return; - } - continue; - } - - // Draw any span fragments above the shared area. - for (x = 0; x < 4; ++x) - { - if (maxtop > horizspan[x][0]) - { - hcolfunc_post1 (x, sx+x, horizspan[x][0], maxtop-1); - } - } - - // Draw the shared area. - hcolfunc_post4 (sx, maxtop, minbot); - - // For each column, if part of the span is past the shared area, - // set its top to just below the shared area. Otherwise, advance - // to the next span in that column. - for (x = 0; x < 4; ++x) - { - if (minbot < horizspan[x][1]) - { - horizspan[x][0] = minbot+1; - } - else - { - horizspan[x] += 2; - } - } - } - } - - void R_SetupSpanBits(FTexture *tex) - { - using namespace drawerargs; - - tex->GetWidth(); - ds_xbits = tex->WidthBits; - ds_ybits = tex->HeightBits; - if ((1 << ds_xbits) > tex->GetWidth()) - { - ds_xbits--; - } - if ((1 << ds_ybits) > tex->GetHeight()) - { - ds_ybits--; - } - } - - void R_SetSpanColormap(lighttable_t *colormap) - { - using namespace drawerargs; - - ds_colormap = colormap; - } - - void R_SetSpanSource(FTexture *tex) - { - using namespace drawerargs; - - ds_source = tex->GetPixels(); - } - - ///////////////////////////////////////////////////////////////////////// - - void R_FillColumnHoriz() - { - using namespace drawerargs; - - if (dc_count <= 0) - return; - - int x = dc_x & 3; - unsigned int **span = &dc_ctspan[x]; - (*span)[0] = dc_yl; - (*span)[1] = dc_yh; - *span += 2; - - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawColumnHoriz() - { - using namespace drawerargs; - - if (dc_count <= 0) - return; - - int x = dc_x & 3; - unsigned int **span = &dc_ctspan[x]; - (*span)[0] = dc_yl; - (*span)[1] = dc_yh; - *span += 2; - - DrawerCommandQueue::QueueCommand(); - } - - // Copies one span at hx to the screen at sx. - void rt_copy1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Copies all four spans to the screen starting at sx. - void rt_copy4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Maps one span at hx to the screen at sx. - void rt_map1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Maps all four spans to the screen starting at sx. - void rt_map4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates one span at hx to the screen at sx. - void rt_tlate1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_map1col(hx, sx, yl, yh); - } - - // Translates all four spans to the screen starting at sx. - void rt_tlate4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_map4cols(sx, yl, yh); - } - - // Adds one span at hx to the screen at sx without clamping. - void rt_add1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Adds all four spans to the screen starting at sx without clamping. - void rt_add4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates and adds one span at hx to the screen at sx without clamping. - void rt_tlateadd1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_add1col(hx, sx, yl, yh); - } - - // Translates and adds all four spans to the screen starting at sx without clamping. - void rt_tlateadd4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_add4cols(sx, yl, yh); - } - - // Shades one span at hx to the screen at sx. - void rt_shaded1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Shades all four spans to the screen starting at sx. - void rt_shaded4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Adds one span at hx to the screen at sx with clamping. - void rt_addclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Adds all four spans to the screen starting at sx with clamping. - void rt_addclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates and adds one span at hx to the screen at sx with clamping. - void rt_tlateaddclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_addclamp1col(hx, sx, yl, yh); - } - - // Translates and adds all four spans to the screen starting at sx with clamping. - void rt_tlateaddclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_addclamp4cols(sx, yl, yh); - } - - // Subtracts one span at hx to the screen at sx with clamping. - void rt_subclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Subtracts all four spans to the screen starting at sx with clamping. - void rt_subclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates and subtracts one span at hx to the screen at sx with clamping. - void rt_tlatesubclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_subclamp1col(hx, sx, yl, yh); - } - - // Translates and subtracts all four spans to the screen starting at sx with clamping. - void rt_tlatesubclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_subclamp4cols(sx, yl, yh); - } - - // Subtracts one span at hx from the screen at sx with clamping. - void rt_revsubclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Subtracts all four spans from the screen starting at sx with clamping. - void rt_revsubclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates and subtracts one span at hx from the screen at sx with clamping. - void rt_tlaterevsubclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_revsubclamp1col(hx, sx, yl, yh); - } - - // Translates and subtracts all four spans from the screen starting at sx with clamping. - void rt_tlaterevsubclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_revsubclamp4cols(sx, yl, yh); - } - - void R_DrawWallCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallMaskedCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallMaskedCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallAddCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallAddCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallAddClampCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallAddClampCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallSubClampCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallSubClampCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallRevSubClampCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallRevSubClampCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSingleSkyCol1(uint32_t solid_top, uint32_t solid_bottom) - { - DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); - } - - void R_DrawSingleSkyCol4(uint32_t solid_top, uint32_t solid_bottom) - { - DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); - } - - void R_DrawDoubleSkyCol1(uint32_t solid_top, uint32_t solid_bottom) - { - DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); - } - - void R_DrawDoubleSkyCol4(uint32_t solid_top, uint32_t solid_bottom) - { - DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); - } - - void R_DrawColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillAddColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillAddClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillSubClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillRevSubClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawFuzzColumn() - { - using namespace drawerargs; - - DrawerCommandQueue::QueueCommand(); - - dc_yl = MAX(dc_yl, 1); - dc_yh = MIN(dc_yh, fuzzviewheight); - if (dc_yl <= dc_yh) - fuzzpos = (fuzzpos + dc_yh - dc_yl + 1) % FUZZTABLE; - } - - void R_DrawAddColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawTranslatedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawTlatedAddColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawShadedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawAddClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawAddClampTranslatedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSubClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSubClampTranslatedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawRevSubClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawRevSubClampTranslatedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpan() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanMasked() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanTranslucent() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanMaskedTranslucent() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanAddClamp() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanMaskedAddClamp() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillSpan() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawTiltedSpan(int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy) - { - DrawerCommandQueue::QueueCommand(y, x1, x2, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy); - } - - void R_DrawColoredSpan(int y, int x1, int x2) - { - DrawerCommandQueue::QueueCommand(y, x1, x2); - } - - namespace - { - const uint8_t *slab_colormap; - } - - void R_SetupDrawSlab(uint8_t *colormap) - { - slab_colormap = colormap; - } - - void R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const uint8_t *vptr, uint8_t *p) - { - DrawerCommandQueue::QueueCommand(dx, v, dy, vi, vptr, p, slab_colormap); - } - - void R_DrawFogBoundarySection(int y, int y2, int x1) - { - for (; y < y2; ++y) - { - int x2 = spanend[y]; - DrawerCommandQueue::QueueCommand(y, x1, x2); - } - } - - void R_DrawFogBoundary(int x1, int x2, short *uclip, short *dclip) - { - // This is essentially the same as R_MapVisPlane but with an extra step - // to create new horizontal spans whenever the light changes enough that - // we need to use a new colormap. - - double lightstep = rw_lightstep; - double light = rw_light + rw_lightstep*(x2 - x1 - 1); - int x = x2 - 1; - int t2 = uclip[x]; - int b2 = dclip[x]; - int rcolormap = GETPALOOKUP(light, wallshade); - int lcolormap; - uint8_t *basecolormapdata = basecolormap->Maps; - - if (b2 > t2) - { - fillshort(spanend + t2, b2 - t2, x); - } - - R_SetColorMapLight(basecolormap->Maps, (float)light, wallshade); - - uint8_t *fake_dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); - - for (--x; x >= x1; --x) - { - int t1 = uclip[x]; - int b1 = dclip[x]; - const int xr = x + 1; - int stop; - - light -= rw_lightstep; - lcolormap = GETPALOOKUP(light, wallshade); - if (lcolormap != rcolormap) - { - if (t2 < b2 && rcolormap != 0) - { // Colormap 0 is always the identity map, so rendering it is - // just a waste of time. - R_DrawFogBoundarySection(t2, b2, xr); - } - if (t1 < t2) t2 = t1; - if (b1 > b2) b2 = b1; - if (t2 < b2) - { - fillshort(spanend + t2, b2 - t2, x); - } - rcolormap = lcolormap; - R_SetColorMapLight(basecolormap->Maps, (float)light, wallshade); - fake_dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); - } - else - { - if (fake_dc_colormap != basecolormapdata) - { - stop = MIN(t1, b2); - while (t2 < stop) - { - int y = t2++; - DrawerCommandQueue::QueueCommand(y, xr, spanend[y]); - } - stop = MAX(b1, t2); - while (b2 > stop) - { - int y = --b2; - DrawerCommandQueue::QueueCommand(y, xr, spanend[y]); - } - } - else - { - t2 = MAX(t2, MIN(t1, b2)); - b2 = MIN(b2, MAX(b1, t2)); - } - - stop = MIN(t2, b1); - while (t1 < stop) - { - spanend[t1++] = x; - } - stop = MAX(b2, t2); - while (b1 > stop) - { - spanend[--b1] = x; - } - } - - t2 = uclip[x]; - b2 = dclip[x]; - } - if (t2 < b2 && rcolormap != 0) - { - R_DrawFogBoundarySection(t2, b2, x1); - } - } - - void R_DrawParticle(vissprite_t *sprite) - { - R_DrawParticle_C(sprite); - } -} diff --git a/src/r_draw.h b/src/r_draw.h deleted file mode 100644 index c245034d7d..0000000000 --- a/src/r_draw.h +++ /dev/null @@ -1,197 +0,0 @@ - -#pragma once - -#include "r_defs.h" - -EXTERN_CVAR(Bool, r_multithreaded); -EXTERN_CVAR(Int, r_drawfuzz); -EXTERN_CVAR(Bool, r_drawtrans); -EXTERN_CVAR(Float, transsouls); -EXTERN_CVAR(Int, r_columnmethod); - -namespace swrenderer -{ - struct vissprite_t; - - extern double dc_texturemid; - - namespace drawerargs - { - extern int dc_pitch; - extern lighttable_t *dc_colormap; - extern int dc_x; - extern int dc_yl; - extern int dc_yh; - extern fixed_t dc_iscale; - extern fixed_t dc_texturefrac; - extern uint32_t dc_textureheight; - extern int dc_color; - extern uint32_t dc_srccolor; - extern uint32_t dc_srccolor_bgra; - extern uint32_t *dc_srcblend; - extern uint32_t *dc_destblend; - extern fixed_t dc_srcalpha; - extern fixed_t dc_destalpha; - extern const uint8_t *dc_source; - extern const uint8_t *dc_source2; - extern uint32_t dc_texturefracx; - extern uint8_t *dc_translation; - extern uint8_t *dc_dest; - extern uint8_t *dc_destorg; - extern int dc_destheight; - extern int dc_count; - - extern uint32_t dc_wall_texturefrac[4]; - extern uint32_t dc_wall_iscale[4]; - extern uint8_t *dc_wall_colormap[4]; - extern fixed_t dc_wall_light[4]; - extern const uint8_t *dc_wall_source[4]; - extern const uint8_t *dc_wall_source2[4]; - extern uint32_t dc_wall_texturefracx[4]; - extern uint32_t dc_wall_sourceheight[4]; - extern int dc_wall_fracbits; - - extern int ds_y; - extern int ds_x1; - extern int ds_x2; - extern lighttable_t * ds_colormap; - extern dsfixed_t ds_light; - extern dsfixed_t ds_xfrac; - extern dsfixed_t ds_yfrac; - extern dsfixed_t ds_xstep; - extern dsfixed_t ds_ystep; - extern int ds_xbits; - extern int ds_ybits; - extern fixed_t ds_alpha; - extern double ds_lod; - extern const uint8_t *ds_source; - extern int ds_color; - - extern unsigned int dc_tspans[4][MAXHEIGHT]; - extern unsigned int *dc_ctspan[4]; - extern unsigned int *horizspan[4]; - } - - extern int ylookup[MAXHEIGHT]; - extern uint8_t shadetables[/*NUMCOLORMAPS*16*256*/]; - extern FDynamicColormap ShadeFakeColormap[16]; - extern uint8_t identitymap[256]; - extern FDynamicColormap identitycolormap; - - // Spectre/Invisibility. - #define FUZZTABLE 50 - extern int fuzzoffset[FUZZTABLE + 1]; - extern int fuzzpos; - extern int fuzzviewheight; - - void R_InitColumnDrawers(); - void R_InitShadeMaps(); - void R_InitFuzzTable(int fuzzoff); - - enum ESPSResult - { - DontDraw, // not useful to draw this - DoDraw0, // draw this as if r_columnmethod is 0 - DoDraw1, // draw this as if r_columnmethod is 1 - }; - - ESPSResult R_SetPatchStyle(FRenderStyle style, fixed_t alpha, int translation, uint32_t color); - ESPSResult R_SetPatchStyle(FRenderStyle style, float alpha, int translation, uint32_t color); - void R_FinishSetPatchStyle(); // Call this after finished drawing the current thing, in case its style was STYLE_Shade - bool R_GetTransMaskDrawers(void(**drawCol1)(), void(**drawCol4)()); - - const uint8_t *R_GetColumn(FTexture *tex, int col); - - void rt_initcols(uint8_t *buffer = nullptr); - void rt_span_coverage(int x, int start, int stop); - void rt_draw4cols(int sx); - void rt_flip_posts(); - void rt_copy1col(int hx, int sx, int yl, int yh); - void rt_copy4cols(int sx, int yl, int yh); - void rt_shaded1col(int hx, int sx, int yl, int yh); - void rt_shaded4cols(int sx, int yl, int yh); - void rt_map1col(int hx, int sx, int yl, int yh); - void rt_add1col(int hx, int sx, int yl, int yh); - void rt_addclamp1col(int hx, int sx, int yl, int yh); - void rt_subclamp1col(int hx, int sx, int yl, int yh); - void rt_revsubclamp1col(int hx, int sx, int yl, int yh); - void rt_tlate1col(int hx, int sx, int yl, int yh); - void rt_tlateadd1col(int hx, int sx, int yl, int yh); - void rt_tlateaddclamp1col(int hx, int sx, int yl, int yh); - void rt_tlatesubclamp1col(int hx, int sx, int yl, int yh); - void rt_tlaterevsubclamp1col(int hx, int sx, int yl, int yh); - void rt_map4cols(int sx, int yl, int yh); - void rt_add4cols(int sx, int yl, int yh); - void rt_addclamp4cols(int sx, int yl, int yh); - void rt_subclamp4cols(int sx, int yl, int yh); - void rt_revsubclamp4cols(int sx, int yl, int yh); - void rt_tlate4cols(int sx, int yl, int yh); - void rt_tlateadd4cols(int sx, int yl, int yh); - void rt_tlateaddclamp4cols(int sx, int yl, int yh); - void rt_tlatesubclamp4cols(int sx, int yl, int yh); - void rt_tlaterevsubclamp4cols(int sx, int yl, int yh); - void R_DrawColumnHoriz(); - void R_DrawColumn(); - void R_DrawFuzzColumn(); - void R_DrawTranslatedColumn(); - void R_DrawShadedColumn(); - void R_FillColumn(); - void R_FillAddColumn(); - void R_FillAddClampColumn(); - void R_FillSubClampColumn(); - void R_FillRevSubClampColumn(); - void R_DrawAddColumn(); - void R_DrawTlatedAddColumn(); - void R_DrawAddClampColumn(); - void R_DrawAddClampTranslatedColumn(); - void R_DrawSubClampColumn(); - void R_DrawSubClampTranslatedColumn(); - void R_DrawRevSubClampColumn(); - void R_DrawRevSubClampTranslatedColumn(); - void R_DrawSpan(); - void R_DrawSpanMasked(); - void R_DrawSpanTranslucent(); - void R_DrawSpanMaskedTranslucent(); - void R_DrawSpanAddClamp(); - void R_DrawSpanMaskedAddClamp(); - void R_FillSpan(); - void R_DrawTiltedSpan(int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy); - void R_DrawColoredSpan(int y, int x1, int x2); - void R_SetupDrawSlab(uint8_t *colormap); - void R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const uint8_t *vptr, uint8_t *p); - void R_DrawFogBoundary(int x1, int x2, short *uclip, short *dclip); - void R_FillColumnHoriz(); - void R_FillSpan(); - - void R_DrawWallCol1(); - void R_DrawWallCol4(); - void R_DrawWallMaskedCol1(); - void R_DrawWallMaskedCol4(); - void R_DrawWallAddCol1(); - void R_DrawWallAddCol4(); - void R_DrawWallAddClampCol1(); - void R_DrawWallAddClampCol4(); - void R_DrawWallSubClampCol1(); - void R_DrawWallSubClampCol4(); - void R_DrawWallRevSubClampCol1(); - void R_DrawWallRevSubClampCol4(); - - void R_DrawSingleSkyCol1(uint32_t solid_top, uint32_t solid_bottom); - void R_DrawSingleSkyCol4(uint32_t solid_top, uint32_t solid_bottom); - void R_DrawDoubleSkyCol1(uint32_t solid_top, uint32_t solid_bottom); - void R_DrawDoubleSkyCol4(uint32_t solid_top, uint32_t solid_bottom); - - void R_SetColorMapLight(lighttable_t *base_colormap, float light, int shade); - void R_SetColorMapLight(FDynamicColormap *base_colormap, float light, int shade); - void R_SetDSColorMapLight(lighttable_t *base_colormap, float light, int shade); - void R_SetDSColorMapLight(FDynamicColormap *base_colormap, float light, int shade); - void R_SetTranslationMap(lighttable_t *translation); - - void R_SetupSpanBits(FTexture *tex); - void R_SetSpanColormap(lighttable_t *colormap); - void R_SetSpanSource(FTexture *tex); - - void R_MapTiltedPlane(int y, int x1); - void R_MapColoredPlane(int y, int x1); - void R_DrawParticle(vissprite_t *); -} diff --git a/src/r_draw_pal.cpp b/src/r_draw_pal.cpp deleted file mode 100644 index 2cdeba3140..0000000000 --- a/src/r_draw_pal.cpp +++ /dev/null @@ -1,2622 +0,0 @@ -/* -** r_draw_pal.cpp -** -**--------------------------------------------------------------------------- -** Copyright 1998-2016 Randy Heit -** Copyright 2016 Magnus Norddahl -** 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 "templates.h" -#include "doomtype.h" -#include "doomdef.h" -#include "r_defs.h" -#include "r_draw.h" -#include "r_main.h" -#include "r_things.h" -#include "v_video.h" -#include "r_draw_pal.h" - -/* - [RH] This translucency algorithm is based on DOSDoom 0.65's, but uses - a 32k RGB table instead of an 8k one. At least on my machine, it's - slightly faster (probably because it uses only one shift instead of - two), and it looks considerably less green at the ends of the - translucency range. The extra size doesn't appear to be an issue. - - The following note is from DOSDoom 0.65: - - New translucency algorithm, by Erik Sandberg: - - Basically, we compute the red, green and blue values for each pixel, and - then use a RGB table to check which one of the palette colours that best - represents those RGB values. The RGB table is 8k big, with 4 R-bits, - 5 G-bits and 4 B-bits. A 4k table gives a bit too bad precision, and a 32k - table takes up more memory and results in more cache misses, so an 8k - table seemed to be quite ultimate. - - The computation of the RGB for each pixel is accelerated by using two - 1k tables for each translucency level. - The xth element of one of these tables contains the r, g and b values for - the colour x, weighted for the current translucency level (for example, - the weighted rgb values for background colour at 75% translucency are 1/4 - of the original rgb values). The rgb values are stored as three - low-precision fixed point values, packed into one long per colour: - Bit 0-4: Frac part of blue (5 bits) - Bit 5-8: Int part of blue (4 bits) - Bit 9-13: Frac part of red (5 bits) - Bit 14-17: Int part of red (4 bits) - Bit 18-22: Frac part of green (5 bits) - Bit 23-27: Int part of green (5 bits) - Bit 28-31: All zeros (4 bits) - - The point of this format is that the two colours now can be added, and - then be converted to a RGB table index very easily: First, we just set - all the frac bits and the four upper zero bits to 1. It's now possible - to get the RGB table index by anding the current value >> 5 with the - current value >> 19. When asm-optimised, this should be the fastest - algorithm that uses RGB tables. -*/ - -namespace swrenderer -{ - PalWall1Command::PalWall1Command() - { - using namespace drawerargs; - - _iscale = dc_iscale; - _texturefrac = dc_texturefrac; - _colormap = dc_colormap; - _count = dc_count; - _source = dc_source; - _dest = dc_dest; - _fracbits = dc_wall_fracbits; - _pitch = dc_pitch; - _srcblend = dc_srcblend; - _destblend = dc_destblend; - } - - PalWall4Command::PalWall4Command() - { - using namespace drawerargs; - - _dest = dc_dest; - _count = dc_count; - _pitch = dc_pitch; - _fracbits = dc_wall_fracbits; - for (int col = 0; col < 4; col++) - { - _colormap[col] = dc_wall_colormap[col]; - _source[col] = dc_wall_source[col]; - _iscale[col] = dc_wall_iscale[col]; - _texturefrac[col] = dc_wall_texturefrac[col]; - } - _srcblend = dc_srcblend; - _destblend = dc_destblend; - } - - void DrawWall1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - *dest = colormap[source[frac >> bits]]; - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWall4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - uint32_t place; - auto pal0 = _colormap[0]; - auto pal1 = _colormap[1]; - auto pal2 = _colormap[2]; - auto pal3 = _colormap[3]; - auto buf0 = _source[0]; - auto buf1 = _source[1]; - auto buf2 = _source[2]; - auto buf3 = _source[3]; - auto dc_wall_iscale0 = _iscale[0]; - auto dc_wall_iscale1 = _iscale[1]; - auto dc_wall_iscale2 = _iscale[2]; - auto dc_wall_iscale3 = _iscale[3]; - auto dc_wall_texturefrac0 = _texturefrac[0]; - auto dc_wall_texturefrac1 = _texturefrac[1]; - auto dc_wall_texturefrac2 = _texturefrac[2]; - auto dc_wall_texturefrac3 = _texturefrac[3]; - auto pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - dc_wall_texturefrac0 += dc_wall_iscale0 * skipped; - dc_wall_texturefrac1 += dc_wall_iscale1 * skipped; - dc_wall_texturefrac2 += dc_wall_iscale2 * skipped; - dc_wall_texturefrac3 += dc_wall_iscale3 * skipped; - dc_wall_iscale0 *= thread->num_cores; - dc_wall_iscale1 *= thread->num_cores; - dc_wall_iscale2 *= thread->num_cores; - dc_wall_iscale3 *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - dest[0] = pal0[buf0[(place = dc_wall_texturefrac0) >> bits]]; dc_wall_texturefrac0 = place + dc_wall_iscale0; - dest[1] = pal1[buf1[(place = dc_wall_texturefrac1) >> bits]]; dc_wall_texturefrac1 = place + dc_wall_iscale1; - dest[2] = pal2[buf2[(place = dc_wall_texturefrac2) >> bits]]; dc_wall_texturefrac2 = place + dc_wall_iscale2; - dest[3] = pal3[buf3[(place = dc_wall_texturefrac3) >> bits]]; dc_wall_texturefrac3 = place + dc_wall_iscale3; - dest += pitch; - } while (--count); - } - - void DrawWallMasked1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - *dest = colormap[pix]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallMasked4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - uint32_t place; - auto pal0 = _colormap[0]; - auto pal1 = _colormap[1]; - auto pal2 = _colormap[2]; - auto pal3 = _colormap[3]; - auto buf0 = _source[0]; - auto buf1 = _source[1]; - auto buf2 = _source[2]; - auto buf3 = _source[3]; - auto dc_wall_iscale0 = _iscale[0]; - auto dc_wall_iscale1 = _iscale[1]; - auto dc_wall_iscale2 = _iscale[2]; - auto dc_wall_iscale3 = _iscale[3]; - auto dc_wall_texturefrac0 = _texturefrac[0]; - auto dc_wall_texturefrac1 = _texturefrac[1]; - auto dc_wall_texturefrac2 = _texturefrac[2]; - auto dc_wall_texturefrac3 = _texturefrac[3]; - auto pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - dc_wall_texturefrac0 += dc_wall_iscale0 * skipped; - dc_wall_texturefrac1 += dc_wall_iscale1 * skipped; - dc_wall_texturefrac2 += dc_wall_iscale2 * skipped; - dc_wall_texturefrac3 += dc_wall_iscale3 * skipped; - dc_wall_iscale0 *= thread->num_cores; - dc_wall_iscale1 *= thread->num_cores; - dc_wall_iscale2 *= thread->num_cores; - dc_wall_iscale3 *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix; - - pix = buf0[(place = dc_wall_texturefrac0) >> bits]; if (pix) dest[0] = pal0[pix]; dc_wall_texturefrac0 = place + dc_wall_iscale0; - pix = buf1[(place = dc_wall_texturefrac1) >> bits]; if (pix) dest[1] = pal1[pix]; dc_wall_texturefrac1 = place + dc_wall_iscale1; - pix = buf2[(place = dc_wall_texturefrac2) >> bits]; if (pix) dest[2] = pal2[pix]; dc_wall_texturefrac2 = place + dc_wall_iscale2; - pix = buf3[(place = dc_wall_texturefrac3) >> bits]; if (pix) dest[3] = pal3[pix]; dc_wall_texturefrac3 = place + dc_wall_iscale3; - dest += pitch; - } while (--count); - } - - void DrawWallAdd1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - uint32_t fg = fg2rgb[colormap[pix]]; - uint32_t bg = bg2rgb[*dest]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallAdd4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - uint32_t dc_wall_texturefrac[4] = { _texturefrac[0], _texturefrac[1], _texturefrac[2], _texturefrac[3] }; - uint32_t dc_wall_iscale[4] = { _iscale[0], _iscale[1], _iscale[2], _iscale[3] }; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] += dc_wall_iscale[i] * skipped; - dc_wall_iscale[i] *= thread->num_cores; - } - pitch *= thread->num_cores; - - do - { - for (int i = 0; i < 4; ++i) - { - uint8_t pix = _source[i][dc_wall_texturefrac[i] >> bits]; - if (pix != 0) - { - uint32_t fg = fg2rgb[_colormap[i][pix]]; - uint32_t bg = bg2rgb[dest[i]]; - fg = (fg + bg) | 0x1f07c1f; - dest[i] = RGB32k.All[fg & (fg >> 15)]; - } - dc_wall_texturefrac[i] += dc_wall_iscale[i]; - } - dest += pitch; - } while (--count); - } - - void DrawWallAddClamp1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - uint32_t a = fg2rgb[colormap[pix]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallAddClamp4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - uint32_t dc_wall_texturefrac[4] = { _texturefrac[0], _texturefrac[1], _texturefrac[2], _texturefrac[3] }; - uint32_t dc_wall_iscale[4] = { _iscale[0], _iscale[1], _iscale[2], _iscale[3] }; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] += dc_wall_iscale[i] * skipped; - dc_wall_iscale[i] *= thread->num_cores; - } - pitch *= thread->num_cores; - - do - { - for (int i = 0; i < 4; ++i) - { - uint8_t pix = _source[i][dc_wall_texturefrac[i] >> bits]; - if (pix != 0) - { - uint32_t a = fg2rgb[_colormap[i][pix]] + bg2rgb[dest[i]]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[i] = RGB32k.All[a & (a >> 15)]; - } - dc_wall_texturefrac[i] += dc_wall_iscale[i]; - } - dest += pitch; - } while (--count); - } - - void DrawWallSubClamp1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - uint32_t a = (fg2rgb[colormap[pix]] | 0x40100400) - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallSubClamp4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - uint32_t dc_wall_texturefrac[4] = { _texturefrac[0], _texturefrac[1], _texturefrac[2], _texturefrac[3] }; - uint32_t dc_wall_iscale[4] = { _iscale[0], _iscale[1], _iscale[2], _iscale[3] }; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] += dc_wall_iscale[i] * skipped; - dc_wall_iscale[i] *= thread->num_cores; - } - pitch *= thread->num_cores; - - do - { - for (int i = 0; i < 4; ++i) - { - uint8_t pix = _source[i][dc_wall_texturefrac[i] >> bits]; - if (pix != 0) - { - uint32_t a = (fg2rgb[_colormap[i][pix]] | 0x40100400) - bg2rgb[dest[i]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[i] = RGB32k.All[a & (a >> 15)]; - } - dc_wall_texturefrac[i] += dc_wall_iscale[i]; - } - dest += pitch; - } while (--count); - } - - void DrawWallRevSubClamp1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[pix]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallRevSubClamp4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - uint32_t dc_wall_texturefrac[4] = { _texturefrac[0], _texturefrac[1], _texturefrac[2], _texturefrac[3] }; - uint32_t dc_wall_iscale[4] = { _iscale[0], _iscale[1], _iscale[2], _iscale[3] }; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] += dc_wall_iscale[i] * skipped; - dc_wall_iscale[i] *= thread->num_cores; - } - pitch *= thread->num_cores; - - do - { - for (int i = 0; i < 4; ++i) - { - uint8_t pix = _source[i][dc_wall_texturefrac[i] >> bits]; - if (pix != 0) - { - uint32_t a = (bg2rgb[dest[i]] | 0x40100400) - fg2rgb[_colormap[i][pix]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[i] = RGB32k.All[a & (a >> 15)]; - } - dc_wall_texturefrac[i] += dc_wall_iscale[i]; - } - dest += _pitch; - } while (--count); - } - - ///////////////////////////////////////////////////////////////////////// - - PalSkyCommand::PalSkyCommand(uint32_t solid_top, uint32_t solid_bottom) : solid_top(solid_top), solid_bottom(solid_bottom) - { - using namespace drawerargs; - - _dest = dc_dest; - _count = dc_count; - _pitch = dc_pitch; - for (int col = 0; col < 4; col++) - { - _source[col] = dc_wall_source[col]; - _source2[col] = dc_wall_source2[col]; - _sourceheight[col] = dc_wall_sourceheight[col]; - _iscale[col] = dc_wall_iscale[col]; - _texturefrac[col] = dc_wall_texturefrac[col]; - } - } - - void DrawSingleSky1PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int pitch = _pitch; - const uint8_t *source0 = _source[0]; - int textureheight0 = _sourceheight[0]; - - int32_t frac = _texturefrac[0]; - int32_t fracstep = _iscale[0]; - - int start_fade = 2; // How fast it should fade out - - int solid_top_r = RPART(solid_top); - int solid_top_g = GPART(solid_top); - int solid_top_b = BPART(solid_top); - int solid_bottom_r = RPART(solid_bottom); - int solid_bottom_g = GPART(solid_bottom); - int solid_bottom_b = BPART(solid_bottom); - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * skipped; - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - for (int index = 0; index < count; index++) - { - uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[sample_index]; - - int alpha_top = MAX(MIN(frac >> (16 - start_fade), 256), 0); - int alpha_bottom = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); - - if (alpha_top == 256 && alpha_bottom == 256) - { - *dest = fg; - } - else - { - int inv_alpha_top = 256 - alpha_top; - int inv_alpha_bottom = 256 - alpha_bottom; - - const auto &c = GPalette.BaseColors[fg]; - int c_red = c.r; - int c_green = c.g; - int c_blue = c.b; - c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; - c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; - c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; - c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; - c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; - c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; - *dest = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - } - - frac += fracstep; - dest += pitch; - } - } - - void DrawSingleSky4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int pitch = _pitch; - const uint8_t *source0[4] = { _source[0], _source[1], _source[2], _source[3] }; - int textureheight0 = _sourceheight[0]; - const uint32_t *palette = (const uint32_t *)GPalette.BaseColors; - int32_t frac[4] = { (int32_t)_texturefrac[0], (int32_t)_texturefrac[1], (int32_t)_texturefrac[2], (int32_t)_texturefrac[3] }; - int32_t fracstep[4] = { (int32_t)_iscale[0], (int32_t)_iscale[1], (int32_t)_iscale[2], (int32_t)_iscale[3] }; - uint8_t output[4]; - - int start_fade = 2; // How fast it should fade out - - int solid_top_r = RPART(solid_top); - int solid_top_g = GPART(solid_top); - int solid_top_b = BPART(solid_top); - int solid_bottom_r = RPART(solid_bottom); - int solid_bottom_g = GPART(solid_bottom); - int solid_bottom_b = BPART(solid_bottom); - uint32_t solid_top_fill = RGB32k.RGB[(solid_top_r >> 3)][(solid_top_g >> 3)][(solid_top_b >> 3)]; - uint32_t solid_bottom_fill = RGB32k.RGB[(solid_bottom_r >> 3)][(solid_bottom_g >> 3)][(solid_bottom_b >> 3)]; - solid_top_fill = (solid_top_fill << 24) | (solid_top_fill << 16) | (solid_top_fill << 8) | solid_top_fill; - solid_bottom_fill = (solid_bottom_fill << 24) | (solid_bottom_fill << 16) | (solid_bottom_fill << 8) | solid_bottom_fill; - - // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: - int fade_length = (1 << (24 - start_fade)); - int start_fadetop_y = (-frac[0]) / fracstep[0]; - int end_fadetop_y = (fade_length - frac[0]) / fracstep[0]; - int start_fadebottom_y = ((2 << 24) - fade_length - frac[0]) / fracstep[0]; - int end_fadebottom_y = ((2 << 24) - frac[0]) / fracstep[0]; - for (int col = 1; col < 4; col++) - { - start_fadetop_y = MIN(start_fadetop_y, (-frac[0]) / fracstep[0]); - end_fadetop_y = MAX(end_fadetop_y, (fade_length - frac[0]) / fracstep[0]); - start_fadebottom_y = MIN(start_fadebottom_y, ((2 << 24) - fade_length - frac[0]) / fracstep[0]); - end_fadebottom_y = MAX(end_fadebottom_y, ((2 << 24) - frac[0]) / fracstep[0]); - } - start_fadetop_y = clamp(start_fadetop_y, 0, count); - end_fadetop_y = clamp(end_fadetop_y, 0, count); - start_fadebottom_y = clamp(start_fadebottom_y, 0, count); - end_fadebottom_y = clamp(end_fadebottom_y, 0, count); - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int col = 0; col < 4; col++) - { - frac[col] += fracstep[col] * skipped; - fracstep[col] *= thread->num_cores; - } - pitch *= thread->num_cores; - int num_cores = thread->num_cores; - int index = skipped; - - // Top solid color: - while (index < start_fadetop_y) - { - *((uint32_t*)dest) = solid_top_fill; - dest += pitch; - for (int col = 0; col < 4; col++) - frac[col] += fracstep[col]; - index += num_cores; - } - - // Top fade: - while (index < end_fadetop_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - - uint32_t c = palette[fg]; - int alpha_top = MAX(MIN(frac[col] >> (16 - start_fade), 256), 0); - int inv_alpha_top = 256 - alpha_top; - int c_red = RPART(c); - int c_green = GPART(c); - int c_blue = BPART(c); - c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; - c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; - c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; - output[col] = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - - frac[col] += fracstep[col]; - } - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Textured center: - while (index < start_fadebottom_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - output[col] = source0[col][sample_index]; - - frac[col] += fracstep[col]; - } - - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Fade bottom: - while (index < end_fadebottom_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - - uint32_t c = palette[fg]; - int alpha_bottom = MAX(MIN(((2 << 24) - frac[col]) >> (16 - start_fade), 256), 0); - int inv_alpha_bottom = 256 - alpha_bottom; - int c_red = RPART(c); - int c_green = GPART(c); - int c_blue = BPART(c); - c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; - c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; - c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; - output[col] = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - - frac[col] += fracstep[col]; - } - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Bottom solid color: - while (index < count) - { - *((uint32_t*)dest) = solid_bottom_fill; - dest += pitch; - index += num_cores; - } - } - - void DrawDoubleSky1PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int pitch = _pitch; - const uint8_t *source0 = _source[0]; - const uint8_t *source1 = _source2[0]; - int textureheight0 = _sourceheight[0]; - uint32_t maxtextureheight1 = _sourceheight[1] - 1; - - int32_t frac = _texturefrac[0]; - int32_t fracstep = _iscale[0]; - - int start_fade = 2; // How fast it should fade out - - int solid_top_r = RPART(solid_top); - int solid_top_g = GPART(solid_top); - int solid_top_b = BPART(solid_top); - int solid_bottom_r = RPART(solid_bottom); - int solid_bottom_g = GPART(solid_bottom); - int solid_bottom_b = BPART(solid_bottom); - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * skipped; - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - for (int index = 0; index < count; index++) - { - uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[sample_index]; - if (fg == 0) - { - uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); - fg = source1[sample_index2]; - } - - int alpha_top = MAX(MIN(frac >> (16 - start_fade), 256), 0); - int alpha_bottom = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); - - if (alpha_top == 256 && alpha_bottom == 256) - { - *dest = fg; - } - else - { - int inv_alpha_top = 256 - alpha_top; - int inv_alpha_bottom = 256 - alpha_bottom; - - const auto &c = GPalette.BaseColors[fg]; - int c_red = c.r; - int c_green = c.g; - int c_blue = c.b; - c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; - c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; - c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; - c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; - c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; - c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; - *dest = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - } - - frac += fracstep; - dest += pitch; - } - } - - void DrawDoubleSky4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int pitch = _pitch; - const uint8_t *source0[4] = { _source[0], _source[1], _source[2], _source[3] }; - const uint8_t *source1[4] = { _source2[0], _source2[1], _source2[2], _source2[3] }; - int textureheight0 = _sourceheight[0]; - uint32_t maxtextureheight1 = _sourceheight[1] - 1; - const uint32_t *palette = (const uint32_t *)GPalette.BaseColors; - int32_t frac[4] = { (int32_t)_texturefrac[0], (int32_t)_texturefrac[1], (int32_t)_texturefrac[2], (int32_t)_texturefrac[3] }; - int32_t fracstep[4] = { (int32_t)_iscale[0], (int32_t)_iscale[1], (int32_t)_iscale[2], (int32_t)_iscale[3] }; - uint8_t output[4]; - - int start_fade = 2; // How fast it should fade out - - int solid_top_r = RPART(solid_top); - int solid_top_g = GPART(solid_top); - int solid_top_b = BPART(solid_top); - int solid_bottom_r = RPART(solid_bottom); - int solid_bottom_g = GPART(solid_bottom); - int solid_bottom_b = BPART(solid_bottom); - uint32_t solid_top_fill = RGB32k.RGB[(solid_top_r >> 3)][(solid_top_g >> 3)][(solid_top_b >> 3)]; - uint32_t solid_bottom_fill = RGB32k.RGB[(solid_bottom_r >> 3)][(solid_bottom_g >> 3)][(solid_bottom_b >> 3)]; - solid_top_fill = (solid_top_fill << 24) | (solid_top_fill << 16) | (solid_top_fill << 8) | solid_top_fill; - solid_bottom_fill = (solid_bottom_fill << 24) | (solid_bottom_fill << 16) | (solid_bottom_fill << 8) | solid_bottom_fill; - - // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: - int fade_length = (1 << (24 - start_fade)); - int start_fadetop_y = (-frac[0]) / fracstep[0]; - int end_fadetop_y = (fade_length - frac[0]) / fracstep[0]; - int start_fadebottom_y = ((2 << 24) - fade_length - frac[0]) / fracstep[0]; - int end_fadebottom_y = ((2 << 24) - frac[0]) / fracstep[0]; - for (int col = 1; col < 4; col++) - { - start_fadetop_y = MIN(start_fadetop_y, (-frac[0]) / fracstep[0]); - end_fadetop_y = MAX(end_fadetop_y, (fade_length - frac[0]) / fracstep[0]); - start_fadebottom_y = MIN(start_fadebottom_y, ((2 << 24) - fade_length - frac[0]) / fracstep[0]); - end_fadebottom_y = MAX(end_fadebottom_y, ((2 << 24) - frac[0]) / fracstep[0]); - } - start_fadetop_y = clamp(start_fadetop_y, 0, count); - end_fadetop_y = clamp(end_fadetop_y, 0, count); - start_fadebottom_y = clamp(start_fadebottom_y, 0, count); - end_fadebottom_y = clamp(end_fadebottom_y, 0, count); - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int col = 0; col < 4; col++) - { - frac[col] += fracstep[col] * skipped; - fracstep[col] *= thread->num_cores; - } - pitch *= thread->num_cores; - int num_cores = thread->num_cores; - int index = skipped; - - // Top solid color: - while (index < start_fadetop_y) - { - *((uint32_t*)dest) = solid_top_fill; - dest += pitch; - for (int col = 0; col < 4; col++) - frac[col] += fracstep[col]; - index += num_cores; - } - - // Top fade: - while (index < end_fadetop_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - if (fg == 0) - { - uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); - fg = source1[col][sample_index2]; - } - output[col] = fg; - - uint32_t c = palette[fg]; - int alpha_top = MAX(MIN(frac[col] >> (16 - start_fade), 256), 0); - int inv_alpha_top = 256 - alpha_top; - int c_red = RPART(c); - int c_green = GPART(c); - int c_blue = BPART(c); - c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; - c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; - c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; - output[col] = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - - frac[col] += fracstep[col]; - } - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Textured center: - while (index < start_fadebottom_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - if (fg == 0) - { - uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); - fg = source1[col][sample_index2]; - } - output[col] = fg; - - frac[col] += fracstep[col]; - } - - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Fade bottom: - while (index < end_fadebottom_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - if (fg == 0) - { - uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); - fg = source1[col][sample_index2]; - } - output[col] = fg; - - uint32_t c = palette[fg]; - int alpha_bottom = MAX(MIN(((2 << 24) - frac[col]) >> (16 - start_fade), 256), 0); - int inv_alpha_bottom = 256 - alpha_bottom; - int c_red = RPART(c); - int c_green = GPART(c); - int c_blue = BPART(c); - c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; - c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; - c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; - output[col] = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - - frac[col] += fracstep[col]; - } - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Bottom solid color: - while (index < count) - { - *((uint32_t*)dest) = solid_bottom_fill; - dest += pitch; - index += num_cores; - } - } - - ///////////////////////////////////////////////////////////////////////// - - PalColumnCommand::PalColumnCommand() - { - using namespace drawerargs; - - _count = dc_count; - _dest = dc_dest; - _pitch = dc_pitch; - _iscale = dc_iscale; - _texturefrac = dc_texturefrac; - _colormap = dc_colormap; - _source = dc_source; - _translation = dc_translation; - _color = dc_color; - _srcblend = dc_srcblend; - _destblend = dc_destblend; - _srccolor = dc_srccolor; - } - - void DrawColumnPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - - // Framebuffer destination address. - dest = _dest; - - // Determine scaling, - // which is the only mapping to be done. - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - // [RH] Get local copies of these variables so that the compiler - // has a better chance of optimizing this well. - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - - // Inner loop that does the actual texture mapping, - // e.g. a DDA-lile scaling. - // This is as fast as it gets. - do - { - // Re-map color indices from wall texture column - // using a lighting/special effects LUT. - *dest = colormap[source[frac >> FRACBITS]]; - - dest += pitch; - frac += fracstep; - - } while (--count); - } - - void FillColumnPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - dest = _dest; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - uint8_t color = _color; - do - { - *dest = color; - dest += pitch; - } while (--count); - } - - void FillColumnAddPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - dest = _dest; - uint32_t *bg2rgb; - uint32_t fg; - - bg2rgb = _destblend; - fg = _srccolor; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - do - { - uint32_t bg; - bg = (fg + bg2rgb[*dest]) | 0x1f07c1f; - *dest = RGB32k.All[bg & (bg >> 15)]; - dest += pitch; - } while (--count); - - } - - void FillColumnAddClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - - dest = _dest; - uint32_t *bg2rgb; - uint32_t fg; - - bg2rgb = _destblend; - fg = _srccolor; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - do - { - uint32_t a = fg + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - } while (--count); - } - - void FillColumnSubClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - - dest = _dest; - uint32_t *bg2rgb; - uint32_t fg; - - bg2rgb = _destblend; - fg = _srccolor | 0x40100400; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - do - { - uint32_t a = fg - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - } while (--count); - } - - void FillColumnRevSubClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - if (count <= 0) - return; - - dest = _dest; - uint32_t *bg2rgb; - uint32_t fg; - - bg2rgb = _destblend; - fg = _srccolor; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - do - { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - } while (--count); - } - - void DrawColumnAddPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - - do - { - uint32_t fg = colormap[source[frac >> FRACBITS]]; - uint32_t bg = *dest; - - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnTranslatedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t* dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - // [RH] Local copies of global vars to improve compiler optimizations - const uint8_t *colormap = _colormap; - const uint8_t *translation = _translation; - const uint8_t *source = _source; - - do - { - *dest = colormap[translation[source[frac >> FRACBITS]]]; - dest += pitch; - - frac += fracstep; - } while (--count); - } - - void DrawColumnTlatedAddPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - const uint8_t *translation = _translation; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - - do - { - uint32_t fg = colormap[translation[source[frac >> FRACBITS]]]; - uint32_t bg = *dest; - - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnShadedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac, fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - uint32_t *fgstart = &Col2RGB8[0][_color]; - - do - { - uint32_t val = colormap[source[frac >> FRACBITS]]; - uint32_t fg = fgstart[val << 8]; - val = (Col2RGB8[64 - val][*dest] + fg) | 0x1f07c1f; - *dest = RGB32k.All[val & (val >> 15)]; - - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnAddClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = fg2rgb[colormap[source[frac >> FRACBITS]]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnAddClampTranslatedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *translation = _translation; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = fg2rgb[colormap[translation[source[frac >> FRACBITS]]]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[(a >> 15) & a]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnSubClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = (fg2rgb[colormap[source[frac >> FRACBITS]]] | 0x40100400) - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnSubClampTranslatedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *translation = _translation; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = (fg2rgb[colormap[translation[source[frac >> FRACBITS]]]] | 0x40100400) - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a >> 15) & a]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnRevSubClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[source[frac >> FRACBITS]]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnRevSubClampTranslatedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *translation = _translation; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[translation[source[frac >> FRACBITS]]]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a >> 15) & a]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - ///////////////////////////////////////////////////////////////////////// - - DrawFuzzColumnPalCommand::DrawFuzzColumnPalCommand() - { - using namespace drawerargs; - - _yl = dc_yl; - _yh = dc_yh; - _x = dc_x; - _destorg = dc_destorg; - _pitch = dc_pitch; - _fuzzpos = fuzzpos; - _fuzzviewheight = fuzzviewheight; - } - - void DrawFuzzColumnPalCommand::Execute(DrawerThread *thread) - { - int yl = MAX(_yl, 1); - int yh = MIN(_yh, _fuzzviewheight); - - int count = thread->count_for_thread(yl, yh - yl + 1); - - // Zero length. - if (count <= 0) - return; - - uint8_t *map = &NormalLight.Maps[6 * 256]; - - uint8_t *dest = thread->dest_for_thread(yl, _pitch, ylookup[yl] + _x + _destorg); - - int pitch = _pitch * thread->num_cores; - int fuzzstep = thread->num_cores; - int fuzz = (_fuzzpos + thread->skipped_by_thread(yl)) % FUZZTABLE; - - yl += thread->skipped_by_thread(yl); - - // Handle the case where we would go out of bounds at the top: - if (yl < fuzzstep) - { - uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep + pitch; - //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (_pitch)) < viewheight); - - *dest = map[*srcdest]; - dest += pitch; - fuzz += fuzzstep; - fuzz %= FUZZTABLE; - - count--; - if (count == 0) - return; - } - - bool lowerbounds = (yl + (count + fuzzstep - 1) * fuzzstep > _fuzzviewheight); - if (lowerbounds) - count--; - - // Fuzz where fuzzoffset stays within bounds - while (count > 0) - { - int available = (FUZZTABLE - fuzz); - int next_wrap = available / fuzzstep; - if (available % fuzzstep != 0) - next_wrap++; - - int cnt = MIN(count, next_wrap); - count -= cnt; - do - { - uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep; - //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (_pitch)) < viewheight); - - *dest = map[*srcdest]; - dest += pitch; - fuzz += fuzzstep; - } while (--cnt); - - fuzz %= FUZZTABLE; - } - - // Handle the case where we would go out of bounds at the bottom - if (lowerbounds) - { - uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep - pitch; - //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (_pitch)) < viewheight); - - *dest = map[*srcdest]; - } - } - - ///////////////////////////////////////////////////////////////////////// - - PalSpanCommand::PalSpanCommand() - { - using namespace drawerargs; - - _source = ds_source; - _colormap = ds_colormap; - _xfrac = ds_xfrac; - _yfrac = ds_yfrac; - _y = ds_y; - _x1 = ds_x1; - _x2 = ds_x2; - _destorg = dc_destorg; - _xstep = ds_xstep; - _ystep = ds_ystep; - _xbits = ds_xbits; - _ybits = ds_ybits; - _srcblend = dc_srcblend; - _destblend = dc_destblend; - _color = ds_color; - } - - void DrawSpanPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - // Current texture index in u,v. - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - - // Lookup pixel from flat texture tile, - // re-index using light/colormap. - *dest++ = colormap[source[spot]]; - - // Next step in u,v. - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - - do - { - // Current texture index in u,v. - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - - // Lookup pixel from flat texture tile, - // re-index using light/colormap. - *dest++ = colormap[source[spot]]; - - // Next step in u,v. - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanMaskedPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - int texdata; - - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - texdata = source[spot]; - if (texdata != 0) - { - *dest = colormap[texdata]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - int texdata; - - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - texdata = source[spot]; - if (texdata != 0) - { - *dest = colormap[texdata]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanTranslucentPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - uint32_t fg = colormap[source[spot]]; - uint32_t bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest++ = RGB32k.All[fg & (fg >> 15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - uint32_t fg = colormap[source[spot]]; - uint32_t bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest++ = RGB32k.All[fg & (fg >> 15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanMaskedTranslucentPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - uint8_t texdata; - - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - texdata = source[spot]; - if (texdata != 0) - { - uint32_t fg = colormap[texdata]; - uint32_t bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - uint8_t texdata; - - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - texdata = source[spot]; - if (texdata != 0) - { - uint32_t fg = colormap[texdata]; - uint32_t bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanAddClampPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - uint32_t a = fg2rgb[colormap[source[spot]]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest++ = RGB32k.All[a & (a >> 15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - uint32_t a = fg2rgb[colormap[source[spot]]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest++ = RGB32k.All[a & (a >> 15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanMaskedAddClampPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - uint8_t texdata; - - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - texdata = source[spot]; - if (texdata != 0) - { - uint32_t a = fg2rgb[colormap[texdata]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - uint8_t texdata; - - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - texdata = source[spot]; - if (texdata != 0) - { - uint32_t a = fg2rgb[colormap[texdata]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void FillSpanPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - memset(ylookup[_y] + _x1 + _destorg, _color, _x2 - _x1 + 1); - } - - ///////////////////////////////////////////////////////////////////////// - - DrawTiltedSpanPalCommand::DrawTiltedSpanPalCommand(int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy) - : y(y), x1(x1), x2(x2), plane_sz(plane_sz), plane_su(plane_su), plane_sv(plane_sv), plane_shade(plane_shade), planeshade(planeshade), planelightfloat(planelightfloat), pviewx(pviewx), pviewy(pviewy) - { - using namespace drawerargs; - - _colormap = ds_colormap; - _destorg = dc_destorg; - _ybits = ds_ybits; - _xbits = ds_xbits; - _source = ds_source; - basecolormapdata = basecolormap->Maps; - } - - void DrawTiltedSpanPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(y)) - return; - - const uint8_t **tiltlighting = thread->tiltlighting; - - int width = x2 - x1; - double iz, uz, vz; - uint8_t *fb; - uint32_t u, v; - int i; - - iz = plane_sz[2] + plane_sz[1] * (centery - y) + plane_sz[0] * (x1 - centerx); - - // Lighting is simple. It's just linear interpolation from start to end - if (plane_shade) - { - uz = (iz + plane_sz[0] * width) * planelightfloat; - vz = iz * planelightfloat; - CalcTiltedLighting(vz, uz, width, thread); - } - else - { - for (int i = 0; i < width; ++i) - { - tiltlighting[i] = _colormap; - } - } - - uz = plane_su[2] + plane_su[1] * (centery - y) + plane_su[0] * (x1 - centerx); - vz = plane_sv[2] + plane_sv[1] * (centery - y) + plane_sv[0] * (x1 - centerx); - - fb = ylookup[y] + x1 + _destorg; - - uint8_t vshift = 32 - _ybits; - uint8_t ushift = vshift - _xbits; - int umask = ((1 << _xbits) - 1) << _ybits; - - #if 0 - // The "perfect" reference version of this routine. Pretty slow. - // Use it only to see how things are supposed to look. - i = 0; - do - { - double z = 1.f / iz; - - u = int64_t(uz*z) + pviewx; - v = int64_t(vz*z) + pviewy; - R_SetDSColorMapLight(tiltlighting[i], 0, 0); - fb[i++] = ds_colormap[ds_source[(v >> vshift) | ((u >> ushift) & umask)]]; - iz += plane_sz[0]; - uz += plane_su[0]; - vz += plane_sv[0]; - } while (--width >= 0); - #else - //#define SPANSIZE 32 - //#define INVSPAN 0.03125f - //#define SPANSIZE 8 - //#define INVSPAN 0.125f - #define SPANSIZE 16 - #define INVSPAN 0.0625f - - double startz = 1.f / iz; - double startu = uz*startz; - double startv = vz*startz; - double izstep, uzstep, vzstep; - - izstep = plane_sz[0] * SPANSIZE; - uzstep = plane_su[0] * SPANSIZE; - vzstep = plane_sv[0] * SPANSIZE; - x1 = 0; - width++; - - while (width >= SPANSIZE) - { - iz += izstep; - uz += uzstep; - vz += vzstep; - - double endz = 1.f / iz; - double endu = uz*endz; - double endv = vz*endz; - uint32_t stepu = (uint32_t)int64_t((endu - startu) * INVSPAN); - uint32_t stepv = (uint32_t)int64_t((endv - startv) * INVSPAN); - u = (uint32_t)(int64_t(startu) + pviewx); - v = (uint32_t)(int64_t(startv) + pviewy); - - for (i = SPANSIZE - 1; i >= 0; i--) - { - fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); - x1++; - u += stepu; - v += stepv; - } - startu = endu; - startv = endv; - width -= SPANSIZE; - } - if (width > 0) - { - if (width == 1) - { - u = (uint32_t)int64_t(startu); - v = (uint32_t)int64_t(startv); - fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); - } - else - { - double left = width; - iz += plane_sz[0] * left; - uz += plane_su[0] * left; - vz += plane_sv[0] * left; - - double endz = 1.f / iz; - double endu = uz*endz; - double endv = vz*endz; - left = 1.f / left; - uint32_t stepu = (uint32_t)int64_t((endu - startu) * left); - uint32_t stepv = (uint32_t)int64_t((endv - startv) * left); - u = (uint32_t)(int64_t(startu) + pviewx); - v = (uint32_t)(int64_t(startv) + pviewy); - - for (; width != 0; width--) - { - fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); - x1++; - u += stepu; - v += stepv; - } - } - } - #endif - } - - // Calculates the lighting for one row of a tilted plane. If the definition - // of GETPALOOKUP changes, this needs to change, too. - void DrawTiltedSpanPalCommand::CalcTiltedLighting(double lval, double lend, int width, DrawerThread *thread) - { - const uint8_t **tiltlighting = thread->tiltlighting; - - double lstep; - uint8_t *lightfiller; - int i = 0; - - if (width == 0 || lval == lend) - { // Constant lighting - lightfiller = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); - } - else - { - lstep = (lend - lval) / width; - if (lval >= MAXLIGHTVIS) - { // lval starts "too bright". - lightfiller = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); - for (; i <= width && lval >= MAXLIGHTVIS; ++i) - { - tiltlighting[i] = lightfiller; - lval += lstep; - } - } - if (lend >= MAXLIGHTVIS) - { // lend ends "too bright". - lightfiller = basecolormapdata + (GETPALOOKUP(lend, planeshade) << COLORMAPSHIFT); - for (; width > i && lend >= MAXLIGHTVIS; --width) - { - tiltlighting[width] = lightfiller; - lend -= lstep; - } - } - if (width > 0) - { - lval = FIXED2DBL(planeshade) - lval; - lend = FIXED2DBL(planeshade) - lend; - lstep = (lend - lval) / width; - if (lstep < 0) - { // Going from dark to light - if (lval < 1.) - { // All bright - lightfiller = basecolormapdata; - } - else - { - if (lval >= NUMCOLORMAPS) - { // Starts beyond the dark end - uint8_t *clight = basecolormapdata + ((NUMCOLORMAPS - 1) << COLORMAPSHIFT); - while (lval >= NUMCOLORMAPS && i <= width) - { - tiltlighting[i++] = clight; - lval += lstep; - } - if (i > width) - return; - } - while (i <= width && lval >= 0) - { - tiltlighting[i++] = basecolormapdata + (xs_ToInt(lval) << COLORMAPSHIFT); - lval += lstep; - } - lightfiller = basecolormapdata; - } - } - else - { // Going from light to dark - if (lval >= (NUMCOLORMAPS - 1)) - { // All dark - lightfiller = basecolormapdata + ((NUMCOLORMAPS - 1) << COLORMAPSHIFT); - } - else - { - while (lval < 0 && i <= width) - { - tiltlighting[i++] = basecolormapdata; - lval += lstep; - } - if (i > width) - return; - while (i <= width && lval < (NUMCOLORMAPS - 1)) - { - tiltlighting[i++] = basecolormapdata + (xs_ToInt(lval) << COLORMAPSHIFT); - lval += lstep; - } - lightfiller = basecolormapdata + ((NUMCOLORMAPS - 1) << COLORMAPSHIFT); - } - } - } - } - for (; i <= width; i++) - { - tiltlighting[i] = lightfiller; - } - } - - ///////////////////////////////////////////////////////////////////////// - - DrawColoredSpanPalCommand::DrawColoredSpanPalCommand(int y, int x1, int x2) : y(y), x1(x1), x2(x2) - { - using namespace drawerargs; - color = ds_color; - destorg = dc_destorg; - } - - void DrawColoredSpanPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(y)) - return; - - memset(ylookup[y] + x1 + destorg, color, x2 - x1 + 1); - } - - ///////////////////////////////////////////////////////////////////////// - - DrawSlabPalCommand::DrawSlabPalCommand(int dx, fixed_t v, int dy, fixed_t vi, const uint8_t *vptr, uint8_t *p, const uint8_t *colormap) - : _dx(dx), _v(v), _dy(dy), _vi(vi), _vvptr(vptr), _p(p), _colormap(colormap) - { - using namespace drawerargs; - _pitch = dc_pitch; - _start_y = static_cast((p - dc_destorg) / dc_pitch); - } - - void DrawSlabPalCommand::Execute(DrawerThread *thread) - { - int count = _dy; - uint8_t *dest = _p; - int pitch = _pitch; - int width = _dx; - const uint8_t *colormap = _colormap; - const uint8_t *source = _vvptr; - fixed_t fracpos = _v; - fixed_t iscale = _vi; - - count = thread->count_for_thread(_start_y, count); - dest = thread->dest_for_thread(_start_y, pitch, dest); - fracpos += iscale * thread->skipped_by_thread(_start_y); - iscale *= thread->num_cores; - pitch *= thread->num_cores; - - while (count > 0) - { - uint8_t color = colormap[source[fracpos >> FRACBITS]]; - - for (int x = 0; x < width; x++) - dest[x] = color; - - dest += pitch; - fracpos += iscale; - count--; - } - } - - ///////////////////////////////////////////////////////////////////////// - - DrawFogBoundaryLinePalCommand::DrawFogBoundaryLinePalCommand(int y, int x1, int x2) : y(y), x1(x1), x2(x2) - { - using namespace drawerargs; - _colormap = dc_colormap; - _destorg = dc_destorg; - } - - void DrawFogBoundaryLinePalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(y)) - return; - - const uint8_t *colormap = _colormap; - uint8_t *dest = ylookup[y] + _destorg; - int x = x1; - do - { - dest[x] = colormap[dest[x]]; - } while (++x <= x2); - } -} diff --git a/src/r_draw_pal.h b/src/r_draw_pal.h deleted file mode 100644 index ab3c98327e..0000000000 --- a/src/r_draw_pal.h +++ /dev/null @@ -1,329 +0,0 @@ - -#pragma once - -#include "r_draw.h" -#include "v_palette.h" -#include "r_thread.h" - -namespace swrenderer -{ - class PalWall1Command : public DrawerCommand - { - public: - PalWall1Command(); - FString DebugInfo() override { return "PalWallCommand"; } - - protected: - uint32_t _iscale; - uint32_t _texturefrac; - uint8_t *_colormap; - int _count; - const uint8_t *_source; - uint8_t *_dest; - int _fracbits; - int _pitch; - uint32_t *_srcblend; - uint32_t *_destblend; - }; - - class PalWall4Command : public DrawerCommand - { - public: - PalWall4Command(); - FString DebugInfo() override { return "PalWallCommand"; } - - protected: - uint8_t *_dest; - int _count; - int _pitch; - int _fracbits; - uint8_t *_colormap[4]; - const uint8_t *_source[4]; - uint32_t _iscale[4]; - uint32_t _texturefrac[4]; - uint32_t *_srcblend; - uint32_t *_destblend; - }; - - class DrawWall1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWall4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallMasked1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallMasked4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallAdd1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallAdd4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallAddClamp1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallAddClamp4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallSubClamp1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallSubClamp4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallRevSubClamp1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallRevSubClamp4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - - class PalSkyCommand : public DrawerCommand - { - public: - PalSkyCommand(uint32_t solid_top, uint32_t solid_bottom); - FString DebugInfo() override { return "PalSkyCommand"; } - - protected: - uint32_t solid_top; - uint32_t solid_bottom; - - uint8_t *_dest; - int _count; - int _pitch; - const uint8_t *_source[4]; - const uint8_t *_source2[4]; - int _sourceheight[4]; - uint32_t _iscale[4]; - uint32_t _texturefrac[4]; - }; - - class DrawSingleSky1PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; - class DrawSingleSky4PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; - class DrawDoubleSky1PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; - class DrawDoubleSky4PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; - - class PalColumnCommand : public DrawerCommand - { - public: - PalColumnCommand(); - FString DebugInfo() override { return "PalColumnCommand"; } - - protected: - int _count; - uint8_t *_dest; - int _pitch; - fixed_t _iscale; - fixed_t _texturefrac; - const uint8_t *_colormap; - const uint8_t *_source; - const uint8_t *_translation; - int _color; - uint32_t *_srcblend; - uint32_t *_destblend; - uint32_t _srccolor; - }; - - class DrawColumnPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnAddPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnAddClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnSubClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnRevSubClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnAddPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnTranslatedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnTlatedAddPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnShadedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnAddClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnAddClampTranslatedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnSubClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnSubClampTranslatedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnRevSubClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnRevSubClampTranslatedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - - class DrawFuzzColumnPalCommand : public DrawerCommand - { - public: - DrawFuzzColumnPalCommand(); - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "DrawFuzzColumnPalCommand"; } - - private: - int _yl; - int _yh; - int _x; - uint8_t *_destorg; - int _pitch; - int _fuzzpos; - int _fuzzviewheight; - }; - - class PalSpanCommand : public DrawerCommand - { - public: - PalSpanCommand(); - FString DebugInfo() override { return "PalSpanCommand"; } - - protected: - const uint8_t *_source; - const uint8_t *_colormap; - dsfixed_t _xfrac; - dsfixed_t _yfrac; - int _y; - int _x1; - int _x2; - uint8_t *_destorg; - dsfixed_t _xstep; - dsfixed_t _ystep; - int _xbits; - int _ybits; - uint32_t *_srcblend; - uint32_t *_destblend; - int _color; - }; - - class DrawSpanPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanMaskedPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanTranslucentPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanMaskedTranslucentPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanAddClampPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanMaskedAddClampPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class FillSpanPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - - class DrawTiltedSpanPalCommand : public DrawerCommand - { - public: - DrawTiltedSpanPalCommand(int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy); - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "DrawTiltedSpanPalCommand"; } - - private: - void CalcTiltedLighting(double lval, double lend, int width, DrawerThread *thread); - - int y; - int x1; - int x2; - FVector3 plane_sz; - FVector3 plane_su; - FVector3 plane_sv; - bool plane_shade; - int planeshade; - float planelightfloat; - fixed_t pviewx; - fixed_t pviewy; - - const uint8_t *_colormap; - uint8_t *_destorg; - int _ybits; - int _xbits; - const uint8_t *_source; - uint8_t *basecolormapdata; - }; - - class DrawColoredSpanPalCommand : public PalSpanCommand - { - public: - DrawColoredSpanPalCommand(int y, int x1, int x2); - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "DrawColoredSpanPalCommand"; } - - private: - int y; - int x1; - int x2; - int color; - uint8_t *destorg; - }; - - class DrawSlabPalCommand : public PalSpanCommand - { - public: - DrawSlabPalCommand(int dx, fixed_t v, int dy, fixed_t vi, const uint8_t *vptr, uint8_t *p, const uint8_t *colormap); - void Execute(DrawerThread *thread) override; - - private: - int _dx; - fixed_t _v; - int _dy; - fixed_t _vi; - const uint8_t *_vvptr; - uint8_t *_p; - const uint8_t *_colormap; - int _pitch; - int _start_y; - }; - - class DrawFogBoundaryLinePalCommand : public PalSpanCommand - { - public: - DrawFogBoundaryLinePalCommand(int y, int x1, int x2); - void Execute(DrawerThread *thread) override; - - private: - int y, x1, x2; - const uint8_t *_colormap; - uint8_t *_destorg; - }; - - class RtInitColsPalCommand : public DrawerCommand - { - public: - RtInitColsPalCommand(uint8_t *buff); - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "RtInitColsPalCommand"; } - - private: - uint8_t *buff; - }; - - class PalColumnHorizCommand : public DrawerCommand - { - public: - PalColumnHorizCommand(); - - protected: - const uint8_t *_source; - fixed_t _iscale; - fixed_t _texturefrac; - int _count; - int _color; - int _x; - int _yl; - }; - - class DrawColumnHorizPalCommand : public PalColumnHorizCommand - { - public: - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "DrawColumnHorizPalCommand"; } - }; - - class FillColumnHorizPalCommand : public PalColumnHorizCommand - { - public: - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "FillColumnHorizPalCommand"; } - }; - - class PalRtCommand : public DrawerCommand - { - public: - PalRtCommand(int hx, int sx, int yl, int yh); - FString DebugInfo() override { return "PalRtCommand"; } - - protected: - int hx, sx, yl, yh; - uint8_t *_destorg; - int _pitch; - const uint8_t *_colormap; - const uint32_t *_srcblend; - const uint32_t *_destblend; - const uint8_t *_translation; - int _color; - }; - - class DrawColumnRt1CopyPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4CopyPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1PalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4PalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1TranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4TranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1AddPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4AddPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt1AddTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt4AddTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1ShadedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4ShadedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1AddClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4AddClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt1AddClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt4AddClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1SubClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4SubClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt1SubClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt4SubClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1RevSubClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4RevSubClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt1RevSubClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt4RevSubClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; -} diff --git a/src/r_drawt_pal.cpp b/src/r_drawt_pal.cpp deleted file mode 100644 index 3356592d25..0000000000 --- a/src/r_drawt_pal.cpp +++ /dev/null @@ -1,867 +0,0 @@ -/* -** r_drawt.cpp -** Faster column drawers for modern processors -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit -** 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. -**--------------------------------------------------------------------------- -** -** These functions stretch columns into a temporary buffer and then -** map them to the screen. On modern machines, this is faster than drawing -** them directly to the screen. -** -** Will I be able to even understand any of this if I come back to it later? -** Let's hope so. :-) -*/ - -#include "templates.h" -#include "doomtype.h" -#include "doomdef.h" -#include "r_defs.h" -#include "r_draw.h" -#include "r_main.h" -#include "r_things.h" -#include "v_video.h" -#include "r_draw_pal.h" - -// I should have commented this stuff better. -// -// dc_temp is the buffer R_DrawColumnHoriz writes into. -// dc_tspans points into it. -// dc_ctspan points into dc_tspans. -// horizspan also points into dc_tspans. - -// dc_ctspan is advanced while drawing into dc_temp. -// horizspan is advanced up to dc_ctspan when drawing from dc_temp to the screen. - -namespace swrenderer -{ - RtInitColsPalCommand::RtInitColsPalCommand(uint8_t *buff) : buff(buff) - { - } - - void RtInitColsPalCommand::Execute(DrawerThread *thread) - { - thread->dc_temp = buff == nullptr ? thread->dc_temp_buff : buff; - } - - ///////////////////////////////////////////////////////////////////// - - PalColumnHorizCommand::PalColumnHorizCommand() - { - using namespace drawerargs; - - _source = dc_source; - _iscale = dc_iscale; - _texturefrac = dc_texturefrac; - _count = dc_count; - _color = dc_color; - _x = dc_x; - _yl = dc_yl; - } - - void DrawColumnHorizPalCommand::Execute(DrawerThread *thread) - { - int count = _count; - uint8_t *dest; - fixed_t fracstep; - fixed_t frac; - - count = thread->count_for_thread(_yl, count); - if (count <= 0) - return; - - fracstep = _iscale; - frac = _texturefrac; - - const uint8_t *source = _source; - - int x = _x & 3; - dest = &thread->dc_temp[x + thread->temp_line_for_thread(_yl) * 4]; - frac += fracstep * thread->skipped_by_thread(_yl); - fracstep *= thread->num_cores; - - if (count & 1) { - *dest = source[frac >> FRACBITS]; dest += 4; frac += fracstep; - } - if (count & 2) { - dest[0] = source[frac >> FRACBITS]; frac += fracstep; - dest[4] = source[frac >> FRACBITS]; frac += fracstep; - dest += 8; - } - if (count & 4) { - dest[0] = source[frac >> FRACBITS]; frac += fracstep; - dest[4] = source[frac >> FRACBITS]; frac += fracstep; - dest[8] = source[frac >> FRACBITS]; frac += fracstep; - dest[12] = source[frac >> FRACBITS]; frac += fracstep; - dest += 16; - } - count >>= 3; - if (!count) return; - - do - { - dest[0] = source[frac >> FRACBITS]; frac += fracstep; - dest[4] = source[frac >> FRACBITS]; frac += fracstep; - dest[8] = source[frac >> FRACBITS]; frac += fracstep; - dest[12] = source[frac >> FRACBITS]; frac += fracstep; - dest[16] = source[frac >> FRACBITS]; frac += fracstep; - dest[20] = source[frac >> FRACBITS]; frac += fracstep; - dest[24] = source[frac >> FRACBITS]; frac += fracstep; - dest[28] = source[frac >> FRACBITS]; frac += fracstep; - dest += 32; - } while (--count); - } - - void FillColumnHorizPalCommand::Execute(DrawerThread *thread) - { - int count = _count; - uint8_t color = _color; - uint8_t *dest; - - count = thread->count_for_thread(_yl, count); - if (count <= 0) - return; - - int x = _x & 3; - dest = &thread->dc_temp[x + thread->temp_line_for_thread(_yl) * 4]; - - if (count & 1) { - *dest = color; - dest += 4; - } - if (!(count >>= 1)) - return; - do { - dest[0] = color; dest[4] = color; - dest += 8; - } while (--count); - } - - ///////////////////////////////////////////////////////////////////// - - PalRtCommand::PalRtCommand(int hx, int sx, int yl, int yh) : hx(hx), sx(sx), yl(yl), yh(yh) - { - using namespace drawerargs; - - _destorg = dc_destorg; - _pitch = dc_pitch; - _colormap = dc_colormap; - _srcblend = dc_srcblend; - _destblend = dc_destblend; - _translation = dc_translation; - _color = dc_color; - } - - void DrawColumnRt1CopyPalCommand::Execute(DrawerThread *thread) - { - uint8_t *source; - uint8_t *dest; - int count; - int pitch; - - count = yh - yl + 1; - - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - - if (count & 1) { - *dest = *source; - source += 4; - dest += pitch; - } - if (count & 2) { - dest[0] = source[0]; - dest[pitch] = source[4]; - source += 8; - dest += pitch*2; - } - if (!(count >>= 2)) - return; - - do { - dest[0] = source[0]; - dest[pitch] = source[4]; - dest[pitch*2] = source[8]; - dest[pitch*3] = source[12]; - source += 16; - dest += pitch*4; - } while (--count); - } - - void DrawColumnRt4CopyPalCommand::Execute(DrawerThread *thread) - { - int *source; - int *dest; - int count; - int pitch; - - count = yh - yl + 1; - - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - dest = (int *)(ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg); - source = (int *)(&thread->dc_temp[thread->temp_line_for_thread(yl)*4]); - pitch = _pitch*thread->num_cores/sizeof(int); - - if (count & 1) { - *dest = *source; - source += 4/sizeof(int); - dest += pitch; - } - if (!(count >>= 1)) - return; - - do { - dest[0] = source[0]; - dest[pitch] = source[4/sizeof(int)]; - source += 8/sizeof(int); - dest += pitch*2; - } while (--count); - } - - void DrawColumnRt1PalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int count; - int pitch; - - count = yh - yl + 1; - - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - colormap = _colormap; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl) *4 + hx]; - pitch = _pitch*thread->num_cores; - - if (count & 1) { - *dest = colormap[*source]; - source += 4; - dest += pitch; - } - if (!(count >>= 1)) - return; - - do { - dest[0] = colormap[source[0]]; - dest[pitch] = colormap[source[4]]; - source += 8; - dest += pitch*2; - } while (--count); - } - - void DrawColumnRt4PalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int count; - int pitch; - - count = yh - yl + 1; - - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - colormap = _colormap; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch*thread->num_cores; - - if (count & 1) { - dest[0] = colormap[source[0]]; - dest[1] = colormap[source[1]]; - dest[2] = colormap[source[2]]; - dest[3] = colormap[source[3]]; - source += 4; - dest += pitch; - } - if (!(count >>= 1)) - return; - - do { - dest[0] = colormap[source[0]]; - dest[1] = colormap[source[1]]; - dest[2] = colormap[source[2]]; - dest[3] = colormap[source[3]]; - dest[pitch] = colormap[source[4]]; - dest[pitch+1] = colormap[source[5]]; - dest[pitch+2] = colormap[source[6]]; - dest[pitch+3] = colormap[source[7]]; - source += 8; - dest += pitch*2; - } while (--count); - } - - void DrawColumnRt1TranslatedPalCommand::Execute(DrawerThread *thread) - { - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - uint8_t *source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - const uint8_t *translation = _translation; - - // Things we do to hit the compiler's optimizer with a clue bat: - // 1. Parallelism is explicitly spelled out by using a separate - // C instruction for each assembly instruction. GCC lets me - // have four temporaries, but VC++ spills to the stack with - // more than two. Two is probably optimal, anyway. - // 2. The results of the translation lookups are explicitly - // stored in byte-sized variables. This causes the VC++ code - // to use byte mov instructions in most cases; for apparently - // random reasons, it will use movzx for some places. GCC - // ignores this and uses movzx always. - - // Do 8 rows at a time. - for (int count8 = count >> 3; count8; --count8) - { - int c0, c1; - uint8_t b0, b1; - - c0 = source[0]; c1 = source[4]; - b0 = translation[c0]; b1 = translation[c1]; - source[0] = b0; source[4] = b1; - - c0 = source[8]; c1 = source[12]; - b0 = translation[c0]; b1 = translation[c1]; - source[8] = b0; source[12] = b1; - - c0 = source[16]; c1 = source[20]; - b0 = translation[c0]; b1 = translation[c1]; - source[16] = b0; source[20] = b1; - - c0 = source[24]; c1 = source[28]; - b0 = translation[c0]; b1 = translation[c1]; - source[24] = b0; source[28] = b1; - - source += 32; - } - // Finish by doing 1 row at a time. - for (count &= 7; count; --count, source += 4) - { - source[0] = translation[source[0]]; - } - } - - void DrawColumnRt4TranslatedPalCommand::Execute(DrawerThread *thread) - { - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - uint8_t *source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - const uint8_t *translation = _translation; - int c0, c1; - uint8_t b0, b1; - - // Do 2 rows at a time. - for (int count8 = count >> 1; count8; --count8) - { - c0 = source[0]; c1 = source[1]; - b0 = translation[c0]; b1 = translation[c1]; - source[0] = b0; source[1] = b1; - - c0 = source[2]; c1 = source[3]; - b0 = translation[c0]; b1 = translation[c1]; - source[2] = b0; source[3] = b1; - - c0 = source[4]; c1 = source[5]; - b0 = translation[c0]; b1 = translation[c1]; - source[4] = b0; source[5] = b1; - - c0 = source[6]; c1 = source[7]; - b0 = translation[c0]; b1 = translation[c1]; - source[6] = b0; source[7] = b1; - - source += 8; - } - // Do the final row if count was odd. - if (count & 1) - { - c0 = source[0]; c1 = source[1]; - b0 = translation[c0]; b1 = translation[c1]; - source[0] = b0; source[1] = b1; - - c0 = source[2]; c1 = source[3]; - b0 = translation[c0]; b1 = translation[c1]; - source[2] = b0; source[3] = b1; - } - } - - void DrawColumnRt1AddPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t fg = colormap[*source]; - uint32_t bg = *dest; - - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg>>15)]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4AddPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t fg = colormap[source[0]]; - uint32_t bg = dest[0]; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - dest[0] = RGB32k.All[fg & (fg>>15)]; - - fg = colormap[source[1]]; - bg = dest[1]; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - dest[1] = RGB32k.All[fg & (fg>>15)]; - - - fg = colormap[source[2]]; - bg = dest[2]; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - dest[2] = RGB32k.All[fg & (fg>>15)]; - - fg = colormap[source[3]]; - bg = dest[3]; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - dest[3] = RGB32k.All[fg & (fg>>15)]; - - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt1ShadedPalCommand::Execute(DrawerThread *thread) - { - uint32_t *fgstart; - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - fgstart = &Col2RGB8[0][_color]; - colormap = _colormap; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - - do { - uint32_t val = colormap[*source]; - uint32_t fg = fgstart[val<<8]; - val = (Col2RGB8[64-val][*dest] + fg) | 0x1f07c1f; - *dest = RGB32k.All[val & (val>>15)]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4ShadedPalCommand::Execute(DrawerThread *thread) - { - uint32_t *fgstart; - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - fgstart = &Col2RGB8[0][_color]; - colormap = _colormap; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - - do { - uint32_t val; - - val = colormap[source[0]]; - val = (Col2RGB8[64-val][dest[0]] + fgstart[val<<8]) | 0x1f07c1f; - dest[0] = RGB32k.All[val & (val>>15)]; - - val = colormap[source[1]]; - val = (Col2RGB8[64-val][dest[1]] + fgstart[val<<8]) | 0x1f07c1f; - dest[1] = RGB32k.All[val & (val>>15)]; - - val = colormap[source[2]]; - val = (Col2RGB8[64-val][dest[2]] + fgstart[val<<8]) | 0x1f07c1f; - dest[2] = RGB32k.All[val & (val>>15)]; - - val = colormap[source[3]]; - val = (Col2RGB8[64-val][dest[3]] + fgstart[val<<8]) | 0x1f07c1f; - dest[3] = RGB32k.All[val & (val>>15)]; - - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt1AddClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = fg2rgb[colormap[*source]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[(a>>15) & a]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4AddClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - - do { - uint32_t a = fg2rgb[colormap[source[0]]] + bg2rgb[dest[0]]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[0] = RGB32k.All[(a>>15) & a]; - - a = fg2rgb[colormap[source[1]]] + bg2rgb[dest[1]]; - b = a; - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[1] = RGB32k.All[(a>>15) & a]; - - a = fg2rgb[colormap[source[2]]] + bg2rgb[dest[2]]; - b = a; - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[2] = RGB32k.All[(a>>15) & a]; - - a = fg2rgb[colormap[source[3]]] + bg2rgb[dest[3]]; - b = a; - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[3] = RGB32k.All[(a>>15) & a]; - - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt1SubClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = (fg2rgb[colormap[*source]] | 0x40100400) - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a>>15) & a]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4SubClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = (fg2rgb[colormap[source[0]]] | 0x40100400) - bg2rgb[dest[0]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[0] = RGB32k.All[(a>>15) & a]; - - a = (fg2rgb[colormap[source[1]]] | 0x40100400) - bg2rgb[dest[1]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[1] = RGB32k.All[(a>>15) & a]; - - a = (fg2rgb[colormap[source[2]]] | 0x40100400) - bg2rgb[dest[2]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[2] = RGB32k.All[(a>>15) & a]; - - a = (fg2rgb[colormap[source[3]]] | 0x40100400) - bg2rgb[dest[3]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[3] = RGB32k.All[(a>>15) & a]; - - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt1RevSubClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[*source]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a>>15) & a]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4RevSubClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = (bg2rgb[dest[0]] | 0x40100400) - fg2rgb[colormap[source[0]]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[0] = RGB32k.All[(a>>15) & a]; - - a = (bg2rgb[dest[1]] | 0x40100400) - fg2rgb[colormap[source[1]]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[1] = RGB32k.All[(a>>15) & a]; - - a = (bg2rgb[dest[2]] | 0x40100400) - fg2rgb[colormap[source[2]]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[2] = RGB32k.All[(a>>15) & a]; - - a = (bg2rgb[dest[3]] | 0x40100400) - fg2rgb[colormap[source[3]]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[3] = RGB32k.All[(a>>15) & a]; - - source += 4; - dest += pitch; - } while (--count); - } -} diff --git a/src/r_local.h b/src/r_local.h deleted file mode 100644 index b0ba8841ee..0000000000 --- a/src/r_local.h +++ /dev/null @@ -1,40 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Refresh (R_*) module, global header. -// All the rendering/drawing stuff is here. -// -//----------------------------------------------------------------------------- - -#ifndef __R_LOCAL_H__ -#define __R_LOCAL_H__ - -// Binary Angles, sine/cosine/atan lookups. - -// Screen size related parameters. -#include "doomdef.h" - -// Include the refresh/render data structs. - -// -// Separate header file for each module. -// -#include "r_main.h" -#include "r_things.h" -#include "r_draw.h" - -#endif // __R_LOCAL_H__ diff --git a/src/r_main.cpp b/src/r_main.cpp deleted file mode 100644 index 4a8e796add..0000000000 --- a/src/r_main.cpp +++ /dev/null @@ -1,1077 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// $Log:$ -// -// DESCRIPTION: -// Rendering main loop and setup functions, -// utility functions (BSP, geometry, trigonometry). -// See tables.c, too. -// -//----------------------------------------------------------------------------- - -// HEADER FILES ------------------------------------------------------------ - -#include -#include - -#include "templates.h" -#include "doomdef.h" -#include "d_net.h" -#include "doomstat.h" -#include "m_random.h" -#include "m_bbox.h" -#include "r_local.h" -#include "r_plane.h" -#include "r_bsp.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "r_sky.h" -#include "st_stuff.h" -#include "c_cvars.h" -#include "c_dispatch.h" -#include "v_video.h" -#include "stats.h" -#include "i_video.h" -#include "i_system.h" -#include "a_sharedglobal.h" -#include "r_data/r_translate.h" -#include "p_3dmidtex.h" -#include "r_data/r_interpolate.h" -#include "v_palette.h" -#include "po_man.h" -#include "p_effect.h" -#include "st_start.h" -#include "v_font.h" -#include "r_data/colormaps.h" -#include "p_maputl.h" -#include "r_thread.h" -#include "events.h" - -CVAR (String, r_viewsize, "", CVAR_NOSET) -CVAR (Bool, r_shadercolormaps, true, CVAR_ARCHIVE) - -CUSTOM_CVAR (Int, r_columnmethod, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self != 0 && self != 1) - { - self = 1; - } - else - { // Trigger the change - setsizeneeded = true; - } -} - -CVAR(Int, r_portal_recursions, 4, CVAR_ARCHIVE) -CVAR(Bool, r_highlight_portals, false, CVAR_ARCHIVE) - -EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) - -extern cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; -extern cycle_t FrameCycles; - -extern bool r_showviewer; - -cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; - -namespace swrenderer -{ - using namespace drawerargs; - -// MACROS ------------------------------------------------------------------ - -#if 0 -#define TEST_X 32343794 -#define TEST_Y 111387517 -#define TEST_Z 2164524 -#define TEST_ANGLE 2468347904 -#endif - - -// TYPES ------------------------------------------------------------------- - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -void R_SpanInitData (); -void R_DeinitSprites(); - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static void R_ShutdownRenderer(); - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -extern short *openings; -extern bool r_fakingunderwater; -extern int fuzzviewheight; -extern subsector_t *InSubsector; - - -// PRIVATE DATA DECLARATIONS ----------------------------------------------- - -static double CurrentVisibility = 8.f; -static double MaxVisForWall; -static double MaxVisForFloor; -bool r_dontmaplines; - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -double r_BaseVisibility; -double r_WallVisibility; -double r_FloorVisibility; -float r_TiltVisibility; -double r_SpriteVisibility; -double r_ParticleVisibility; - -double GlobVis; -fixed_t viewingrangerecip; -double FocalLengthX; -double FocalLengthY; -FDynamicColormap*basecolormap; // [RH] colormap currently drawing with -int fixedlightlev; -lighttable_t *fixedcolormap; -FSpecialColormap *realfixedcolormap; -double WallTMapScale2; - - -bool bRenderingToCanvas; // [RH] True if rendering to a special canvas -double globaluclip, globaldclip; -double CenterX, CenterY; -double YaspectMul; -double BaseYaspectMul; // yaspectmul without a forced aspect ratio -double IYaspectMul; -double InvZtoScale; - -// just for profiling purposes -int linecount; -int loopcount; - - -// -// precalculated math tables -// - -// The xtoviewangleangle[] table maps a screen pixel -// to the lowest viewangle that maps back to x ranges -// from clipangle to -clipangle. -angle_t xtoviewangle[MAXWIDTH+1]; - -bool foggy; // [RH] ignore extralight and fullbright? -int r_actualextralight; - -void (*colfunc) (void); -void (*basecolfunc) (void); -void (*fuzzcolfunc) (void); -void (*transcolfunc) (void); -void (*spanfunc) (void); - -void (*hcolfunc_pre) (void); -void (*hcolfunc_post1) (int hx, int sx, int yl, int yh); -void (*hcolfunc_post2) (int hx, int sx, int yl, int yh); -void (*hcolfunc_post4) (int sx, int yl, int yh); - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static int lastcenteryfrac; - -// CODE -------------------------------------------------------------------- - -//========================================================================== -// -// R_InitTextureMapping -// -//========================================================================== - -void R_InitTextureMapping () -{ - int i; - - // Calc focallength so FieldOfView angles cover viewwidth. - FocalLengthX = CenterX / FocalTangent; - FocalLengthY = FocalLengthX * YaspectMul; - - // This is 1/FocalTangent before the widescreen extension of FOV. - viewingrangerecip = FLOAT2FIXED(1. / tan(FieldOfView.Radians() / 2)); - - - // Now generate xtoviewangle for sky texture mapping. - // [RH] Do not generate viewangletox, because texture mapping is no - // longer done with trig, so it's not needed. - const double slopestep = FocalTangent / centerx; - double slope; - - for (i = centerx, slope = 0; i <= viewwidth; i++, slope += slopestep) - { - xtoviewangle[i] = angle_t((2 * M_PI - atan(slope)) * (ANGLE_180 / M_PI)); - } - for (i = 0; i < centerx; i++) - { - xtoviewangle[i] = 0 - xtoviewangle[viewwidth - i - 1]; - } -} - -//========================================================================== -// -// R_SetVisibility -// -// Changes how rapidly things get dark with distance -// -//========================================================================== - -void R_SetVisibility(double vis) -{ - // Allow negative visibilities, just for novelty's sake - vis = clamp(vis, -204.7, 204.7); // (205 and larger do not work in 5:4 aspect ratio) - - CurrentVisibility = vis; - - if (FocalTangent == 0 || FocalLengthY == 0) - { // If r_visibility is called before the renderer is all set up, don't - // divide by zero. This will be called again later, and the proper - // values can be initialized then. - return; - } - - r_BaseVisibility = vis; - - // Prevent overflow on walls - if (r_BaseVisibility < 0 && r_BaseVisibility < -MaxVisForWall) - r_WallVisibility = -MaxVisForWall; - else if (r_BaseVisibility > 0 && r_BaseVisibility > MaxVisForWall) - r_WallVisibility = MaxVisForWall; - else - r_WallVisibility = r_BaseVisibility; - - r_WallVisibility = (InvZtoScale * SCREENWIDTH*AspectBaseHeight(WidescreenRatio) / - (viewwidth*SCREENHEIGHT*3)) * (r_WallVisibility * FocalTangent); - - // Prevent overflow on floors/ceilings. Note that the calculation of - // MaxVisForFloor means that planes less than two units from the player's - // view could still overflow, but there is no way to totally eliminate - // that while still using fixed point math. - if (r_BaseVisibility < 0 && r_BaseVisibility < -MaxVisForFloor) - r_FloorVisibility = -MaxVisForFloor; - else if (r_BaseVisibility > 0 && r_BaseVisibility > MaxVisForFloor) - r_FloorVisibility = MaxVisForFloor; - else - r_FloorVisibility = r_BaseVisibility; - - r_FloorVisibility = 160.0 * r_FloorVisibility / FocalLengthY; - - r_TiltVisibility = float(vis * FocalTangent * (16.f * 320.f) / viewwidth); - r_SpriteVisibility = r_WallVisibility; -} - -//========================================================================== -// -// R_GetVisibility -// -//========================================================================== - -double R_GetVisibility() -{ - return CurrentVisibility; -} - -//========================================================================== -// -// CCMD r_visibility -// -// Controls how quickly light ramps across a 1/z range. Set this, and it -// sets all the r_*Visibility variables (except r_SkyVisibilily, which is -// currently unused). -// -//========================================================================== - -CCMD (r_visibility) -{ - if (argv.argc() < 2) - { - Printf ("Visibility is %g\n", R_GetVisibility()); - } - else if (!netgame) - { - R_SetVisibility(atof(argv[1])); - } - else - { - Printf ("Visibility cannot be changed in net games.\n"); - } -} - -//========================================================================== -// -// R_SetWindow -// -//========================================================================== - -void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) -{ - int virtheight, virtwidth, virtwidth2, virtheight2; - - if (!bRenderingToCanvas) - { // Set r_viewsize cvar to reflect the current view size - UCVarValue value; - char temp[16]; - - mysnprintf (temp, countof(temp), "%d x %d", viewwidth, viewheight); - value.String = temp; - r_viewsize.ForceSet (value, CVAR_String); - } - - fuzzviewheight = viewheight - 2; // Maximum row the fuzzer can draw to - - lastcenteryfrac = 1<<30; - CenterX = centerx; - CenterY = centery; - - virtwidth = virtwidth2 = fullWidth; - virtheight = virtheight2 = fullHeight; - - if (AspectTallerThanWide(trueratio)) - { - virtheight2 = virtheight2 * AspectMultiplier(trueratio) / 48; - } - else - { - virtwidth2 = virtwidth2 * AspectMultiplier(trueratio) / 48; - } - - if (AspectTallerThanWide(WidescreenRatio)) - { - virtheight = virtheight * AspectMultiplier(WidescreenRatio) / 48; - } - else - { - virtwidth = virtwidth * AspectMultiplier(WidescreenRatio) / 48; - } - - BaseYaspectMul = 320.0 * virtheight2 / (r_Yaspect * virtwidth2); - YaspectMul = 320.0 * virtheight / (r_Yaspect * virtwidth); - IYaspectMul = (double)virtwidth * r_Yaspect / 320.0 / virtheight; - InvZtoScale = YaspectMul * CenterX; - - WallTMapScale2 = IYaspectMul / CenterX; - - // psprite scales - pspritexscale = centerxwide / 160.0; - pspriteyscale = pspritexscale * YaspectMul; - pspritexiscale = 1 / pspritexscale; - - // thing clipping - fillshort (screenheightarray, viewwidth, (short)viewheight); - - R_InitTextureMapping (); - - MaxVisForWall = (InvZtoScale * (SCREENWIDTH*r_Yaspect) / - (viewwidth*SCREENHEIGHT * FocalTangent)); - MaxVisForWall = 32767.0 / MaxVisForWall; - MaxVisForFloor = 32767.0 / (viewheight >> 2) * FocalLengthY / 160; - - // Reset r_*Visibility vars - R_SetVisibility(R_GetVisibility()); -} - -//========================================================================== -// -// R_Init -// -//========================================================================== - -void R_InitRenderer() -{ - atterm(R_ShutdownRenderer); - // viewwidth / viewheight are set by the defaults - fillshort (zeroarray, MAXWIDTH, 0); - - R_InitPlanes (); - R_InitShadeMaps(); - R_InitColumnDrawers (); - - colfunc = basecolfunc = R_DrawColumn; - fuzzcolfunc = R_DrawFuzzColumn; - transcolfunc = R_DrawTranslatedColumn; - spanfunc = R_DrawSpan; - - // [RH] Horizontal column drawers - hcolfunc_pre = R_DrawColumnHoriz; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; -} - -//========================================================================== -// -// R_ShutdownRenderer -// -//========================================================================== - -static void R_ShutdownRenderer() -{ - R_DeinitSprites(); - R_DeinitPlanes(); - // Free openings - if (openings != NULL) - { - M_Free (openings); - openings = NULL; - } - - // Free drawsegs - if (drawsegs != NULL) - { - M_Free (drawsegs); - drawsegs = NULL; - } -} - -//========================================================================== -// -// R_CopyStackedViewParameters -// -//========================================================================== - -void R_CopyStackedViewParameters() -{ - stacked_viewpos = ViewPos; - stacked_angle = ViewAngle; - stacked_extralight = extralight; - stacked_visibility = R_GetVisibility(); -} - -//========================================================================== -// -// R_SetupColormap -// -// Sets up special fixed colormaps -// -//========================================================================== - -void R_SetupColormap(player_t *player) -{ - realfixedcolormap = NULL; - fixedcolormap = NULL; - fixedlightlev = -1; - - if (player != NULL && camera == player->mo) - { - if (player->fixedcolormap >= 0 && player->fixedcolormap < (int)SpecialColormaps.Size()) - { - realfixedcolormap = &SpecialColormaps[player->fixedcolormap]; - if (RenderTarget == screen && (DFrameBuffer *)screen->Accel2D && r_shadercolormaps) - { - // Render everything fullbright. The copy to video memory will - // apply the special colormap, so it won't be restricted to the - // palette. - fixedcolormap = realcolormaps; - } - else - { - fixedcolormap = SpecialColormaps[player->fixedcolormap].Colormap; - } - } - else if (player->fixedlightlevel >= 0 && player->fixedlightlevel < NUMCOLORMAPS) - { - fixedlightlev = player->fixedlightlevel * 256; - // [SP] Emulate GZDoom's light-amp goggles. - if (r_fullbrightignoresectorcolor && fixedlightlev >= 0) - { - fixedcolormap = FullNormalLight.Maps; - } - } - } - // [RH] Inverse light for shooting the Sigil - if (fixedcolormap == NULL && extralight == INT_MIN) - { - fixedcolormap = SpecialColormaps[INVERSECOLORMAP].Colormap; - extralight = 0; - } -} - -//========================================================================== -// -// R_SetupFreelook -// -// [RH] freelook stuff -// -//========================================================================== - -void R_SetupFreelook() -{ - double dy; - - if (camera != NULL) - { - dy = FocalLengthY * (-ViewPitch).Tan(); - } - else - { - dy = 0; - } - - CenterY = (viewheight / 2.0) + dy; - centery = xs_ToInt(CenterY); - globaluclip = -CenterY / InvZtoScale; - globaldclip = (viewheight - CenterY) / InvZtoScale; - - //centeryfrac &= 0xffff0000; - int e, i; - - i = 0; - e = viewheight; - float focus = float(FocalLengthY); - float den; - float cy = float(CenterY); - if (i < centery) - { - den = cy - i - 0.5f; - if (e <= centery) - { - do { - yslope[i] = focus / den; - den -= 1; - } while (++i < e); - } - else - { - do { - yslope[i] = focus / den; - den -= 1; - } while (++i < centery); - den = i - cy + 0.5f; - do { - yslope[i] = focus / den; - den += 1; - } while (++i < e); - } - } - else - { - den = i - cy + 0.5f; - do { - yslope[i] = focus / den; - den += 1; - } while (++i < e); - } -} - -//========================================================================== -// -// R_EnterPortal -// -// [RH] Draw the reflection inside a mirror -// [ZZ] Merged with portal code, originally called R_EnterMirror -// -//========================================================================== - -void R_HighlightPortal (PortalDrawseg* pds) -{ - // [ZZ] NO OVERFLOW CHECKS HERE - // I believe it won't break. if it does, blame me. :( - - BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 255, 0, 0, 0, 255); - - BYTE* pixels = RenderTarget->GetBuffer(); - // top edge - for (int x = pds->x1; x < pds->x2; x++) - { - if (x < 0 || x >= RenderTarget->GetWidth()) - continue; - - int p = x - pds->x1; - int Ytop = pds->ceilingclip[p]; - int Ybottom = pds->floorclip[p]; - - if (x == pds->x1 || x == pds->x2-1) - { - RenderTarget->DrawLine(x, Ytop, x, Ybottom+1, color, 0); - continue; - } - - int YtopPrev = pds->ceilingclip[p-1]; - int YbottomPrev = pds->floorclip[p-1]; - - if (abs(Ytop-YtopPrev) > 1) - RenderTarget->DrawLine(x, YtopPrev, x, Ytop, color, 0); - else *(pixels + Ytop * RenderTarget->GetPitch() + x) = color; - - if (abs(Ybottom-YbottomPrev) > 1) - RenderTarget->DrawLine(x, YbottomPrev, x, Ybottom, color, 0); - else *(pixels + Ybottom * RenderTarget->GetPitch() + x) = color; - } -} - -void R_EnterPortal (PortalDrawseg* pds, int depth) -{ - // [ZZ] check depth. fill portal with black if it's exceeding the visual recursion limit, and continue like nothing happened. - if (depth >= r_portal_recursions) - { - BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 0, 0, 0, 0, 255); - int spacing = RenderTarget->GetPitch(); - for (int x = pds->x1; x < pds->x2; x++) - { - if (x < 0 || x >= RenderTarget->GetWidth()) - continue; - - int Ytop = pds->ceilingclip[x-pds->x1]; - int Ybottom = pds->floorclip[x-pds->x1]; - - BYTE *dest = RenderTarget->GetBuffer() + x + Ytop * spacing; - - for (int y = Ytop; y <= Ybottom; y++) - { - *dest = color; - dest += spacing; - } - } - - if (r_highlight_portals) - R_HighlightPortal(pds); - - return; - } - - DAngle startang = ViewAngle; - DVector3 startpos = ViewPos; - DVector3 savedpath[2] = { ViewPath[0], ViewPath[1] }; - ActorRenderFlags savedvisibility = camera? camera->renderflags & RF_INVISIBLE : ActorRenderFlags::FromInt(0); - - CurrentPortalUniq++; - - unsigned int portalsAtStart = WallPortals.Size (); - - if (pds->mirror) - { - //vertex_t *v1 = ds->curline->v1; - vertex_t *v1 = pds->src->v1; - - // Reflect the current view behind the mirror. - if (pds->src->Delta().X == 0) - { // vertical mirror - ViewPos.X = v1->fX() - startpos.X + v1->fX(); - } - else if (pds->src->Delta().Y == 0) - { // horizontal mirror - ViewPos.Y = v1->fY() - startpos.Y + v1->fY(); - } - else - { // any mirror - vertex_t *v2 = pds->src->v2; - - double dx = v2->fX() - v1->fX(); - double dy = v2->fY() - v1->fY(); - double x1 = v1->fX(); - double y1 = v1->fY(); - double x = startpos.X; - double y = startpos.Y; - - // the above two cases catch len == 0 - double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); - - ViewPos.X = (x1 + r * dx)*2 - x; - ViewPos.Y = (y1 + r * dy)*2 - y; - } - ViewAngle = pds->src->Delta().Angle() * 2 - startang; - } - else - { - P_TranslatePortalXY(pds->src, ViewPos.X, ViewPos.Y); - P_TranslatePortalZ(pds->src, ViewPos.Z); - P_TranslatePortalAngle(pds->src, ViewAngle); - P_TranslatePortalXY(pds->src, ViewPath[0].X, ViewPath[0].Y); - P_TranslatePortalXY(pds->src, ViewPath[1].X, ViewPath[1].Y); - - if (!r_showviewer && camera && P_PointOnLineSidePrecise(ViewPath[0], pds->dst) != P_PointOnLineSidePrecise(ViewPath[1], pds->dst)) - { - double distp = (ViewPath[0] - ViewPath[1]).Length(); - if (distp > EQUAL_EPSILON) - { - double dist1 = (ViewPos - ViewPath[0]).Length(); - double dist2 = (ViewPos - ViewPath[1]).Length(); - - if (dist1 + dist2 < distp + 1) - { - camera->renderflags |= RF_INVISIBLE; - } - } - } - } - - ViewSin = ViewAngle.Sin(); - ViewCos = ViewAngle.Cos(); - - ViewTanSin = FocalTangent * ViewSin; - ViewTanCos = FocalTangent * ViewCos; - - R_CopyStackedViewParameters(); - - validcount++; - PortalDrawseg* prevpds = CurrentPortal; - CurrentPortal = pds; - - R_ClearPlanes (false); - R_ClearClipSegs (pds->x1, pds->x2); - - WindowLeft = pds->x1; - WindowRight = pds->x2; - - // RF_XFLIP should be removed before calling the root function - int prevmf = MirrorFlags; - if (pds->mirror) - { - if (MirrorFlags & RF_XFLIP) - MirrorFlags &= ~RF_XFLIP; - else MirrorFlags |= RF_XFLIP; - } - - // some portals have height differences, account for this here - R_3D_EnterSkybox(); // push 3D floor height map - CurrentPortalInSkybox = false; // first portal in a skybox should set this variable to false for proper clipping in skyboxes. - - // first pass, set clipping - memcpy (ceilingclip + pds->x1, &pds->ceilingclip[0], pds->len*sizeof(*ceilingclip)); - memcpy (floorclip + pds->x1, &pds->floorclip[0], pds->len*sizeof(*floorclip)); - - InSubsector = NULL; - R_RenderBSPNode (nodes + numnodes - 1); - R_3D_ResetClip(); // reset clips (floor/ceiling) - if (!savedvisibility && camera) camera->renderflags &= ~RF_INVISIBLE; - - PlaneCycles.Clock(); - R_DrawPlanes (); - R_DrawPortals (); - PlaneCycles.Unclock(); - - double vzp = ViewPos.Z; - - int prevuniq = CurrentPortalUniq; - // depth check is in another place right now - unsigned int portalsAtEnd = WallPortals.Size (); - for (; portalsAtStart < portalsAtEnd; portalsAtStart++) - { - R_EnterPortal (&WallPortals[portalsAtStart], depth + 1); - } - int prevuniq2 = CurrentPortalUniq; - CurrentPortalUniq = prevuniq; - - NetUpdate(); - - MaskedCycles.Clock(); // [ZZ] count sprites in portals/mirrors along with normal ones. - R_DrawMasked (); // this is required since with portals there often will be cases when more than 80% of the view is inside a portal. - MaskedCycles.Unclock(); - - NetUpdate(); - - R_3D_LeaveSkybox(); // pop 3D floor height map - CurrentPortalUniq = prevuniq2; - - // draw a red line around a portal if it's being highlighted - if (r_highlight_portals) - R_HighlightPortal(pds); - - CurrentPortal = prevpds; - MirrorFlags = prevmf; - ViewAngle = startang; - ViewPos = startpos; - ViewPath[0] = savedpath[0]; - ViewPath[1] = savedpath[1]; -} - -//========================================================================== -// -// R_SetupBuffer -// -// Precalculate all row offsets and fuzz table. -// -//========================================================================== - -void R_SetupBuffer () -{ - static BYTE *lastbuff = NULL; - - int pitch = RenderTarget->GetPitch(); - BYTE *lineptr = RenderTarget->GetBuffer() + viewwindowy*pitch + viewwindowx; - - if (dc_pitch != pitch || lineptr != lastbuff) - { - if (dc_pitch != pitch) - { - dc_pitch = pitch; - R_InitFuzzTable (pitch); - } - dc_destorg = lineptr; - for (int i = 0; i < RenderTarget->GetHeight(); i++) - { - ylookup[i] = i * pitch; - } - } -} - -//========================================================================== -// -// R_RenderActorView -// -//========================================================================== - -void R_RenderActorView (AActor *actor, bool dontmaplines) -{ - WallCycles.Reset(); - PlaneCycles.Reset(); - MaskedCycles.Reset(); - WallScanCycles.Reset(); - - fakeActive = 0; // kg3D - reset fake floor indicator - R_3D_ResetClip(); // reset clips (floor/ceiling) - - R_SetupBuffer (); - R_SetupFrame (actor); - - // Clear buffers. - R_ClearClipSegs (0, viewwidth); - R_ClearDrawSegs (); - R_ClearPlanes (true); - R_ClearSprites (); - - NetUpdate (); - - // [RH] Show off segs if r_drawflat is 1 - if (r_drawflat) - { - hcolfunc_pre = R_FillColumnHoriz; - hcolfunc_post1 = rt_copy1col; - hcolfunc_post4 = rt_copy4cols; - colfunc = R_FillColumn; - spanfunc = R_FillSpan; - } - else - { - hcolfunc_pre = R_DrawColumnHoriz; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; - colfunc = basecolfunc; - spanfunc = R_DrawSpan; - } - - WindowLeft = 0; - WindowRight = viewwidth; - MirrorFlags = 0; - CurrentPortal = NULL; - CurrentPortalUniq = 0; - - r_dontmaplines = dontmaplines; - - // [RH] Hack to make windows into underwater areas possible - r_fakingunderwater = false; - - // [RH] Setup particles for this frame - P_FindParticleSubsectors (); - - WallCycles.Clock(); - ActorRenderFlags savedflags = camera->renderflags; - // Never draw the player unless in chasecam mode - if (!r_showviewer) - { - camera->renderflags |= RF_INVISIBLE; - } - // Link the polyobjects right before drawing the scene to reduce the amounts of calls to this function - PO_LinkToSubsectors(); - InSubsector = NULL; - R_RenderBSPNode (nodes + numnodes - 1); // The head node is the last node output. - R_3D_ResetClip(); // reset clips (floor/ceiling) - camera->renderflags = savedflags; - WallCycles.Unclock(); - - NetUpdate (); - - if (viewactive) - { - PlaneCycles.Clock(); - R_DrawPlanes (); - R_DrawPortals (); - PlaneCycles.Unclock(); - - // [RH] Walk through mirrors - // [ZZ] Merged with portals - size_t lastportal = WallPortals.Size(); - for (unsigned int i = 0; i < lastportal; i++) - { - R_EnterPortal(&WallPortals[i], 0); - } - - CurrentPortal = NULL; - CurrentPortalUniq = 0; - - NetUpdate (); - - MaskedCycles.Clock(); - R_DrawMasked (); - MaskedCycles.Unclock(); - - NetUpdate (); - } - WallPortals.Clear (); - interpolator.RestoreInterpolations (); - R_SetupBuffer (); - - // If we don't want shadered colormaps, NULL it now so that the - // copy to the screen does not use a special colormap shader. - if (!r_shadercolormaps) - { - realfixedcolormap = NULL; - } -} - -//========================================================================== -// -// R_RenderViewToCanvas -// -// Pre: Canvas is already locked. -// -//========================================================================== - -void R_RenderViewToCanvas (AActor *actor, DCanvas *canvas, - int x, int y, int width, int height, bool dontmaplines) -{ - const bool savedviewactive = viewactive; - - R_BeginDrawerCommands(); - - viewwidth = width; - RenderTarget = canvas; - bRenderingToCanvas = true; - - R_SetWindow (12, width, height, height, true); - viewwindowx = x; - viewwindowy = y; - viewactive = true; - - R_RenderActorView (actor, dontmaplines); - - R_EndDrawerCommands(); - - RenderTarget = screen; - bRenderingToCanvas = false; - R_ExecuteSetViewSize (); - screen->Lock (true); - R_SetupBuffer (); - screen->Unlock (); - viewactive = savedviewactive; -} - -//========================================================================== -// -// R_MultiresInit -// -// Called from V_SetResolution() -// -//========================================================================== - -void R_MultiresInit () -{ - R_PlaneInitData (); -} - - -//========================================================================== -// -// STAT fps -// -// Displays statistics about rendering times -// -//========================================================================== - -ADD_STAT (fps) -{ - FString out; - out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms", - FrameCycles.TimeMS(), WallCycles.TimeMS(), PlaneCycles.TimeMS(), MaskedCycles.TimeMS()); - return out; -} - - -static double f_acc, w_acc,p_acc,m_acc; -static int acc_c; - -ADD_STAT (fps_accumulated) -{ - f_acc += FrameCycles.TimeMS(); - w_acc += WallCycles.TimeMS(); - p_acc += PlaneCycles.TimeMS(); - m_acc += MaskedCycles.TimeMS(); - acc_c++; - FString out; - out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms %d counts", - f_acc/acc_c, w_acc/acc_c, p_acc/acc_c, m_acc/acc_c, acc_c); - Printf(PRINT_LOG, "%s\n", out.GetChars()); - return out; -} - -//========================================================================== -// -// STAT wallcycles -// -// Displays the minimum number of cycles spent drawing walls -// -//========================================================================== - -static double bestwallcycles = HUGE_VAL; - -ADD_STAT (wallcycles) -{ - FString out; - double cycles = WallCycles.Time(); - if (cycles && cycles < bestwallcycles) - bestwallcycles = cycles; - out.Format ("%g", bestwallcycles); - return out; -} - -//========================================================================== -// -// CCMD clearwallcycles -// -// Resets the count of minimum wall drawing cycles -// -//========================================================================== - -CCMD (clearwallcycles) -{ - bestwallcycles = HUGE_VAL; -} - -#if 0 -// The replacement code for Build's wallscan doesn't have any timing calls so this does not work anymore. -static double bestscancycles = HUGE_VAL; - -ADD_STAT (scancycles) -{ - FString out; - double scancycles = WallScanCycles.Time(); - if (scancycles && scancycles < bestscancycles) - bestscancycles = scancycles; - out.Format ("%g", bestscancycles); - return out; -} - -CCMD (clearscancycles) -{ - bestscancycles = HUGE_VAL; -} -#endif - -} \ No newline at end of file diff --git a/src/r_main.h b/src/r_main.h deleted file mode 100644 index be1c36306d..0000000000 --- a/src/r_main.h +++ /dev/null @@ -1,153 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// System specific interface stuff. -// -//----------------------------------------------------------------------------- - - -#ifndef __R_MAIN_H__ -#define __R_MAIN_H__ - -#include "r_utility.h" -#include "d_player.h" -#include "v_palette.h" -#include "r_data/colormaps.h" - -extern double ViewCos; -extern double ViewSin; -extern int viewwindowx; -extern int viewwindowy; - -typedef BYTE lighttable_t; // This could be wider for >8 bit display. - -namespace swrenderer -{ - -// -// POV related. -// -extern bool bRenderingToCanvas; -extern fixed_t viewingrangerecip; -extern double FocalLengthX, FocalLengthY; -extern double InvZtoScale; - -extern double WallTMapScale2; - - -extern double CenterX; -extern double CenterY; -extern double YaspectMul; -extern double IYaspectMul; - -extern FDynamicColormap*basecolormap; // [RH] Colormap for sector currently being drawn - -extern int linecount; -extern int loopcount; - -extern bool r_dontmaplines; - -// -// Lighting. -// -// [RH] This has changed significantly from Doom, which used lookup -// tables based on 1/z for walls and z for flats and only recognized -// 16 discrete light levels. The terminology I use is borrowed from Build. -// - -// The size of a single colormap, in bits -#define COLORMAPSHIFT 8 - -// Convert a light level into an unbounded colormap index (shade). Result is -// fixed point. Why the +12? I wish I knew, but experimentation indicates it -// is necessary in order to best reproduce Doom's original lighting. -#define LIGHT2SHADE(l) ((NUMCOLORMAPS*2*FRACUNIT)-(((l)+12)*(FRACUNIT*NUMCOLORMAPS/128))) - -// MAXLIGHTSCALE from original DOOM, divided by 2. -#define MAXLIGHTVIS (24.0) - -// Convert a shade and visibility to a clamped colormap index. -// Result is not fixed point. -// Change R_CalcTiltedLighting() when this changes. -#define GETPALOOKUP(vis,shade) (clamp (((shade)-FLOAT2FIXED(MIN(MAXLIGHTVIS,double(vis))))>>FRACBITS, 0, NUMCOLORMAPS-1)) - -// Converts fixedlightlev into a shade value -#define FIXEDLIGHT2SHADE(lightlev) (((lightlev) >> COLORMAPSHIFT) << FRACBITS) - -extern double GlobVis; - -void R_SetVisibility(double visibility); -double R_GetVisibility(); - -extern double r_BaseVisibility; -extern double r_WallVisibility; -extern double r_FloorVisibility; -extern float r_TiltVisibility; -extern double r_SpriteVisibility; - -extern int r_actualextralight; -extern bool foggy; -extern int fixedlightlev; -extern lighttable_t* fixedcolormap; -extern FSpecialColormap*realfixedcolormap; - - -// -// Function pointers to switch refresh/drawing functions. -// Used to select shadow mode etc. -// -extern void (*colfunc) (void); -extern void (*basecolfunc) (void); -extern void (*fuzzcolfunc) (void); -extern void (*transcolfunc) (void); -// No shadow effects on floors. -extern void (*spanfunc) (void); - -// [RH] Function pointers for the horizontal column drawers. -extern void (*hcolfunc_pre) (void); -extern void (*hcolfunc_post1) (int hx, int sx, int yl, int yh); -extern void (*hcolfunc_post2) (int hx, int sx, int yl, int yh); -extern void (*hcolfunc_post4) (int sx, int yl, int yh); - - -void R_InitTextureMapping (); - - -// -// REFRESH - the actual rendering functions. -// - -// Called by G_Drawer. -void R_RenderActorView (AActor *actor, bool dontmaplines = false); -void R_SetupBuffer (); - -void R_RenderViewToCanvas (AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines = false); - -// [RH] Initialize multires stuff for renderer -void R_MultiresInit (void); - - -extern int stacked_extralight; -extern double stacked_visibility; -extern DVector3 stacked_viewpos; -extern DAngle stacked_angle; - -extern void R_CopyStackedViewParameters(); - -} - -#endif // __R_MAIN_H__ diff --git a/src/r_plane.cpp b/src/r_plane.cpp deleted file mode 100644 index cc62d18a94..0000000000 --- a/src/r_plane.cpp +++ /dev/null @@ -1,1789 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// $Log:$ -// -// DESCRIPTION: -// Here is a core component: drawing the floors and ceilings, -// while maintaining a per column clipping list only. -// Moreover, the sky areas have to be determined. -// -// MAXVISPLANES is no longer a limit on the number of visplanes, -// but a limit on the number of hash slots; larger numbers mean -// better performance usually but after a point they are wasted, -// and memory and time overheads creep in. -// -// Lee Killough -// -// [RH] Further modified to significantly increase accuracy and add slopes. -// -//----------------------------------------------------------------------------- - -#include -#include - -#include "templates.h" -#include "i_system.h" -#include "w_wad.h" - -#include "doomdef.h" -#include "doomstat.h" - -#include "r_local.h" -#include "r_sky.h" -#include "stats.h" - -#include "v_video.h" -#include "a_sharedglobal.h" -#include "c_console.h" -#include "cmdlib.h" -#include "d_net.h" -#include "g_level.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "v_palette.h" -#include "r_data/colormaps.h" -#include "g_levellocals.h" -#include "events.h" - -#ifdef _MSC_VER -#pragma warning(disable:4244) -#endif - -CVAR(Bool, tilt, false, 0); -CVAR(Bool, r_skyboxes, true, 0) - -EXTERN_CVAR(Int, r_skymode) - -namespace swrenderer -{ - using namespace drawerargs; - -extern subsector_t *InSubsector; - -static void R_DrawSkyStriped (visplane_t *pl); - -planefunction_t floorfunc; -planefunction_t ceilingfunc; - -// Here comes the obnoxious "visplane". -#define MAXVISPLANES 128 /* must be a power of 2 */ - -// Avoid infinite recursion with stacked sectors by limiting them. -#define MAX_SKYBOX_PLANES 1000 - -// [RH] Allocate one extra for sky box planes. -static visplane_t *visplanes[MAXVISPLANES+1]; // killough -static visplane_t *freetail; // killough -static visplane_t **freehead = &freetail; // killough - -visplane_t *floorplane; -visplane_t *ceilingplane; - -// killough -- hash function for visplanes -// Empirically verified to be fairly uniform: - -#define visplane_hash(picnum,lightlevel,height) \ - ((unsigned)((picnum)*3+(lightlevel)+(FLOAT2FIXED((height).fD()))*7) & (MAXVISPLANES-1)) - -// These are copies of the main parameters used when drawing stacked sectors. -// When you change the main parameters, you should copy them here too *unless* -// you are changing them to draw a stacked sector. Otherwise, stacked sectors -// won't draw in skyboxes properly. -int stacked_extralight; -double stacked_visibility; -DVector3 stacked_viewpos; -DAngle stacked_angle; - - -// -// opening -// - -size_t maxopenings; -short *openings; -ptrdiff_t lastopening; - -// -// Clip values are the solid pixel bounding the range. -// floorclip starts out SCREENHEIGHT and is just outside the range -// ceilingclip starts out 0 and is just inside the range -// -short floorclip[MAXWIDTH]; -short ceilingclip[MAXWIDTH]; - -// -// texture mapping -// - -static double planeheight; - -extern "C" { -// -// spanend holds the end of a plane span in each screen row -// -short spanend[MAXHEIGHT]; - -int planeshade; -FVector3 plane_sz, plane_su, plane_sv; -float planelightfloat; -bool plane_shade; -fixed_t pviewx, pviewy; -} - -float yslope[MAXHEIGHT]; -static fixed_t xscale, yscale; -static double xstepscale, ystepscale; -static double basexfrac, baseyfrac; - -void R_DrawSinglePlane (visplane_t *, fixed_t alpha, bool additive, bool masked); -void R_DrawSkySegment(visplane_t *vis, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const uint8_t *(*getcol)(FTexture *tex, int col)); - -//========================================================================== -// -// R_InitPlanes -// -// Called at game startup. -// -//========================================================================== - -void R_InitPlanes () -{ -} - -//========================================================================== -// -// R_DeinitPlanes -// -//========================================================================== - -void R_DeinitPlanes () -{ - fakeActive = 0; - - // do not use R_ClearPlanes because at this point the screen pointer is no longer valid. - for (int i = 0; i <= MAXVISPLANES; i++) // new code -- killough - { - for (*freehead = visplanes[i], visplanes[i] = NULL; *freehead; ) - { - freehead = &(*freehead)->next; - } - } - for (visplane_t *pl = freetail; pl != NULL; ) - { - visplane_t *next = pl->next; - free (pl); - pl = next; - } -} - -//========================================================================== -// -// R_MapPlane -// -// Globals used: planeheight, ds_source, basexscale, baseyscale, -// pviewx, pviewy, xoffs, yoffs, basecolormap, xscale, yscale. -// -//========================================================================== - -void R_MapPlane (int y, int x1) -{ - int x2 = spanend[y]; - double distance; - -#ifdef RANGECHECK - if (x2 < x1 || x1<0 || x2>=viewwidth || (unsigned)y>=(unsigned)viewheight) - { - I_FatalError ("R_MapPlane: %i, %i at %i", x1, x2, y); - } -#endif - - // [RH] Notice that I dumped the caching scheme used by Doom. - // It did not offer any appreciable speedup. - - distance = planeheight * yslope[y]; - - if (ds_xbits != 0) - { - ds_xstep = xs_ToFixed(32 - ds_xbits, distance * xstepscale); - ds_xfrac = xs_ToFixed(32 - ds_xbits, distance * basexfrac) + pviewx; - } - else - { - ds_xstep = 0; - ds_xfrac = 0; - } - if (ds_ybits != 0) - { - ds_ystep = xs_ToFixed(32 - ds_ybits, distance * ystepscale); - ds_yfrac = xs_ToFixed(32 - ds_ybits, distance * baseyfrac) + pviewy; - } - else - { - ds_ystep = 0; - ds_yfrac = 0; - } - - if (plane_shade) - { - // Determine lighting based on the span's distance from the viewer. - R_SetDSColorMapLight(basecolormap, GlobVis * fabs(CenterY - y), planeshade); - } - - ds_y = y; - ds_x1 = x1; - ds_x2 = x2; - - spanfunc (); -} - -//========================================================================== -// -// R_MapTiltedPlane -// -//========================================================================== - -void R_MapTiltedPlane (int y, int x1) -{ - R_DrawTiltedSpan(y, x1, spanend[y], plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy); -} - -//========================================================================== -// -// R_MapColoredPlane -// -//========================================================================== - -void R_MapColoredPlane(int y, int x1) -{ - R_DrawColoredSpan(y, x1, spanend[y]); -} - -//========================================================================== -// -// R_ClearPlanes -// -// Called at the beginning of each frame. -// -//========================================================================== - -void R_ClearPlanes (bool fullclear) -{ - int i; - - // Don't clear fake planes if not doing a full clear. - if (!fullclear) - { - for (i = 0; i <= MAXVISPLANES-1; i++) // new code -- killough - { - for (visplane_t **probe = &visplanes[i]; *probe != NULL; ) - { - if ((*probe)->sky < 0) - { // fake: move past it - probe = &(*probe)->next; - } - else - { // not fake: move to freelist - visplane_t *vis = *probe; - *freehead = vis; - *probe = vis->next; - vis->next = NULL; - freehead = &vis->next; - } - } - } - } - else - { - for (i = 0; i <= MAXVISPLANES; i++) // new code -- killough - { - for (*freehead = visplanes[i], visplanes[i] = NULL; *freehead; ) - { - freehead = &(*freehead)->next; - } - } - - // opening / clipping determination - fillshort (floorclip, viewwidth, viewheight); - // [RH] clip ceiling to console bottom - fillshort (ceilingclip, viewwidth, - !screen->Accel2D && ConBottom > viewwindowy && !bRenderingToCanvas - ? (ConBottom - viewwindowy) : 0); - - lastopening = 0; - } -} - -//========================================================================== -// -// new_visplane -// -// New function, by Lee Killough -// [RH] top and bottom buffers get allocated immediately after the visplane. -// -//========================================================================== - -static visplane_t *new_visplane (unsigned hash) -{ - visplane_t *check = freetail; - - if (check == NULL) - { - check = (visplane_t *)M_Malloc (sizeof(*check) + 3 + sizeof(*check->top)*(MAXWIDTH*2)); - memset(check, 0, sizeof(*check) + 3 + sizeof(*check->top)*(MAXWIDTH*2)); - check->bottom = check->top + MAXWIDTH+2; - } - else if (NULL == (freetail = freetail->next)) - { - freehead = &freetail; - } - - check->next = visplanes[hash]; - visplanes[hash] = check; - return check; -} - - -//========================================================================== -// -// R_FindPlane -// -// killough 2/28/98: Add offsets -//========================================================================== - -visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightlevel, double Alpha, bool additive, - const FTransform &xxform, - int sky, FSectorPortal *portal) -{ - secplane_t plane; - visplane_t *check; - unsigned hash; // killough - bool isskybox; - const FTransform *xform = &xxform; - fixed_t alpha = FLOAT2FIXED(Alpha); - //angle_t angle = (xform.Angle + xform.baseAngle).BAMs(); - - FTransform nulltransform; - - if (picnum == skyflatnum) // killough 10/98 - { // most skies map together - lightlevel = 0; - xform = &nulltransform; - nulltransform.xOffs = nulltransform.yOffs = nulltransform.baseyOffs = 0; - nulltransform.xScale = nulltransform.yScale = 1; - nulltransform.Angle = nulltransform.baseAngle = 0.0; - additive = false; - // [RH] Map floor skies and ceiling skies to separate visplanes. This isn't - // always necessary, but it is needed if a floor and ceiling sky are in the - // same column but separated by a wall. If they both try to reside in the - // same visplane, then only the floor sky will be drawn. - plane.set(0., 0., height.fC(), 0.); - isskybox = portal != NULL && !(portal->mFlags & PORTSF_INSKYBOX); - } - else if (portal != NULL && !(portal->mFlags & PORTSF_INSKYBOX)) - { - plane = height; - isskybox = true; - } - else - { - plane = height; - isskybox = false; - // kg3D - hack, store alpha in sky - // i know there is ->alpha, but this also allows to identify fake plane - // and ->alpha is for stacked sectors - if (fake3D & (FAKE3D_FAKEFLOOR|FAKE3D_FAKECEILING)) sky = 0x80000000 | fakeAlpha; - else sky = 0; // not skyflatnum so it can't be a sky - portal = NULL; - alpha = OPAQUE; - } - - // New visplane algorithm uses hash table -- killough - hash = isskybox ? MAXVISPLANES : visplane_hash (picnum.GetIndex(), lightlevel, height); - - for (check = visplanes[hash]; check; check = check->next) // killough - { - if (isskybox) - { - if (portal == check->portal && plane == check->height) - { - if (portal->mType != PORTS_SKYVIEWPOINT) - { // This skybox is really a stacked sector, so we need to - // check even more. - if (check->extralight == stacked_extralight && - check->visibility == stacked_visibility && - check->viewpos == stacked_viewpos && - ( - // headache inducing logic... :( - (portal->mType != PORTS_STACKEDSECTORTHING) || - ( - check->Alpha == alpha && - check->Additive == additive && - (alpha == 0 || // if alpha is > 0 everything needs to be checked - (plane == check->height && - picnum == check->picnum && - lightlevel == check->lightlevel && - basecolormap == check->colormap && // [RH] Add more checks - *xform == check->xform - ) - ) && - check->viewangle == stacked_angle - ) - ) - ) - { - return check; - } - } - else - { - return check; - } - } - } - else - if (plane == check->height && - picnum == check->picnum && - lightlevel == check->lightlevel && - basecolormap == check->colormap && // [RH] Add more checks - *xform == check->xform && - sky == check->sky && - CurrentPortalUniq == check->CurrentPortalUniq && - MirrorFlags == check->MirrorFlags && - CurrentSkybox == check->CurrentSkybox && - ViewPos == check->viewpos - ) - { - return check; - } - } - - check = new_visplane (hash); // killough - - check->height = plane; - check->picnum = picnum; - check->lightlevel = lightlevel; - check->xform = *xform; - check->colormap = basecolormap; // [RH] Save colormap - check->sky = sky; - check->portal = portal; - check->left = viewwidth; // Was SCREENWIDTH -- killough 11/98 - check->right = 0; - check->extralight = stacked_extralight; - check->visibility = stacked_visibility; - check->viewpos = stacked_viewpos; - check->viewangle = stacked_angle; - check->Alpha = alpha; - check->Additive = additive; - check->CurrentPortalUniq = CurrentPortalUniq; - check->MirrorFlags = MirrorFlags; - check->CurrentSkybox = CurrentSkybox; - - fillshort (check->top, viewwidth, 0x7fff); - - return check; -} - -//========================================================================== -// -// R_CheckPlane -// -//========================================================================== - -visplane_t *R_CheckPlane (visplane_t *pl, int start, int stop) -{ - int intrl, intrh; - int unionl, unionh; - int x; - - assert (start >= 0 && start < viewwidth); - assert (stop > start && stop <= viewwidth); - - if (start < pl->left) - { - intrl = pl->left; - unionl = start; - } - else - { - unionl = pl->left; - intrl = start; - } - - if (stop > pl->right) - { - intrh = pl->right; - unionh = stop; - } - else - { - unionh = pl->right; - intrh = stop; - } - - for (x = intrl; x < intrh && pl->top[x] == 0x7fff; x++) - ; - - if (x >= intrh) - { - // use the same visplane - pl->left = unionl; - pl->right = unionh; - } - else - { - // make a new visplane - unsigned hash; - - if (pl->portal != NULL && !(pl->portal->mFlags & PORTSF_INSKYBOX) && viewactive) - { - hash = MAXVISPLANES; - } - else - { - hash = visplane_hash (pl->picnum.GetIndex(), pl->lightlevel, pl->height); - } - visplane_t *new_pl = new_visplane (hash); - - new_pl->height = pl->height; - new_pl->picnum = pl->picnum; - new_pl->lightlevel = pl->lightlevel; - new_pl->xform = pl->xform; - new_pl->colormap = pl->colormap; - new_pl->portal = pl->portal; - new_pl->extralight = pl->extralight; - new_pl->visibility = pl->visibility; - new_pl->viewpos = pl->viewpos; - new_pl->viewangle = pl->viewangle; - new_pl->sky = pl->sky; - new_pl->Alpha = pl->Alpha; - new_pl->Additive = pl->Additive; - new_pl->CurrentPortalUniq = pl->CurrentPortalUniq; - new_pl->MirrorFlags = pl->MirrorFlags; - new_pl->CurrentSkybox = pl->CurrentSkybox; - pl = new_pl; - pl->left = start; - pl->right = stop; - fillshort (pl->top, viewwidth, 0x7fff); - } - return pl; -} - - -//========================================================================== -// -// R_MakeSpans -// -// -//========================================================================== - -inline void R_MakeSpans (int x, int t1, int b1, int t2, int b2, void (*mapfunc)(int y, int x1)) -{ -} - -//========================================================================== -// -// R_DrawSky -// -// Can handle overlapped skies. Note that the front sky is *not* masked in -// in the normal convention for patches, but uses color 0 as a transparent -// color instead. -// -// Note that since ZDoom now uses color 0 as transparent for other purposes, -// you can use normal texture transparency, so the distinction isn't so -// important anymore, but you should still be aware of it. -// -//========================================================================== - -static FTexture *frontskytex, *backskytex; -static angle_t skyflip; -static int frontpos, backpos; -static double frontyScale; -static fixed_t frontcyl, backcyl; -static double skymid; -static angle_t skyangle; -static double frontiScale; - -extern float swall[MAXWIDTH]; -extern fixed_t lwall[MAXWIDTH]; -extern fixed_t rw_offset; -extern FTexture *rw_pic; - -// Allow for layer skies up to 512 pixels tall. This is overkill, -// since the most anyone can ever see of the sky is 500 pixels. -// We need 4 skybufs because R_DrawSkySegment can draw up to 4 columns at a time. -static BYTE skybuf[4][512]; -static DWORD lastskycol[4]; -static int skycolplace; - -// Get a column of sky when there is only one sky texture. -static const BYTE *R_GetOneSkyColumn (FTexture *fronttex, int x) -{ - angle_t column = (skyangle + xtoviewangle[x]) ^ skyflip; - return fronttex->GetColumn((UMulScale16(column, frontcyl) + frontpos) >> FRACBITS, NULL); -} - -// Get a column of sky when there are two overlapping sky textures -static const BYTE *R_GetTwoSkyColumns (FTexture *fronttex, int x) -{ - DWORD ang = (skyangle + xtoviewangle[x]) ^ skyflip; - DWORD angle1 = (DWORD)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); - DWORD angle2 = (DWORD)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); - - // Check if this column has already been built. If so, there's - // no reason to waste time building it again. - DWORD skycol = (angle1 << 16) | angle2; - int i; - - for (i = 0; i < 4; ++i) - { - if (lastskycol[i] == skycol) - { - return skybuf[i]; - } - } - - lastskycol[skycolplace] = skycol; - BYTE *composite = skybuf[skycolplace]; - skycolplace = (skycolplace + 1) & 3; - - // The ordering of the following code has been tuned to allow VC++ to optimize - // it well. In particular, this arrangement lets it keep count in a register - // instead of on the stack. - const BYTE *front = fronttex->GetColumn (angle1, NULL); - const BYTE *back = backskytex->GetColumn (angle2, NULL); - - int count = MIN (512, MIN (backskytex->GetHeight(), fronttex->GetHeight())); - i = 0; - do - { - if (front[i]) - { - composite[i] = front[i]; - } - else - { - composite[i] = back[i]; - } - } while (++i, --count); - return composite; -} - -static void R_DrawSkyColumnStripe(int start_x, int y1, int y2, int columns, double scale, double texturemid, double yrepeat) -{ - uint32_t height = frontskytex->GetHeight(); - - for (int i = 0; i < columns; i++) - { - double uv_stepd = skyiscale * yrepeat; - double v = (texturemid + uv_stepd * (y1 - CenterY + 0.5)) / height; - double v_step = uv_stepd / height; - - uint32_t uv_pos = (uint32_t)(v * 0x01000000); - uint32_t uv_step = (uint32_t)(v_step * 0x01000000); - - int x = start_x + i; - if (MirrorFlags & RF_XFLIP) - x = (viewwidth - x); - - DWORD ang, angle1, angle2; - - ang = (skyangle + xtoviewangle[x]) ^ skyflip; - angle1 = (DWORD)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); - angle2 = (DWORD)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); - - dc_wall_source[i] = (const BYTE *)frontskytex->GetColumn(angle1, nullptr); - dc_wall_source2[i] = backskytex ? (const BYTE *)backskytex->GetColumn(angle2, nullptr) : nullptr; - - dc_wall_iscale[i] = uv_step; - dc_wall_texturefrac[i] = uv_pos; - } - - dc_wall_sourceheight[0] = height; - dc_wall_sourceheight[1] = backskytex ? backskytex->GetHeight() : height; - dc_dest = (ylookup[y1] + start_x) + dc_destorg; - dc_count = y2 - y1; - - uint32_t solid_top = frontskytex->GetSkyCapColor(false); - uint32_t solid_bottom = frontskytex->GetSkyCapColor(true); - - if (columns == 4) - if (!backskytex) - R_DrawSingleSkyCol4(solid_top, solid_bottom); - else - R_DrawDoubleSkyCol4(solid_top, solid_bottom); - else - if (!backskytex) - R_DrawSingleSkyCol1(solid_top, solid_bottom); - else - R_DrawDoubleSkyCol1(solid_top, solid_bottom); -} - -static void R_DrawSkyColumn(int start_x, int y1, int y2, int columns) -{ - if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) - { - double texturemid = skymid * frontskytex->Scale.Y + frontskytex->GetHeight(); - R_DrawSkyColumnStripe(start_x, y1, y2, columns, frontskytex->Scale.Y, texturemid, frontskytex->Scale.Y); - } - else - { - double yrepeat = frontskytex->Scale.Y; - double scale = frontskytex->Scale.Y * skyscale; - double iscale = 1 / scale; - short drawheight = short(frontskytex->GetHeight() * scale); - double topfrac = fmod(skymid + iscale * (1 - CenterY), frontskytex->GetHeight()); - if (topfrac < 0) topfrac += frontskytex->GetHeight(); - double texturemid = topfrac - iscale * (1 - CenterY); - R_DrawSkyColumnStripe(start_x, y1, y2, columns, scale, texturemid, yrepeat); - } -} - -static void R_DrawCapSky(visplane_t *pl) -{ - int x1 = pl->left; - int x2 = pl->right; - short *uwal = (short *)pl->top; - short *dwal = (short *)pl->bottom; - - // Calculate where 4 column alignment begins and ends: - int aligned_x1 = clamp((x1 + 3) / 4 * 4, x1, x2); - int aligned_x2 = clamp(x2 / 4 * 4, x1, x2); - - // First unaligned columns: - for (int x = x1; x < aligned_x1; x++) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - R_DrawSkyColumn(x, y1, y2, 1); - } - - // The aligned columns - for (int x = aligned_x1; x < aligned_x2; x += 4) - { - // Find y1, y2, light and uv values for four columns: - int y1[4] = { uwal[x], uwal[x + 1], uwal[x + 2], uwal[x + 3] }; - int y2[4] = { dwal[x], dwal[x + 1], dwal[x + 2], dwal[x + 3] }; - - // Figure out where we vertically can start and stop drawing 4 columns in one go - int middle_y1 = y1[0]; - int middle_y2 = y2[0]; - for (int i = 1; i < 4; i++) - { - middle_y1 = MAX(y1[i], middle_y1); - middle_y2 = MIN(y2[i], middle_y2); - } - - // If we got an empty column in our set we cannot draw 4 columns in one go: - bool empty_column_in_set = false; - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - empty_column_in_set = true; - } - if (empty_column_in_set || middle_y2 <= middle_y1) - { - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - continue; - - R_DrawSkyColumn(x + i, y1[i], y2[i], 1); - } - continue; - } - - // Draw the first rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (y1[i] < middle_y1) - R_DrawSkyColumn(x + i, y1[i], middle_y1, 1); - } - - // Draw the area where all 4 columns are active - R_DrawSkyColumn(x, middle_y1, middle_y2, 4); - - // Draw the last rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (middle_y2 < y2[i]) - R_DrawSkyColumn(x + i, middle_y2, y2[i], 1); - } - } - - // The last unaligned columns: - for (int x = aligned_x2; x < x2; x++) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - R_DrawSkyColumn(x, y1, y2, 1); - } -} - -static void R_DrawSky (visplane_t *pl) -{ - if (r_skymode == 2 && !(level.flags & LEVEL_FORCETILEDSKY)) - { - R_DrawCapSky(pl); - return; - } - - int x; - float swal; - - if (pl->left >= pl->right) - return; - - swal = skyiscale; - for (x = pl->left; x < pl->right; ++x) - { - swall[x] = swal; - } - - if (MirrorFlags & RF_XFLIP) - { - for (x = pl->left; x < pl->right; ++x) - { - lwall[x] = (viewwidth - x) << FRACBITS; - } - } - else - { - for (x = pl->left; x < pl->right; ++x) - { - lwall[x] = x << FRACBITS; - } - } - - for (x = 0; x < 4; ++x) - { - lastskycol[x] = 0xffffffff; - } - - rw_pic = frontskytex; - rw_offset = 0; - - frontyScale = rw_pic->Scale.Y; - dc_texturemid = skymid * frontyScale; - - if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) - { // The texture tiles nicely - for (x = 0; x < 4; ++x) - { - lastskycol[x] = 0xffffffff; - } - R_DrawSkySegment (pl, (short *)pl->top, (short *)pl->bottom, swall, lwall, - frontyScale, backskytex == NULL ? R_GetOneSkyColumn : R_GetTwoSkyColumns); - } - else - { // The texture does not tile nicely - frontyScale *= skyscale; - frontiScale = 1 / frontyScale; - R_DrawSkyStriped (pl); - } -} - -static void R_DrawSkyStriped (visplane_t *pl) -{ - short drawheight = short(frontskytex->GetHeight() * frontyScale); - double topfrac; - double iscale = frontiScale; - short top[MAXWIDTH], bot[MAXWIDTH]; - short yl, yh; - int x; - - topfrac = fmod(skymid + iscale * (1 - CenterY), frontskytex->GetHeight()); - if (topfrac < 0) topfrac += frontskytex->GetHeight(); - yl = 0; - yh = short((frontskytex->GetHeight() - topfrac) * frontyScale); - dc_texturemid = topfrac - iscale * (1 - CenterY); - - while (yl < viewheight) - { - for (x = pl->left; x < pl->right; ++x) - { - top[x] = MAX (yl, (short)pl->top[x]); - bot[x] = MIN (yh, (short)pl->bottom[x]); - } - for (x = 0; x < 4; ++x) - { - lastskycol[x] = 0xffffffff; - } - R_DrawSkySegment (pl, top, bot, swall, lwall, rw_pic->Scale.Y, - backskytex == NULL ? R_GetOneSkyColumn : R_GetTwoSkyColumns); - yl = yh; - yh += drawheight; - dc_texturemid = iscale * (centery-yl-1); - } -} - -//========================================================================== -// -// R_DrawPlanes -// -// At the end of each frame. -// -//========================================================================== - -int R_DrawPlanes () -{ - visplane_t *pl; - int i; - int vpcount = 0; - - ds_color = 3; - - for (i = 0; i < MAXVISPLANES; i++) - { - for (pl = visplanes[i]; pl; pl = pl->next) - { - // kg3D - draw only correct planes - if(pl->CurrentPortalUniq != CurrentPortalUniq || pl->CurrentSkybox != CurrentSkybox) - continue; - // kg3D - draw only real planes now - if(pl->sky >= 0) { - vpcount++; - R_DrawSinglePlane (pl, OPAQUE, false, false); - } - } - } - return vpcount; -} - -// kg3D - draw all visplanes with "height" -void R_DrawHeightPlanes(double height) -{ - visplane_t *pl; - int i; - - ds_color = 3; - - DVector3 oViewPos = ViewPos; - DAngle oViewAngle = ViewAngle; - - for (i = 0; i < MAXVISPLANES; i++) - { - for (pl = visplanes[i]; pl; pl = pl->next) - { - // kg3D - draw only correct planes - if(pl->CurrentSkybox != CurrentSkybox || pl->CurrentPortalUniq != CurrentPortalUniq) - continue; - if(pl->sky < 0 && pl->height.Zat0() == height) { - ViewPos = pl->viewpos; - ViewAngle = pl->viewangle; - MirrorFlags = pl->MirrorFlags; - R_DrawSinglePlane (pl, pl->sky & 0x7FFFFFFF, pl->Additive, true); - } - } - } - ViewPos = oViewPos; - ViewAngle = oViewAngle; -} - - -//========================================================================== -// -// R_DrawSinglePlane -// -// Draws a single visplane. -// -//========================================================================== - -void R_DrawSinglePlane (visplane_t *pl, fixed_t alpha, bool additive, bool masked) -{ - if (pl->left >= pl->right) - return; - - if (r_drawflat) - { // [RH] no texture mapping - ds_color += 4; - R_MapVisPlane (pl, R_MapColoredPlane); - } - else if (pl->picnum == skyflatnum) - { // sky flat - R_DrawSkyPlane (pl); - } - else - { // regular flat - FTexture *tex = TexMan(pl->picnum, true); - - if (tex->UseType == FTexture::TEX_Null) - { - return; - } - - if (!masked && !additive) - { // If we're not supposed to see through this plane, draw it opaque. - alpha = OPAQUE; - } - else if (!tex->bMasked) - { // Don't waste time on a masked texture if it isn't really masked. - masked = false; - } - R_SetupSpanBits(tex); - double xscale = pl->xform.xScale * tex->Scale.X; - double yscale = pl->xform.yScale * tex->Scale.Y; - R_SetSpanSource(tex); - - basecolormap = pl->colormap; - planeshade = LIGHT2SHADE(pl->lightlevel); - - if (r_drawflat || (!pl->height.isSlope() && !tilt)) - { - R_DrawNormalPlane(pl, xscale, yscale, alpha, additive, masked); - } - else - { - R_DrawTiltedPlane(pl, xscale, yscale, alpha, additive, masked); - } - } - NetUpdate (); -} - -//========================================================================== -// -// R_DrawPortals -// -// Draws any recorded sky boxes and then frees them. -// -// The process: -// 1. Move the camera to coincide with the SkyViewpoint. -// 2. Clear out the old planes. (They have already been drawn.) -// 3. Clear a window out of the ClipSegs just large enough for the plane. -// 4. Pretend the existing vissprites and drawsegs aren't there. -// 5. Create a drawseg at 0 distance to clip sprites to the visplane. It -// doesn't need to be associated with a line in the map, since there -// will never be any sprites in front of it. -// 6. Render the BSP, then planes, then masked stuff. -// 7. Restore the previous vissprites and drawsegs. -// 8. Repeat for any other sky boxes. -// 9. Put the camera back where it was to begin with. -// -//========================================================================== -static int numskyboxes; - -void R_DrawPortals () -{ - static TArray interestingStack; - static TArray drawsegStack; - static TArray visspriteStack; - static TArray viewposStack; - static TArray visplaneStack; - - numskyboxes = 0; - - if (visplanes[MAXVISPLANES] == NULL) - return; - - R_3D_EnterSkybox(); - CurrentPortalInSkybox = true; - - int savedextralight = extralight; - DVector3 savedpos = ViewPos; - DAngle savedangle = ViewAngle; - ptrdiff_t savedvissprite_p = vissprite_p - vissprites; - ptrdiff_t savedds_p = ds_p - drawsegs; - ptrdiff_t savedlastopening = lastopening; - size_t savedinteresting = FirstInterestingDrawseg; - double savedvisibility = R_GetVisibility(); - AActor *savedcamera = camera; - sector_t *savedsector = viewsector; - - int i; - visplane_t *pl; - - for (pl = visplanes[MAXVISPLANES]; pl != NULL; pl = visplanes[MAXVISPLANES]) - { - // Pop the visplane off the list now so that if this skybox adds more - // skyboxes to the list, they will be drawn instead of skipped (because - // new skyboxes go to the beginning of the list instead of the end). - visplanes[MAXVISPLANES] = pl->next; - pl->next = NULL; - - if (pl->right < pl->left || !r_skyboxes || numskyboxes == MAX_SKYBOX_PLANES || pl->portal == NULL) - { - R_DrawSinglePlane (pl, OPAQUE, false, false); - *freehead = pl; - freehead = &pl->next; - continue; - } - - numskyboxes++; - - FSectorPortal *port = pl->portal; - switch (port->mType) - { - case PORTS_SKYVIEWPOINT: - { - // Don't let gun flashes brighten the sky box - AActor *sky = port->mSkybox; - extralight = 0; - R_SetVisibility(sky->args[0] * 0.25f); - - ViewPos = sky->InterpolatedPosition(r_TicFracF); - ViewAngle = savedangle + (sky->PrevAngles.Yaw + deltaangle(sky->PrevAngles.Yaw, sky->Angles.Yaw) * r_TicFracF); - - R_CopyStackedViewParameters(); - break; - } - - case PORTS_STACKEDSECTORTHING: - case PORTS_PORTAL: - case PORTS_LINKEDPORTAL: - extralight = pl->extralight; - R_SetVisibility (pl->visibility); - ViewPos.X = pl->viewpos.X + port->mDisplacement.X; - ViewPos.Y = pl->viewpos.Y + port->mDisplacement.Y; - ViewPos.Z = pl->viewpos.Z; - ViewAngle = pl->viewangle; - break; - - case PORTS_HORIZON: - case PORTS_PLANE: - // not implemented yet - - default: - R_DrawSinglePlane(pl, OPAQUE, false, false); - *freehead = pl; - freehead = &pl->next; - numskyboxes--; - continue; - } - - port->mFlags |= PORTSF_INSKYBOX; - if (port->mPartner > 0) level.sectorPortals[port->mPartner].mFlags |= PORTSF_INSKYBOX; - camera = NULL; - viewsector = port->mDestination; - assert(viewsector != NULL); - R_SetViewAngle (); - validcount++; // Make sure we see all sprites - - R_ClearPlanes (false); - R_ClearClipSegs (pl->left, pl->right); - WindowLeft = pl->left; - WindowRight = pl->right; - - for (i = pl->left; i < pl->right; i++) - { - if (pl->top[i] == 0x7fff) - { - ceilingclip[i] = viewheight; - floorclip[i] = -1; - } - else - { - ceilingclip[i] = pl->top[i]; - floorclip[i] = pl->bottom[i]; - } - } - - // Create a drawseg to clip sprites to the sky plane - R_CheckDrawSegs (); - ds_p->CurrentPortalUniq = CurrentPortalUniq; - ds_p->siz1 = INT_MAX; - ds_p->siz2 = INT_MAX; - ds_p->sz1 = 0; - ds_p->sz2 = 0; - ds_p->x1 = pl->left; - ds_p->x2 = pl->right; - ds_p->silhouette = SIL_BOTH; - ds_p->sprbottomclip = R_NewOpening (pl->right - pl->left); - ds_p->sprtopclip = R_NewOpening (pl->right - pl->left); - ds_p->maskedtexturecol = ds_p->swall = -1; - ds_p->bFogBoundary = false; - ds_p->curline = NULL; - ds_p->fake = 0; - memcpy (openings + ds_p->sprbottomclip, floorclip + pl->left, (pl->right - pl->left)*sizeof(short)); - memcpy (openings + ds_p->sprtopclip, ceilingclip + pl->left, (pl->right - pl->left)*sizeof(short)); - - firstvissprite = vissprite_p; - firstdrawseg = ds_p++; - FirstInterestingDrawseg = InterestingDrawsegs.Size(); - - interestingStack.Push (FirstInterestingDrawseg); - ptrdiff_t diffnum = firstdrawseg - drawsegs; - drawsegStack.Push (diffnum); - diffnum = firstvissprite - vissprites; - visspriteStack.Push (diffnum); - viewposStack.Push(ViewPos); - visplaneStack.Push (pl); - - InSubsector = NULL; - R_RenderBSPNode (nodes + numnodes - 1); - R_3D_ResetClip(); // reset clips (floor/ceiling) - R_DrawPlanes (); - - port->mFlags &= ~PORTSF_INSKYBOX; - if (port->mPartner > 0) level.sectorPortals[port->mPartner].mFlags &= ~PORTSF_INSKYBOX; - } - - // Draw all the masked textures in a second pass, in the reverse order they - // were added. This must be done separately from the previous step for the - // sake of nested skyboxes. - while (interestingStack.Pop (FirstInterestingDrawseg)) - { - ptrdiff_t pd = 0; - - drawsegStack.Pop (pd); - firstdrawseg = drawsegs + pd; - visspriteStack.Pop (pd); - firstvissprite = vissprites + pd; - - // Masked textures and planes need the view coordinates restored for proper positioning. - viewposStack.Pop(ViewPos); - - R_DrawMasked (); - - ds_p = firstdrawseg; - vissprite_p = firstvissprite; - - visplaneStack.Pop (pl); - if (pl->Alpha > 0 && pl->picnum != skyflatnum) - { - R_DrawSinglePlane (pl, pl->Alpha, pl->Additive, true); - } - *freehead = pl; - freehead = &pl->next; - } - firstvissprite = vissprites; - vissprite_p = vissprites + savedvissprite_p; - firstdrawseg = drawsegs; - ds_p = drawsegs + savedds_p; - InterestingDrawsegs.Resize ((unsigned int)FirstInterestingDrawseg); - FirstInterestingDrawseg = savedinteresting; - - lastopening = savedlastopening; - - camera = savedcamera; - viewsector = savedsector; - ViewPos = savedpos; - R_SetVisibility(savedvisibility); - extralight = savedextralight; - ViewAngle = savedangle; - R_SetViewAngle (); - - CurrentPortalInSkybox = false; - R_3D_LeaveSkybox(); - - if(fakeActive) return; - - for (*freehead = visplanes[MAXVISPLANES], visplanes[MAXVISPLANES] = NULL; *freehead; ) - freehead = &(*freehead)->next; -} - -ADD_STAT(skyboxes) -{ - FString out; - out.Format ("%d skybox planes", numskyboxes); - return out; -} - -//========================================================================== -// -// R_DrawSkyPlane -// -//========================================================================== - -void R_DrawSkyPlane (visplane_t *pl) -{ - FTextureID sky1tex, sky2tex; - double frontdpos = 0, backdpos = 0; - - if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) - { - sky1tex = sky2texture; - } - else - { - sky1tex = sky1texture; - } - sky2tex = sky2texture; - skymid = skytexturemid; - skyangle = ViewAngle.BAMs(); - - if (pl->picnum == skyflatnum) - { - if (!(pl->sky & PL_SKYFLAT)) - { // use sky1 - sky1: - frontskytex = TexMan(sky1tex, true); - if (level.flags & LEVEL_DOUBLESKY) - backskytex = TexMan(sky2tex, true); - else - backskytex = NULL; - skyflip = 0; - frontdpos = sky1pos; - backdpos = sky2pos; - frontcyl = sky1cyl; - backcyl = sky2cyl; - } - else if (pl->sky == PL_SKYFLAT) - { // use sky2 - frontskytex = TexMan(sky2tex, true); - backskytex = NULL; - frontcyl = sky2cyl; - skyflip = 0; - frontdpos = sky2pos; - } - else - { // MBF's linedef-controlled skies - // Sky Linedef - const line_t *l = &level.lines[(pl->sky & ~PL_SKYFLAT)-1]; - - // Sky transferred from first sidedef - const side_t *s = l->sidedef[0]; - int pos; - - // Texture comes from upper texture of reference sidedef - // [RH] If swapping skies, then use the lower sidedef - if (level.flags & LEVEL_SWAPSKIES && s->GetTexture(side_t::bottom).isValid()) - { - pos = side_t::bottom; - } - else - { - pos = side_t::top; - } - - frontskytex = TexMan(s->GetTexture(pos), true); - if (frontskytex == NULL || frontskytex->UseType == FTexture::TEX_Null) - { // [RH] The blank texture: Use normal sky instead. - goto sky1; - } - backskytex = NULL; - - // Horizontal offset is turned into an angle offset, - // to allow sky rotation as well as careful positioning. - // However, the offset is scaled very small, so that it - // allows a long-period of sky rotation. - skyangle += FLOAT2FIXED(s->GetTextureXOffset(pos)); - - // Vertical offset allows careful sky positioning. - skymid = s->GetTextureYOffset(pos) - 28; - - // We sometimes flip the picture horizontally. - // - // Doom always flipped the picture, so we make it optional, - // to make it easier to use the new feature, while to still - // allow old sky textures to be used. - skyflip = l->args[2] ? 0u : ~0u; - - int frontxscale = int(frontskytex->Scale.X * 1024); - frontcyl = MAX(frontskytex->GetWidth(), frontxscale); - if (skystretch) - { - skymid = skymid * frontskytex->GetScaledHeightDouble() / SKYSTRETCH_HEIGHT; - } - } - } - frontpos = int(fmod(frontdpos, sky1cyl * 65536.0)); - if (backskytex != NULL) - { - backpos = int(fmod(backdpos, sky2cyl * 65536.0)); - } - - bool fakefixed = false; - if (fixedcolormap) - { - R_SetColorMapLight(fixedcolormap, 0, 0); - } - else - { - fakefixed = true; - fixedcolormap = NormalLight.Maps; - R_SetColorMapLight(fixedcolormap, 0, 0); - } - - R_DrawSky (pl); - - if (fakefixed) - fixedcolormap = NULL; -} - -//========================================================================== -// -// R_DrawNormalPlane -// -//========================================================================== - -void R_DrawNormalPlane (visplane_t *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked) -{ - if (alpha <= 0) - { - return; - } - - double planeang = (pl->xform.Angle + pl->xform.baseAngle).Radians(); - double xstep, ystep, leftxfrac, leftyfrac, rightxfrac, rightyfrac; - double x; - - xscale = xs_ToFixed(32 - ds_xbits, _xscale); - yscale = xs_ToFixed(32 - ds_ybits, _yscale); - if (planeang != 0) - { - double cosine = cos(planeang), sine = sin(planeang); - pviewx = FLOAT2FIXED(pl->xform.xOffs + ViewPos.X * cosine - ViewPos.Y * sine); - pviewy = FLOAT2FIXED(pl->xform.yOffs + pl->xform.baseyOffs - ViewPos.X * sine - ViewPos.Y * cosine); - } - else - { - pviewx = FLOAT2FIXED(pl->xform.xOffs + ViewPos.X); - pviewy = FLOAT2FIXED(pl->xform.yOffs - ViewPos.Y); - } - - pviewx = FixedMul (xscale, pviewx); - pviewy = FixedMul (yscale, pviewy); - - // left to right mapping - planeang += (ViewAngle - 90).Radians(); - - // Scale will be unit scale at FocalLengthX (normally SCREENWIDTH/2) distance - xstep = cos(planeang) / FocalLengthX; - ystep = -sin(planeang) / FocalLengthX; - - // [RH] flip for mirrors - if (MirrorFlags & RF_XFLIP) - { - xstep = -xstep; - ystep = -ystep; - } - - planeang += M_PI/2; - double cosine = cos(planeang), sine = -sin(planeang); - x = pl->right - centerx - 0.5; - rightxfrac = _xscale * (cosine + x * xstep); - rightyfrac = _yscale * (sine + x * ystep); - x = pl->left - centerx - 0.5; - leftxfrac = _xscale * (cosine + x * xstep); - leftyfrac = _yscale * (sine + x * ystep); - - basexfrac = rightxfrac; - baseyfrac = rightyfrac; - xstepscale = (rightxfrac - leftxfrac) / (pl->right - pl->left); - ystepscale = (rightyfrac - leftyfrac) / (pl->right - pl->left); - - planeheight = fabs(pl->height.Zat0() - ViewPos.Z); - - GlobVis = r_FloorVisibility / planeheight; - ds_light = 0; - if (fixedlightlev >= 0) - { - R_SetDSColorMapLight(basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - plane_shade = false; - } - else if (fixedcolormap) - { - R_SetDSColorMapLight(fixedcolormap, 0, 0); - plane_shade = false; - } - else - { - plane_shade = true; - } - - if (spanfunc != R_FillSpan) - { - if (masked) - { - if (alpha < OPAQUE || additive) - { - if (!additive) - { - spanfunc = R_DrawSpanMaskedTranslucent; - dc_srcblend = Col2RGB8[alpha>>10]; - dc_destblend = Col2RGB8[(OPAQUE-alpha)>>10]; - } - else - { - spanfunc = R_DrawSpanMaskedAddClamp; - dc_srcblend = Col2RGB8_LessPrecision[alpha>>10]; - dc_destblend = Col2RGB8_LessPrecision[FRACUNIT>>10]; - } - } - else - { - spanfunc = R_DrawSpanMasked; - } - } - else - { - if (alpha < OPAQUE || additive) - { - if (!additive) - { - spanfunc = R_DrawSpanTranslucent; - dc_srcblend = Col2RGB8[alpha>>10]; - dc_destblend = Col2RGB8[(OPAQUE-alpha)>>10]; - } - else - { - spanfunc = R_DrawSpanAddClamp; - dc_srcblend = Col2RGB8_LessPrecision[alpha>>10]; - dc_destblend = Col2RGB8_LessPrecision[FRACUNIT>>10]; - } - } - else - { - spanfunc = R_DrawSpan; - } - } - } - R_MapVisPlane (pl, R_MapPlane); -} - -//========================================================================== -// -// R_DrawTiltedPlane -// -//========================================================================== - -void R_DrawTiltedPlane (visplane_t *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked) -{ - static const float ifloatpow2[16] = - { - // ifloatpow2[i] = 1 / (1 << i) - 64.f, 32.f, 16.f, 8.f, 4.f, 2.f, 1.f, 0.5f, - 0.25f, 0.125f, 0.0625f, 0.03125f, 0.015625f, 0.0078125f, - 0.00390625f, 0.001953125f - /*, 0.0009765625f, 0.00048828125f, 0.000244140625f, - 1.220703125e-4f, 6.103515625e-5, 3.0517578125e-5*/ - }; - double lxscale, lyscale; - double xscale, yscale; - FVector3 p, m, n; - double ang, planeang, cosine, sine; - double zeroheight; - - if (alpha <= 0) - { - return; - } - - lxscale = _xscale * ifloatpow2[ds_xbits]; - lyscale = _yscale * ifloatpow2[ds_ybits]; - xscale = 64.f / lxscale; - yscale = 64.f / lyscale; - zeroheight = pl->height.ZatPoint(ViewPos); - - pviewx = xs_ToFixed(32 - ds_xbits, pl->xform.xOffs * pl->xform.xScale); - pviewy = xs_ToFixed(32 - ds_ybits, pl->xform.yOffs * pl->xform.yScale); - planeang = (pl->xform.Angle + pl->xform.baseAngle).Radians(); - - // p is the texture origin in view space - // Don't add in the offsets at this stage, because doing so can result in - // errors if the flat is rotated. - ang = M_PI*3/2 - ViewAngle.Radians(); - cosine = cos(ang), sine = sin(ang); - p[0] = ViewPos.X * cosine - ViewPos.Y * sine; - p[2] = ViewPos.X * sine + ViewPos.Y * cosine; - p[1] = pl->height.ZatPoint(0.0, 0.0) - ViewPos.Z; - - // m is the v direction vector in view space - ang = ang - M_PI / 2 - planeang; - cosine = cos(ang), sine = sin(ang); - m[0] = yscale * cosine; - m[2] = yscale * sine; -// m[1] = pl->height.ZatPointF (0, iyscale) - pl->height.ZatPointF (0,0)); -// VectorScale2 (m, 64.f/VectorLength(m)); - - // n is the u direction vector in view space -#if 0 - //let's use the sin/cosine we already know instead of computing new ones - ang += M_PI/2 - n[0] = -xscale * cos(ang); - n[2] = -xscale * sin(ang); -#else - n[0] = xscale * sine; - n[2] = -xscale * cosine; -#endif -// n[1] = pl->height.ZatPointF (ixscale, 0) - pl->height.ZatPointF (0,0)); -// VectorScale2 (n, 64.f/VectorLength(n)); - - // This code keeps the texture coordinates constant across the x,y plane no matter - // how much you slope the surface. Use the commented-out code above instead to keep - // the textures a constant size across the surface's plane instead. - cosine = cos(planeang), sine = sin(planeang); - m[1] = pl->height.ZatPoint(ViewPos.X + yscale * sine, ViewPos.Y + yscale * cosine) - zeroheight; - n[1] = -(pl->height.ZatPoint(ViewPos.X - xscale * cosine, ViewPos.Y + xscale * sine) - zeroheight); - - plane_su = p ^ m; - plane_sv = p ^ n; - plane_sz = m ^ n; - - plane_su.Z *= FocalLengthX; - plane_sv.Z *= FocalLengthX; - plane_sz.Z *= FocalLengthX; - - plane_su.Y *= IYaspectMul; - plane_sv.Y *= IYaspectMul; - plane_sz.Y *= IYaspectMul; - - // Premultiply the texture vectors with the scale factors - plane_su *= 4294967296.f; - plane_sv *= 4294967296.f; - - if (MirrorFlags & RF_XFLIP) - { - plane_su[0] = -plane_su[0]; - plane_sv[0] = -plane_sv[0]; - plane_sz[0] = -plane_sz[0]; - } - - planelightfloat = (r_TiltVisibility * lxscale * lyscale) / (fabs(pl->height.ZatPoint(ViewPos) - ViewPos.Z)) / 65536.f; - - if (pl->height.fC() > 0) - planelightfloat = -planelightfloat; - - if (fixedlightlev >= 0) - { - R_SetDSColorMapLight(basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - plane_shade = false; - } - else if (fixedcolormap) - { - R_SetDSColorMapLight(fixedcolormap, 0, 0); - plane_shade = false; - } - else - { - R_SetDSColorMapLight(basecolormap, 0, 0); - plane_shade = true; - } - - // Hack in support for 1 x Z and Z x 1 texture sizes - if (ds_ybits == 0) - { - plane_sv[2] = plane_sv[1] = plane_sv[0] = 0; - } - if (ds_xbits == 0) - { - plane_su[2] = plane_su[1] = plane_su[0] = 0; - } - - R_MapVisPlane (pl, R_MapTiltedPlane); -} - -//========================================================================== -// -// R_MapVisPlane -// -// t1/b1 are at x -// t2/b2 are at x+1 -// spanend[y] is at the right edge -// -//========================================================================== - -void R_MapVisPlane (visplane_t *pl, void (*mapfunc)(int y, int x1)) -{ - int x = pl->right - 1; - int t2 = pl->top[x]; - int b2 = pl->bottom[x]; - - if (b2 > t2) - { - fillshort (spanend+t2, b2-t2, x); - } - - for (--x; x >= pl->left; --x) - { - int t1 = pl->top[x]; - int b1 = pl->bottom[x]; - const int xr = x+1; - int stop; - - // Draw any spans that have just closed - stop = MIN (t1, b2); - while (t2 < stop) - { - mapfunc (t2++, xr); - } - stop = MAX (b1, t2); - while (b2 > stop) - { - mapfunc (--b2, xr); - } - - // Mark any spans that have just opened - stop = MIN (t2, b1); - while (t1 < stop) - { - spanend[t1++] = x; - } - stop = MAX (b2, t2); - while (b1 > stop) - { - spanend[--b1] = x; - } - - t2 = pl->top[x]; - b2 = pl->bottom[x]; - basexfrac -= xstepscale; - baseyfrac -= ystepscale; - } - // Draw any spans that are still open - while (t2 < b2) - { - mapfunc (--b2, pl->left); - } -} - -//========================================================================== -// -// R_PlaneInitData -// -//========================================================================== - -bool R_PlaneInitData () -{ - int i; - visplane_t *pl; - - // Free all visplanes and let them be re-allocated as needed. - pl = freetail; - - while (pl) - { - visplane_t *next = pl->next; - M_Free (pl); - pl = next; - } - freetail = NULL; - freehead = &freetail; - - for (i = 0; i < MAXVISPLANES; i++) - { - pl = visplanes[i]; - visplanes[i] = NULL; - while (pl) - { - visplane_t *next = pl->next; - M_Free (pl); - pl = next; - } - } - - return true; -} - -} diff --git a/src/r_plane.h b/src/r_plane.h deleted file mode 100644 index cd1405a7c2..0000000000 --- a/src/r_plane.h +++ /dev/null @@ -1,119 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Refresh, visplane stuff (floor, ceilings). -// -//----------------------------------------------------------------------------- - - -#ifndef __R_PLANE_H__ -#define __R_PLANE_H__ - -#include - -namespace swrenderer -{ - -// -// The infamous visplane -// -struct visplane_s -{ - struct visplane_s *next; // Next visplane in hash chain -- killough - - FDynamicColormap *colormap; // [RH] Support multiple colormaps - FSectorPortal *portal; // [RH] Support sky boxes - - FTransform xform; - secplane_t height; - FTextureID picnum; - int lightlevel; - int left, right; - int sky; - - // [RH] This set of variables copies information from the time when the - // visplane is created. They are only used by stacks so that you can - // have stacked sectors inside a skybox. If the visplane is not for a - // stack, then they are unused. - int extralight; - double visibility; - DVector3 viewpos; - DAngle viewangle; - fixed_t Alpha; - bool Additive; - - // kg3D - keep track of mirror and skybox owner - int CurrentSkybox; - int CurrentPortalUniq; // mirror counter, counts all of them - int MirrorFlags; // this is not related to CurrentMirror - - unsigned short *bottom; // [RH] bottom and top arrays are dynamically - unsigned short pad; // allocated immediately after the - unsigned short top[]; // visplane. -}; -typedef struct visplane_s visplane_t; - - - -// Visplane related. -extern ptrdiff_t lastopening; // type short - - -typedef void (*planefunction_t) (int top, int bottom); - -extern planefunction_t floorfunc; -extern planefunction_t ceilingfunc_t; - -extern short floorclip[MAXWIDTH]; -extern short ceilingclip[MAXWIDTH]; - -extern float yslope[MAXHEIGHT]; - -void R_InitPlanes (); -void R_DeinitPlanes (); -void R_ClearPlanes (bool fullclear); - -int R_DrawPlanes (); -void R_DrawPortals (); -void R_DrawSkyPlane (visplane_t *pl); -void R_DrawNormalPlane (visplane_t *pl, double xscale, double yscale, fixed_t alpha, bool additive, bool masked); -void R_DrawTiltedPlane (visplane_t *pl, double xscale, double yscale, fixed_t alpha, bool additive, bool masked); -void R_MapVisPlane (visplane_t *pl, void (*mapfunc)(int y, int x1)); - -visplane_t *R_FindPlane -( const secplane_t &height, - FTextureID picnum, - int lightlevel, - double alpha, - bool additive, - const FTransform &xform, - int sky, - FSectorPortal *portal); - -visplane_t *R_CheckPlane (visplane_t *pl, int start, int stop); - - -// [RH] Added for multires support -bool R_PlaneInitData (void); - - -extern visplane_t* floorplane; -extern visplane_t* ceilingplane; - -} - -#endif // __R_PLANE_H__ diff --git a/src/r_renderer.h b/src/r_renderer.h index 4236993d63..a5ecb12d8f 100644 --- a/src/r_renderer.h +++ b/src/r_renderer.h @@ -29,7 +29,7 @@ struct FRenderer virtual bool UsesColormap() const = 0; // precache one texture - virtual void Precache(BYTE *texhitlist, TMap &actorhitlist) = 0; + virtual void Precache(uint8_t *texhitlist, TMap &actorhitlist) = 0; // render 3D view virtual void RenderView(player_t *player) = 0; @@ -43,9 +43,6 @@ struct FRenderer // draws player sprites with hardware acceleration (only useful for software rendering) virtual void DrawRemainingPlayerSprites() {} - // notifies the renderer that an actor has changed state. - virtual void StateChanged(AActor *actor) {} - // notify the renderer that serialization of the curent level is about to start/end virtual void StartSerialize(FSerializer &arc) {} virtual void EndSerialize(FSerializer &arc) {} @@ -53,19 +50,17 @@ struct FRenderer virtual int GetMaxViewPitch(bool down) = 0; // return value is in plain degrees virtual void OnModeSet () {} - virtual void ErrorCleanup () {} - virtual void ClearBuffer(int color) = 0; + virtual void SetClearColor(int color) = 0; virtual void Init() = 0; - virtual void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) {} - virtual void SetupFrame(player_t *player) {} - virtual void CopyStackedViewParameters() {} virtual void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) = 0; - virtual sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) = 0; + virtual sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) = 0; virtual void SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog) {} virtual void PreprocessLevel() {} virtual void CleanLevelData() {} virtual bool RequireGLNodes() { return false; } + virtual double GetVisibility() { return 8.f; } + virtual void SetVisibility(double vis) { } }; diff --git a/src/r_segs.cpp b/src/r_segs.cpp deleted file mode 100644 index 0873cb373c..0000000000 --- a/src/r_segs.cpp +++ /dev/null @@ -1,2410 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// All the clipping: columns, horizontal spans, sky columns. -// -//----------------------------------------------------------------------------- - -#include -#include - -#include "templates.h" -#include "i_system.h" - -#include "doomdef.h" -#include "doomstat.h" -#include "doomdata.h" -#include "p_lnspec.h" - -#include "r_local.h" -#include "r_sky.h" -#include "v_video.h" - -#include "m_swap.h" -#include "w_wad.h" -#include "stats.h" -#include "a_sharedglobal.h" -#include "d_net.h" -#include "g_level.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "r_draw.h" -#include "v_palette.h" -#include "r_data/colormaps.h" - -#define WALLYREPEAT 8 - - -CVAR(Bool, r_fogboundary, true, 0) -CVAR(Bool, r_drawmirrors, true, 0) -EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); - -namespace swrenderer -{ - using namespace drawerargs; - - void R_DrawWallSegment(FTexture *rw_pic, int x1, int x2, short *walltop, short *wallbottom, float *swall, fixed_t *lwall, double yscale, double top, double bottom, bool mask); - void R_DrawDrawSeg(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat); - -#define HEIGHTBITS 12 -#define HEIGHTSHIFT (FRACBITS-HEIGHTBITS) - -extern double globaluclip, globaldclip; - -PortalDrawseg* CurrentPortal = NULL; -int CurrentPortalUniq = 0; -bool CurrentPortalInSkybox = false; - -// OPTIMIZE: closed two sided lines as single sided - -// killough 1/6/98: replaced globals with statics where appropriate - -static bool segtextured; // True if any of the segs textures might be visible. -bool markfloor; // False if the back side is the same plane. -bool markceiling; -FTexture *toptexture; -FTexture *bottomtexture; -FTexture *midtexture; -fixed_t rw_offset_top; -fixed_t rw_offset_mid; -fixed_t rw_offset_bottom; - - -int wallshade; - -short walltop[MAXWIDTH]; // [RH] record max extents of wall -short wallbottom[MAXWIDTH]; -short wallupper[MAXWIDTH]; -short walllower[MAXWIDTH]; -float swall[MAXWIDTH]; -fixed_t lwall[MAXWIDTH]; -double lwallscale; - -// -// regular wall -// -extern double rw_backcz1, rw_backcz2; -extern double rw_backfz1, rw_backfz2; -extern double rw_frontcz1, rw_frontcz2; -extern double rw_frontfz1, rw_frontfz2; - -int rw_ceilstat, rw_floorstat; -bool rw_mustmarkfloor, rw_mustmarkceiling; -bool rw_prepped; -bool rw_markportal; -bool rw_havehigh; -bool rw_havelow; - -float rw_light; // [RH] Scale lights with viewsize adjustments -float rw_lightstep; -float rw_lightleft; - -static double rw_frontlowertop; - -static int rw_x; -static int rw_stopx; -fixed_t rw_offset; -static double rw_scalestep; -static double rw_midtexturemid; -static double rw_toptexturemid; -static double rw_bottomtexturemid; -static double rw_midtexturescalex; -static double rw_midtexturescaley; -static double rw_toptexturescalex; -static double rw_toptexturescaley; -static double rw_bottomtexturescalex; -static double rw_bottomtexturescaley; - -FTexture *rw_pic; - -static fixed_t *maskedtexturecol; - -static void R_RenderDecal (side_t *wall, DBaseDecal *first, drawseg_t *clipper, int pass); -static void WallSpriteColumn (void (*drawfunc)(const BYTE *column, const FTexture::Span *spans)); - -inline bool IsFogBoundary (sector_t *front, sector_t *back) -{ - return r_fogboundary && fixedcolormap == NULL && front->ColorMap->Fade && - front->ColorMap->Fade != back->ColorMap->Fade && - (front->GetTexture(sector_t::ceiling) != skyflatnum || back->GetTexture(sector_t::ceiling) != skyflatnum); -} - - -// -// R_RenderMaskedSegRange -// -float *MaskedSWall; -float MaskedScaleY; - -static void BlastMaskedColumn (FTexture *tex, bool useRt) -{ - // calculate lighting - if (fixedcolormap == NULL && fixedlightlev < 0) - { - R_SetColorMapLight(basecolormap, rw_light, wallshade); - } - - dc_iscale = xs_Fix<16>::ToFix(MaskedSWall[dc_x] * MaskedScaleY); - if (sprflipvert) - sprtopscreen = CenterY + dc_texturemid * spryscale; - else - sprtopscreen = CenterY - dc_texturemid * spryscale; - - // killough 1/25/98: here's where Medusa came in, because - // it implicitly assumed that the column was all one patch. - // Originally, Doom did not construct complete columns for - // multipatched textures, so there were no header or trailer - // bytes in the column referred to below, which explains - // the Medusa effect. The fix is to construct true columns - // when forming multipatched textures (see r_data.c). - - // draw the texture - R_DrawMaskedColumn(tex, maskedtexturecol[dc_x], useRt); - rw_light += rw_lightstep; - spryscale += rw_scalestep; -} - -// Clip a midtexture to the floor and ceiling of the sector in front of it. -void ClipMidtex(int x1, int x2) -{ - short most[MAXWIDTH]; - - R_CreateWallSegmentYSloped(most, curline->frontsector->ceilingplane, &WallC); - for (int i = x1; i < x2; ++i) - { - if (wallupper[i] < most[i]) - wallupper[i] = most[i]; - } - R_CreateWallSegmentYSloped(most, curline->frontsector->floorplane, &WallC); - for (int i = x1; i < x2; ++i) - { - if (walllower[i] > most[i]) - walllower[i] = most[i]; - } -} - -void R_RenderFakeWallRange(drawseg_t *ds, int x1, int x2); - -void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2) -{ - FTexture *tex; - int i; - sector_t tempsec; // killough 4/13/98 - double texheight, texheightscale; - bool notrelevant = false; - double rowoffset; - bool wrap = false; - - const sector_t *sec; - - sprflipvert = false; - - curline = ds->curline; - - // killough 4/11/98: draw translucent 2s normal textures - // [RH] modified because we don't use user-definable translucency maps - ESPSResult drawmode; - - drawmode = R_SetPatchStyle (LegacyRenderStyles[curline->linedef->flags & ML_ADDTRANS ? STYLE_Add : STYLE_Translucent], - (float)MIN(curline->linedef->alpha, 1.), 0, 0); - - if ((drawmode == DontDraw && !ds->bFogBoundary && !ds->bFakeBoundary)) - { - return; - } - - NetUpdate (); - - frontsector = curline->frontsector; - backsector = curline->backsector; - - tex = TexMan(curline->sidedef->GetTexture(side_t::mid), true); - if (i_compatflags & COMPATF_MASKEDMIDTEX) - { - tex = tex->GetRawTexture(); - } - - // killough 4/13/98: get correct lightlevel for 2s normal textures - sec = R_FakeFlat (frontsector, &tempsec, NULL, NULL, false); - - basecolormap = sec->ColorMap; // [RH] Set basecolormap - - wallshade = ds->shade; - rw_lightstep = ds->lightstep; - rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; - - if (fixedlightlev < 0) - { - if (!(fake3D & FAKE3D_CLIPTOP)) - { - sclipTop = sec->ceilingplane.ZatPoint(ViewPos); - } - for (i = frontsector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) - { - if (sclipTop <= frontsector->e->XFloor.lightlist[i].plane.Zat0()) - { - lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - - mfloorclip = openings + ds->sprbottomclip - ds->x1; - mceilingclip = openings + ds->sprtopclip - ds->x1; - - // [RH] Draw fog partition - if (ds->bFogBoundary) - { - R_DrawFogBoundary (x1, x2, mceilingclip, mfloorclip); - if (ds->maskedtexturecol == -1) - { - goto clearfog; - } - } - if ((ds->bFakeBoundary && !(ds->bFakeBoundary & 4)) || drawmode == DontDraw) - { - goto clearfog; - } - - MaskedSWall = (float *)(openings + ds->swall) - ds->x1; - MaskedScaleY = ds->yscale; - maskedtexturecol = (fixed_t *)(openings + ds->maskedtexturecol) - ds->x1; - spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); - rw_scalestep = ds->iscalestep; - - if (fixedlightlev >= 0) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(fixedcolormap, 0, 0); - - // find positioning - texheight = tex->GetScaledHeightDouble(); - texheightscale = fabs(curline->sidedef->GetTextureYScale(side_t::mid)); - if (texheightscale != 1) - { - texheight = texheight / texheightscale; - } - if (curline->linedef->flags & ML_DONTPEGBOTTOM) - { - dc_texturemid = MAX(frontsector->GetPlaneTexZ(sector_t::floor), backsector->GetPlaneTexZ(sector_t::floor)) + texheight; - } - else - { - dc_texturemid = MIN(frontsector->GetPlaneTexZ(sector_t::ceiling), backsector->GetPlaneTexZ(sector_t::ceiling)); - } - - rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid); - - wrap = (curline->linedef->flags & ML_WRAP_MIDTEX) || (curline->sidedef->Flags & WALLF_WRAP_MIDTEX); - if (!wrap) - { // Texture does not wrap vertically. - double textop; - - if (MaskedScaleY < 0) - { - MaskedScaleY = -MaskedScaleY; - sprflipvert = true; - } - if (tex->bWorldPanning) - { - // rowoffset is added before the multiply so that the masked texture will - // still be positioned in world units rather than texels. - dc_texturemid += rowoffset - ViewPos.Z; - textop = dc_texturemid; - dc_texturemid *= MaskedScaleY; - } - else - { - // rowoffset is added outside the multiply so that it positions the texture - // by texels instead of world units. - textop = dc_texturemid + rowoffset / MaskedScaleY - ViewPos.Z; - dc_texturemid = (dc_texturemid - ViewPos.Z) * MaskedScaleY + rowoffset; - } - if (sprflipvert) - { - MaskedScaleY = -MaskedScaleY; - dc_texturemid -= tex->GetHeight() << FRACBITS; - } - - // [RH] Don't bother drawing segs that are completely offscreen - if (globaldclip * ds->sz1 < -textop && globaldclip * ds->sz2 < -textop) - { // Texture top is below the bottom of the screen - goto clearfog; - } - - if (globaluclip * ds->sz1 > texheight - textop && globaluclip * ds->sz2 > texheight - textop) - { // Texture bottom is above the top of the screen - goto clearfog; - } - - if ((fake3D & FAKE3D_CLIPBOTTOM) && textop < sclipBottom - ViewPos.Z) - { - notrelevant = true; - goto clearfog; - } - if ((fake3D & FAKE3D_CLIPTOP) && textop - texheight > sclipTop - ViewPos.Z) - { - notrelevant = true; - goto clearfog; - } - - WallC.sz1 = ds->sz1; - WallC.sz2 = ds->sz2; - WallC.sx1 = ds->sx1; - WallC.sx2 = ds->sx2; - - if (fake3D & FAKE3D_CLIPTOP) - { - R_CreateWallSegmentY(wallupper, textop < sclipTop - ViewPos.Z ? textop : sclipTop - ViewPos.Z, &WallC); - } - else - { - R_CreateWallSegmentY(wallupper, textop, &WallC); - } - if (fake3D & FAKE3D_CLIPBOTTOM) - { - R_CreateWallSegmentY(walllower, textop - texheight > sclipBottom - ViewPos.Z ? textop - texheight : sclipBottom - ViewPos.Z, &WallC); - } - else - { - R_CreateWallSegmentY(walllower, textop - texheight, &WallC); - } - - for (i = x1; i < x2; i++) - { - if (wallupper[i] < mceilingclip[i]) - wallupper[i] = mceilingclip[i]; - } - for (i = x1; i < x2; i++) - { - if (walllower[i] > mfloorclip[i]) - walllower[i] = mfloorclip[i]; - } - - if (CurrentSkybox) - { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor - // or above the ceiling, so the appropriate end won't be clipped automatically when adding - // this drawseg. - if ((curline->linedef->flags & ML_CLIP_MIDTEX) || - (curline->sidedef->Flags & WALLF_CLIP_MIDTEX)) - { - ClipMidtex(x1, x2); - } - } - - mfloorclip = walllower; - mceilingclip = wallupper; - - // draw the columns one at a time - if (drawmode == DoDraw0) - { - for (dc_x = x1; dc_x < x2; ++dc_x) - { - BlastMaskedColumn (tex, false); - } - } - else - { - // [RH] Draw up to four columns at once - int stop = x2 & ~3; - - if (x1 >= x2) - goto clearfog; - - dc_x = x1; - - while ((dc_x < stop) && (dc_x & 3)) - { - BlastMaskedColumn (tex, false); - dc_x++; - } - - while (dc_x < stop) - { - rt_initcols(nullptr); - BlastMaskedColumn (tex, true); dc_x++; - BlastMaskedColumn (tex, true); dc_x++; - BlastMaskedColumn (tex, true); dc_x++; - BlastMaskedColumn (tex, true); - rt_draw4cols (dc_x - 3); - dc_x++; - } - - while (dc_x < x2) - { - BlastMaskedColumn (tex, false); - dc_x++; - } - } - } - else - { // Texture does wrap vertically. - if (tex->bWorldPanning) - { - // rowoffset is added before the multiply so that the masked texture will - // still be positioned in world units rather than texels. - dc_texturemid = (dc_texturemid - ViewPos.Z + rowoffset) * MaskedScaleY; - } - else - { - // rowoffset is added outside the multiply so that it positions the texture - // by texels instead of world units. - dc_texturemid = (dc_texturemid - ViewPos.Z) * MaskedScaleY + rowoffset; - } - - WallC.sz1 = ds->sz1; - WallC.sz2 = ds->sz2; - WallC.sx1 = ds->sx1; - WallC.sx2 = ds->sx2; - - if (CurrentSkybox) - { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor - // or above the ceiling, so the appropriate end won't be clipped automatically when adding - // this drawseg. - if ((curline->linedef->flags & ML_CLIP_MIDTEX) || - (curline->sidedef->Flags & WALLF_CLIP_MIDTEX)) - { - ClipMidtex(x1, x2); - } - } - - if (fake3D & FAKE3D_CLIPTOP) - { - R_CreateWallSegmentY(wallupper, sclipTop - ViewPos.Z, &WallC); - for (i = x1; i < x2; i++) - { - if (wallupper[i] < mceilingclip[i]) - wallupper[i] = mceilingclip[i]; - } - mceilingclip = wallupper; - } - if (fake3D & FAKE3D_CLIPBOTTOM) - { - R_CreateWallSegmentY(walllower, sclipBottom - ViewPos.Z, &WallC); - for (i = x1; i < x2; i++) - { - if (walllower[i] > mfloorclip[i]) - walllower[i] = mfloorclip[i]; - } - mfloorclip = walllower; - } - - rw_offset = 0; - rw_pic = tex; - R_DrawDrawSeg(ds, x1, x2, mceilingclip, mfloorclip, MaskedSWall, maskedtexturecol, ds->yscale); - } - -clearfog: - R_FinishSetPatchStyle (); - if (ds->bFakeBoundary & 3) - { - R_RenderFakeWallRange(ds, x1, x2); - } - if (!notrelevant) - { - if (fake3D & FAKE3D_REFRESHCLIP) - { - if (!wrap) - { - assert(ds->bkup >= 0); - memcpy(openings + ds->sprtopclip, openings + ds->bkup, (ds->x2 - ds->x1) * 2); - } - } - else - { - fillshort(openings + ds->sprtopclip - ds->x1 + x1, x2 - x1, viewheight); - } - } - return; -} - -// kg3D - render one fake wall -void R_RenderFakeWall(drawseg_t *ds, int x1, int x2, F3DFloor *rover) -{ - int i; - double xscale; - double yscale; - - fixed_t Alpha = Scale(rover->alpha, OPAQUE, 255); - ESPSResult drawmode; - drawmode = R_SetPatchStyle (LegacyRenderStyles[rover->flags & FF_ADDITIVETRANS ? STYLE_Add : STYLE_Translucent], - Alpha, 0, 0); - - if(drawmode == DontDraw) { - R_FinishSetPatchStyle(); - return; - } - - rw_lightstep = ds->lightstep; - rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; - - mfloorclip = openings + ds->sprbottomclip - ds->x1; - mceilingclip = openings + ds->sprtopclip - ds->x1; - - spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); - rw_scalestep = ds->iscalestep; - MaskedSWall = (float *)(openings + ds->swall) - ds->x1; - - // find positioning - side_t *scaledside; - side_t::ETexpart scaledpart; - if (rover->flags & FF_UPPERTEXTURE) - { - scaledside = curline->sidedef; - scaledpart = side_t::top; - } - else if (rover->flags & FF_LOWERTEXTURE) - { - scaledside = curline->sidedef; - scaledpart = side_t::bottom; - } - else - { - scaledside = rover->master->sidedef[0]; - scaledpart = side_t::mid; - } - xscale = rw_pic->Scale.X * scaledside->GetTextureXScale(scaledpart); - yscale = rw_pic->Scale.Y * scaledside->GetTextureYScale(scaledpart); - - double rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureYOffset(side_t::mid); - double planez = rover->model->GetPlaneTexZ(sector_t::ceiling); - rw_offset = FLOAT2FIXED(curline->sidedef->GetTextureXOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureXOffset(side_t::mid)); - if (rowoffset < 0) - { - rowoffset += rw_pic->GetHeight(); - } - dc_texturemid = (planez - ViewPos.Z) * yscale; - if (rw_pic->bWorldPanning) - { - // rowoffset is added before the multiply so that the masked texture will - // still be positioned in world units rather than texels. - - dc_texturemid = dc_texturemid + rowoffset * yscale; - rw_offset = xs_RoundToInt(rw_offset * xscale); - } - else - { - // rowoffset is added outside the multiply so that it positions the texture - // by texels instead of world units. - dc_texturemid += rowoffset; - } - - if (fixedlightlev >= 0) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(fixedcolormap, 0, 0); - - WallC.sz1 = ds->sz1; - WallC.sz2 = ds->sz2; - WallC.sx1 = ds->sx1; - WallC.sx2 = ds->sx2; - WallC.tleft.X = ds->cx; - WallC.tleft.Y = ds->cy; - WallC.tright.X = ds->cx + ds->cdx; - WallC.tright.Y = ds->cy + ds->cdy; - WallT = ds->tmapvals; - - R_CreateWallSegmentY(wallupper, sclipTop - ViewPos.Z, &WallC); - R_CreateWallSegmentY(walllower, sclipBottom - ViewPos.Z, &WallC); - - for (i = x1; i < x2; i++) - { - if (wallupper[i] < mceilingclip[i]) - wallupper[i] = mceilingclip[i]; - } - for (i = x1; i < x2; i++) - { - if (walllower[i] > mfloorclip[i]) - walllower[i] = mfloorclip[i]; - } - - PrepLWall (lwall, curline->sidedef->TexelLength*xscale, ds->sx1, ds->sx2); - R_DrawDrawSeg(ds, x1, x2, wallupper, walllower, MaskedSWall, lwall, yscale); - R_FinishSetPatchStyle(); -} - -// kg3D - walls of fake floors -void R_RenderFakeWallRange (drawseg_t *ds, int x1, int x2) -{ - FTexture *const DONT_DRAW = ((FTexture*)(intptr_t)-1); - int i,j; - F3DFloor *rover, *fover = NULL; - int passed, last; - double floorHeight; - double ceilingHeight; - - sprflipvert = false; - curline = ds->curline; - - frontsector = curline->frontsector; - backsector = curline->backsector; - - if (backsector == NULL) - { - return; - } - if ((ds->bFakeBoundary & 3) == 2) - { - sector_t *sec = backsector; - backsector = frontsector; - frontsector = sec; - } - - floorHeight = backsector->CenterFloor(); - ceilingHeight = backsector->CenterCeiling(); - - // maybe fix clipheights - if (!(fake3D & FAKE3D_CLIPBOTTOM)) sclipBottom = floorHeight; - if (!(fake3D & FAKE3D_CLIPTOP)) sclipTop = ceilingHeight; - - // maybe not visible - if (sclipBottom >= frontsector->CenterCeiling()) return; - if (sclipTop <= frontsector->CenterFloor()) return; - - if (fake3D & FAKE3D_DOWN2UP) - { // bottom to viewz - last = 0; - for (i = backsector->e->XFloor.ffloors.Size() - 1; i >= 0; i--) - { - rover = backsector->e->XFloor.ffloors[i]; - if (!(rover->flags & FF_EXISTS)) continue; - - // visible? - passed = 0; - if (!(rover->flags & FF_RENDERSIDES) || rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || - rover->top.plane->Zat0() <= sclipBottom || - rover->bottom.plane->Zat0() >= ceilingHeight || - rover->top.plane->Zat0() <= floorHeight) - { - if (!i) - { - passed = 1; - } - else - { - continue; - } - } - - rw_pic = NULL; - if (rover->bottom.plane->Zat0() >= sclipTop || passed) - { - if (last) - { - break; - } - // maybe wall from inside rendering? - fover = NULL; - for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) - { - fover = frontsector->e->XFloor.ffloors[j]; - if (fover->model == rover->model) - { // never - fover = NULL; - break; - } - if (!(fover->flags & FF_EXISTS)) continue; - if (!(fover->flags & FF_RENDERSIDES)) continue; - // no sloped walls, it's bugged - if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; - - // visible? - if (fover->top.plane->Zat0() <= sclipBottom) continue; // no - if (fover->bottom.plane->Zat0() >= sclipTop) - { // no, last possible - fover = NULL; - break; - } - // it is, render inside? - if (!(fover->flags & (FF_BOTHPLANES|FF_INVERTPLANES))) - { // no - fover = NULL; - } - break; - } - // nothing - if (!fover || j == -1) - { - break; - } - // correct texture - if (fover->flags & rover->flags & FF_SWIMMABLE) - { // don't ever draw (but treat as something has been found) - rw_pic = DONT_DRAW; - } - else if(fover->flags & FF_UPPERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); - } - else if(fover->flags & FF_LOWERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); - } - else - { - rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); - } - } - else if (frontsector->e->XFloor.ffloors.Size()) - { - // maybe not visible? - fover = NULL; - for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) - { - fover = frontsector->e->XFloor.ffloors[j]; - if (fover->model == rover->model) // never - { - break; - } - if (!(fover->flags & FF_EXISTS)) continue; - if (!(fover->flags & FF_RENDERSIDES)) continue; - // no sloped walls, it's bugged - if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; - - // visible? - if (fover->top.plane->Zat0() <= sclipBottom) continue; // no - if (fover->bottom.plane->Zat0() >= sclipTop) - { // visible, last possible - fover = NULL; - break; - } - if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && - !(!(fover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) - ) - { - break; - } - if (fover->flags & rover->flags & FF_SWIMMABLE) - { // don't ever draw (but treat as something has been found) - rw_pic = DONT_DRAW; - } - fover = NULL; // visible - break; - } - if (fover && j != -1) - { - fover = NULL; - last = 1; - continue; // not visible - } - } - if (!rw_pic) - { - fover = NULL; - if (rover->flags & FF_UPPERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); - } - else if(rover->flags & FF_LOWERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); - } - else - { - rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); - } - } - // correct colors now - basecolormap = frontsector->ColorMap; - wallshade = ds->shade; - if (fixedlightlev < 0) - { - if ((ds->bFakeBoundary & 3) == 2) - { - for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) - { - if (sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) - { - lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - else - { - for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) - { - if (sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) - { - lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - } - if (rw_pic != DONT_DRAW) - { - R_RenderFakeWall(ds, x1, x2, fover ? fover : rover); - } - else rw_pic = NULL; - break; - } - } - else - { // top to viewz - for (i = 0; i < (int)backsector->e->XFloor.ffloors.Size(); i++) - { - rover = backsector->e->XFloor.ffloors[i]; - if (!(rover->flags & FF_EXISTS)) continue; - - // visible? - passed = 0; - if (!(rover->flags & FF_RENDERSIDES) || - rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || - rover->bottom.plane->Zat0() >= sclipTop || - rover->top.plane->Zat0() <= floorHeight || - rover->bottom.plane->Zat0() >= ceilingHeight) - { - if ((unsigned)i == backsector->e->XFloor.ffloors.Size() - 1) - { - passed = 1; - } - else - { - continue; - } - } - rw_pic = NULL; - if (rover->top.plane->Zat0() <= sclipBottom || passed) - { // maybe wall from inside rendering? - fover = NULL; - for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) - { - fover = frontsector->e->XFloor.ffloors[j]; - if (fover->model == rover->model) - { // never - fover = NULL; - break; - } - if (!(fover->flags & FF_EXISTS)) continue; - if (!(fover->flags & FF_RENDERSIDES)) continue; - // no sloped walls, it's bugged - if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; - - // visible? - if (fover->bottom.plane->Zat0() >= sclipTop) continue; // no - if (fover->top.plane->Zat0() <= sclipBottom) - { // no, last possible - fover = NULL; - break; - } - // it is, render inside? - if (!(fover->flags & (FF_BOTHPLANES|FF_INVERTPLANES))) - { // no - fover = NULL; - } - break; - } - // nothing - if (!fover || (unsigned)j == frontsector->e->XFloor.ffloors.Size()) - { - break; - } - // correct texture - if (fover->flags & rover->flags & FF_SWIMMABLE) - { - rw_pic = DONT_DRAW; // don't ever draw (but treat as something has been found) - } - else if (fover->flags & FF_UPPERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); - } - else if (fover->flags & FF_LOWERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); - } - else - { - rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); - } - } - else if (frontsector->e->XFloor.ffloors.Size()) - { // maybe not visible? - fover = NULL; - for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) - { - fover = frontsector->e->XFloor.ffloors[j]; - if (fover->model == rover->model) - { // never - break; - } - if (!(fover->flags & FF_EXISTS)) continue; - if (!(fover->flags & FF_RENDERSIDES)) continue; - // no sloped walls, its bugged - if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; - - // visible? - if (fover->bottom.plane->Zat0() >= sclipTop) continue; // no - if (fover->top.plane->Zat0() <= sclipBottom) - { // visible, last possible - fover = NULL; - break; - } - if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && - !(!(rover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) - ) - { - break; - } - if (fover->flags & rover->flags & FF_SWIMMABLE) - { // don't ever draw (but treat as something has been found) - rw_pic = DONT_DRAW; - } - fover = NULL; // visible - break; - } - if (fover && (unsigned)j != frontsector->e->XFloor.ffloors.Size()) - { // not visible - break; - } - } - if (rw_pic == NULL) - { - fover = NULL; - if (rover->flags & FF_UPPERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); - } - else if (rover->flags & FF_LOWERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); - } - else - { - rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); - } - } - // correct colors now - basecolormap = frontsector->ColorMap; - wallshade = ds->shade; - if (fixedlightlev < 0) - { - if ((ds->bFakeBoundary & 3) == 2) - { - for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) - { - if (sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) - { - lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - else - { - for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) - { - if(sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) - { - lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - } - - if (rw_pic != DONT_DRAW) - { - R_RenderFakeWall(ds, x1, x2, fover ? fover : rover); - } - else - { - rw_pic = NULL; - } - break; - } - } - return; -} - -// -// R_RenderSegLoop -// Draws zero, one, or two textures for walls. -// Can draw or mark the starting pixel of floor and ceiling textures. -// CALLED: CORE LOOPING ROUTINE. -// - -void R_RenderSegLoop () -{ - int x1 = rw_x; - int x2 = rw_stopx; - int x; - double xscale; - double yscale; - fixed_t xoffset = rw_offset; - - if (fixedlightlev >= 0) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(fixedcolormap, 0, 0); - - // clip wall to the floor and ceiling - for (x = x1; x < x2; ++x) - { - if (walltop[x] < ceilingclip[x]) - { - walltop[x] = ceilingclip[x]; - } - if (wallbottom[x] > floorclip[x]) - { - wallbottom[x] = floorclip[x]; - } - } - - // mark ceiling areas - if (markceiling) - { - for (x = x1; x < x2; ++x) - { - short top = (fakeFloor && fake3D & 2) ? fakeFloor->ceilingclip[x] : ceilingclip[x]; - short bottom = MIN (walltop[x], floorclip[x]); - if (top < bottom) - { - ceilingplane->top[x] = top; - ceilingplane->bottom[x] = bottom; - } - } - } - - // mark floor areas - if (markfloor) - { - for (x = x1; x < x2; ++x) - { - short top = MAX (wallbottom[x], ceilingclip[x]); - short bottom = (fakeFloor && fake3D & 1) ? fakeFloor->floorclip[x] : floorclip[x]; - if (top < bottom) - { - assert (bottom <= viewheight); - floorplane->top[x] = top; - floorplane->bottom[x] = bottom; - } - } - } - - // kg3D - fake planes clipping - if (fake3D & FAKE3D_REFRESHCLIP) - { - if (fake3D & FAKE3D_CLIPBOTFRONT) - { - memcpy (fakeFloor->floorclip+x1, wallbottom+x1, (x2-x1)*sizeof(short)); - } - else - { - for (x = x1; x < x2; ++x) - { - walllower[x] = MIN (MAX (walllower[x], ceilingclip[x]), wallbottom[x]); - } - memcpy (fakeFloor->floorclip+x1, walllower+x1, (x2-x1)*sizeof(short)); - } - if (fake3D & FAKE3D_CLIPTOPFRONT) - { - memcpy (fakeFloor->ceilingclip+x1, walltop+x1, (x2-x1)*sizeof(short)); - } - else - { - for (x = x1; x < x2; ++x) - { - wallupper[x] = MAX (MIN (wallupper[x], floorclip[x]), walltop[x]); - } - memcpy (fakeFloor->ceilingclip+x1, wallupper+x1, (x2-x1)*sizeof(short)); - } - } - if(fake3D & 7) return; - - // draw the wall tiers - if (midtexture) - { // one sided line - if (midtexture->UseType != FTexture::TEX_Null && viewactive) - { - dc_texturemid = rw_midtexturemid; - rw_pic = midtexture; - xscale = rw_pic->Scale.X * rw_midtexturescalex; - yscale = rw_pic->Scale.Y * rw_midtexturescaley; - if (xscale != lwallscale) - { - PrepLWall (lwall, curline->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2); - lwallscale = xscale; - } - if (midtexture->bWorldPanning) - { - rw_offset = xs_RoundToInt(rw_offset_mid * xscale); - } - else - { - rw_offset = rw_offset_mid; - } - if (xscale < 0) - { - rw_offset = -rw_offset; - } - R_DrawWallSegment(rw_pic, x1, x2, walltop, wallbottom, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_frontfz1, rw_frontfz2), false); - } - fillshort (ceilingclip+x1, x2-x1, viewheight); - fillshort (floorclip+x1, x2-x1, 0xffff); - } - else - { // two sided line - if (toptexture != NULL && toptexture->UseType != FTexture::TEX_Null) - { // top wall - for (x = x1; x < x2; ++x) - { - wallupper[x] = MAX (MIN (wallupper[x], floorclip[x]), walltop[x]); - } - if (viewactive) - { - dc_texturemid = rw_toptexturemid; - rw_pic = toptexture; - xscale = rw_pic->Scale.X * rw_toptexturescalex; - yscale = rw_pic->Scale.Y * rw_toptexturescaley; - if (xscale != lwallscale) - { - PrepLWall (lwall, curline->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2); - lwallscale = xscale; - } - if (toptexture->bWorldPanning) - { - rw_offset = xs_RoundToInt(rw_offset_top * xscale); - } - else - { - rw_offset = rw_offset_top; - } - if (xscale < 0) - { - rw_offset = -rw_offset; - } - R_DrawWallSegment(rw_pic, x1, x2, walltop, wallupper, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_backcz1, rw_backcz2), false); - } - memcpy (ceilingclip+x1, wallupper+x1, (x2-x1)*sizeof(short)); - } - else if (markceiling) - { // no top wall - memcpy (ceilingclip+x1, walltop+x1, (x2-x1)*sizeof(short)); - } - - - if (bottomtexture != NULL && bottomtexture->UseType != FTexture::TEX_Null) - { // bottom wall - for (x = x1; x < x2; ++x) - { - walllower[x] = MIN (MAX (walllower[x], ceilingclip[x]), wallbottom[x]); - } - if (viewactive) - { - dc_texturemid = rw_bottomtexturemid; - rw_pic = bottomtexture; - xscale = rw_pic->Scale.X * rw_bottomtexturescalex; - yscale = rw_pic->Scale.Y * rw_bottomtexturescaley; - if (xscale != lwallscale) - { - PrepLWall (lwall, curline->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2); - lwallscale = xscale; - } - if (bottomtexture->bWorldPanning) - { - rw_offset = xs_RoundToInt(rw_offset_bottom * xscale); - } - else - { - rw_offset = rw_offset_bottom; - } - if (xscale < 0) - { - rw_offset = -rw_offset; - } - R_DrawWallSegment(rw_pic, x1, x2, walllower, wallbottom, swall, lwall, yscale, MAX(rw_backfz1, rw_backfz2), MIN(rw_frontfz1, rw_frontfz2), false); - } - memcpy (floorclip+x1, walllower+x1, (x2-x1)*sizeof(short)); - } - else if (markfloor) - { // no bottom wall - memcpy (floorclip+x1, wallbottom+x1, (x2-x1)*sizeof(short)); - } - } - rw_offset = xoffset; -} - -void R_NewWall (bool needlights) -{ - double rowoffset; - double yrepeat; - - rw_markportal = false; - - sidedef = curline->sidedef; - linedef = curline->linedef; - - // mark the segment as visible for auto map - if (!r_dontmaplines) linedef->flags |= ML_MAPPED; - - midtexture = toptexture = bottomtexture = 0; - - if (sidedef == linedef->sidedef[0] && - (linedef->special == Line_Mirror && r_drawmirrors)) // [ZZ] compatibility with r_drawmirrors cvar that existed way before portals - { - markfloor = markceiling = true; // act like a one-sided wall here (todo: check how does this work with transparency) - rw_markportal = true; - } - else if (backsector == NULL) - { - // single sided line - // a single sided line is terminal, so it must mark ends - markfloor = markceiling = true; - // [RH] Horizon lines do not need to be textured - if (linedef->isVisualPortal()) - { - rw_markportal = true; - } - else if (linedef->special != Line_Horizon) - { - midtexture = TexMan(sidedef->GetTexture(side_t::mid), true); - rw_offset_mid = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); - rowoffset = sidedef->GetTextureYOffset(side_t::mid); - rw_midtexturescalex = sidedef->GetTextureXScale(side_t::mid); - rw_midtexturescaley = sidedef->GetTextureYScale(side_t::mid); - yrepeat = midtexture->Scale.Y * rw_midtexturescaley; - if (yrepeat >= 0) - { // normal orientation - if (linedef->flags & ML_DONTPEGBOTTOM) - { // bottom of texture at bottom - rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + midtexture->GetHeight(); - } - else - { // top of texture at top - rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; - if (rowoffset < 0 && midtexture != NULL) - { - rowoffset += midtexture->GetHeight(); - } - } - } - else - { // upside down - rowoffset = -rowoffset; - if (linedef->flags & ML_DONTPEGBOTTOM) - { // top of texture at bottom - rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat; - } - else - { // bottom of texture at top - rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + midtexture->GetHeight(); - } - } - if (midtexture->bWorldPanning) - { - rw_midtexturemid += rowoffset * yrepeat; - } - else - { - // rowoffset is added outside the multiply so that it positions the texture - // by texels instead of world units. - rw_midtexturemid += rowoffset; - } - } - } - else - { // two-sided line - // hack to allow height changes in outdoor areas - - rw_frontlowertop = frontsector->GetPlaneTexZ(sector_t::ceiling); - - if (frontsector->GetTexture(sector_t::ceiling) == skyflatnum && - backsector->GetTexture(sector_t::ceiling) == skyflatnum) - { - if (rw_havehigh) - { // front ceiling is above back ceiling - memcpy (&walltop[WallC.sx1], &wallupper[WallC.sx1], (WallC.sx2 - WallC.sx1)*sizeof(walltop[0])); - rw_havehigh = false; - } - else if (rw_havelow && frontsector->ceilingplane != backsector->ceilingplane) - { // back ceiling is above front ceiling - // The check for rw_havelow is not Doom-compliant, but it avoids HoM that - // would otherwise occur because there is space made available for this - // wall but nothing to draw for it. - // Recalculate walltop so that the wall is clipped by the back sector's - // ceiling instead of the front sector's ceiling. - R_CreateWallSegmentYSloped (walltop, backsector->ceilingplane, &WallC); - } - // Putting sky ceilings on the front and back of a line alters the way unpegged - // positioning works. - rw_frontlowertop = backsector->GetPlaneTexZ(sector_t::ceiling); - } - - if (linedef->isVisualPortal()) - { - markceiling = markfloor = true; - } - else if ((rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2) || - (rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2)) - { - // closed door - markceiling = markfloor = true; - } - else - { - markfloor = rw_mustmarkfloor - || backsector->floorplane != frontsector->floorplane - || backsector->lightlevel != frontsector->lightlevel - || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) - || backsector->GetPlaneLight(sector_t::floor) != frontsector->GetPlaneLight(sector_t::floor) - - // killough 3/7/98: Add checks for (x,y) offsets - || backsector->planes[sector_t::floor].xform != frontsector->planes[sector_t::floor].xform - || backsector->GetAlpha(sector_t::floor) != frontsector->GetAlpha(sector_t::floor) - - // killough 4/15/98: prevent 2s normals - // from bleeding through deep water - || frontsector->heightsec - - || backsector->GetVisFlags(sector_t::floor) != frontsector->GetVisFlags(sector_t::floor) - - // [RH] Add checks for colormaps - || backsector->ColorMap != frontsector->ColorMap - - - // kg3D - add fake lights - || (frontsector->e && frontsector->e->XFloor.lightlist.Size()) - || (backsector->e && backsector->e->XFloor.lightlist.Size()) - - || (sidedef->GetTexture(side_t::mid).isValid() && - ((linedef->flags & (ML_CLIP_MIDTEX|ML_WRAP_MIDTEX)) || - (sidedef->Flags & (WALLF_CLIP_MIDTEX|WALLF_WRAP_MIDTEX)))) - ; - - markceiling = (frontsector->GetTexture(sector_t::ceiling) != skyflatnum || - backsector->GetTexture(sector_t::ceiling) != skyflatnum) && - (rw_mustmarkceiling - || backsector->ceilingplane != frontsector->ceilingplane - || backsector->lightlevel != frontsector->lightlevel - || backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling) - - // killough 3/7/98: Add checks for (x,y) offsets - || backsector->planes[sector_t::ceiling].xform != frontsector->planes[sector_t::ceiling].xform - || backsector->GetAlpha(sector_t::ceiling) != frontsector->GetAlpha(sector_t::ceiling) - - // killough 4/15/98: prevent 2s normals - // from bleeding through fake ceilings - || (frontsector->heightsec && frontsector->GetTexture(sector_t::ceiling) != skyflatnum) - - || backsector->GetPlaneLight(sector_t::ceiling) != frontsector->GetPlaneLight(sector_t::ceiling) - || backsector->GetFlags(sector_t::ceiling) != frontsector->GetFlags(sector_t::ceiling) - - // [RH] Add check for colormaps - || backsector->ColorMap != frontsector->ColorMap - - // kg3D - add fake lights - || (frontsector->e && frontsector->e->XFloor.lightlist.Size()) - || (backsector->e && backsector->e->XFloor.lightlist.Size()) - - || (sidedef->GetTexture(side_t::mid).isValid() && - ((linedef->flags & (ML_CLIP_MIDTEX|ML_WRAP_MIDTEX)) || - (sidedef->Flags & (WALLF_CLIP_MIDTEX|WALLF_WRAP_MIDTEX)))) - ); - } - - if (rw_havehigh) - { // top texture - toptexture = TexMan(sidedef->GetTexture(side_t::top), true); - - rw_offset_top = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::top)); - rowoffset = sidedef->GetTextureYOffset(side_t::top); - rw_toptexturescalex =sidedef->GetTextureXScale(side_t::top); - rw_toptexturescaley =sidedef->GetTextureYScale(side_t::top); - yrepeat = toptexture->Scale.Y * rw_toptexturescaley; - if (yrepeat >= 0) - { // normal orientation - if (linedef->flags & ML_DONTPEGTOP) - { // top of texture at top - rw_toptexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; - if (rowoffset < 0 && toptexture != NULL) - { - rowoffset += toptexture->GetHeight(); - } - } - else - { // bottom of texture at bottom - rw_toptexturemid = (backsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + toptexture->GetHeight(); - } - } - else - { // upside down - rowoffset = -rowoffset; - if (linedef->flags & ML_DONTPEGTOP) - { // bottom of texture at top - rw_toptexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + toptexture->GetHeight(); - } - else - { // top of texture at bottom - rw_toptexturemid = (backsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; - } - } - if (toptexture->bWorldPanning) - { - rw_toptexturemid += rowoffset * yrepeat; - } - else - { - rw_toptexturemid += rowoffset; - } - } - if (rw_havelow) - { // bottom texture - bottomtexture = TexMan(sidedef->GetTexture(side_t::bottom), true); - - rw_offset_bottom = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::bottom)); - rowoffset = sidedef->GetTextureYOffset(side_t::bottom); - rw_bottomtexturescalex = sidedef->GetTextureXScale(side_t::bottom); - rw_bottomtexturescaley = sidedef->GetTextureYScale(side_t::bottom); - yrepeat = bottomtexture->Scale.Y * rw_bottomtexturescaley; - if (yrepeat >= 0) - { // normal orientation - if (linedef->flags & ML_DONTPEGBOTTOM) - { // bottom of texture at bottom - rw_bottomtexturemid = (rw_frontlowertop - ViewPos.Z) * yrepeat; - } - else - { // top of texture at top - rw_bottomtexturemid = (backsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat; - if (rowoffset < 0 && bottomtexture != NULL) - { - rowoffset += bottomtexture->GetHeight(); - } - } - } - else - { // upside down - rowoffset = -rowoffset; - if (linedef->flags & ML_DONTPEGBOTTOM) - { // top of texture at bottom - rw_bottomtexturemid = (rw_frontlowertop - ViewPos.Z) * yrepeat; - } - else - { // bottom of texture at top - rw_bottomtexturemid = (backsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + bottomtexture->GetHeight(); - } - } - if (bottomtexture->bWorldPanning) - { - rw_bottomtexturemid += rowoffset * yrepeat; - } - else - { - rw_bottomtexturemid += rowoffset; - } - } - rw_markportal = linedef->isVisualPortal(); - } - - // if a floor / ceiling plane is on the wrong side of the view plane, - // it is definitely invisible and doesn't need to be marked. - - // killough 3/7/98: add deep water check - if (frontsector->GetHeightSec() == NULL) - { - int planeside; - - planeside = frontsector->floorplane.PointOnSide(ViewPos); - if (frontsector->floorplane.fC() < 0) // 3D floors have the floor backwards - planeside = -planeside; - if (planeside <= 0) // above view plane - markfloor = false; - - if (frontsector->GetTexture(sector_t::ceiling) != skyflatnum) - { - planeside = frontsector->ceilingplane.PointOnSide(ViewPos); - if (frontsector->ceilingplane.fC() > 0) // 3D floors have the ceiling backwards - planeside = -planeside; - if (planeside <= 0) // below view plane - markceiling = false; - } - } - - FTexture *midtex = TexMan(sidedef->GetTexture(side_t::mid), true); - - segtextured = midtex != NULL || toptexture != NULL || bottomtexture != NULL; - - // calculate light table - if (needlights && (segtextured || (backsector && IsFogBoundary(frontsector, backsector)))) - { - lwallscale = - midtex ? (midtex->Scale.X * sidedef->GetTextureXScale(side_t::mid)) : - toptexture ? (toptexture->Scale.X * sidedef->GetTextureXScale(side_t::top)) : - bottomtexture ? (bottomtexture->Scale.X * sidedef->GetTextureXScale(side_t::bottom)) : - 1.; - - PrepWall (swall, lwall, sidedef->TexelLength * lwallscale, WallC.sx1, WallC.sx2); - - if (fixedcolormap == NULL && fixedlightlev < 0) - { - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, frontsector->lightlevel) - + r_actualextralight); - GlobVis = r_WallVisibility; - rw_lightleft = float (GlobVis / WallC.sz1); - rw_lightstep = float((GlobVis / WallC.sz2 - rw_lightleft) / (WallC.sx2 - WallC.sx1)); - } - else - { - rw_lightleft = 1; - rw_lightstep = 0; - } - } -} - - -// -// R_CheckDrawSegs -// - -void R_CheckDrawSegs () -{ - if (ds_p == &drawsegs[MaxDrawSegs]) - { // [RH] Grab some more drawsegs - size_t newdrawsegs = MaxDrawSegs ? MaxDrawSegs*2 : 32; - ptrdiff_t firstofs = firstdrawseg - drawsegs; - drawsegs = (drawseg_t *)M_Realloc (drawsegs, newdrawsegs * sizeof(drawseg_t)); - firstdrawseg = drawsegs + firstofs; - ds_p = drawsegs + MaxDrawSegs; - MaxDrawSegs = newdrawsegs; - DPrintf (DMSG_NOTIFY, "MaxDrawSegs increased to %zu\n", MaxDrawSegs); - } -} - -// -// R_CheckOpenings -// - -ptrdiff_t R_NewOpening (ptrdiff_t len) -{ - ptrdiff_t res = lastopening; - len = (len + 1) & ~1; // only return DWORD aligned addresses because some code stores fixed_t's and floats in openings... - lastopening += len; - if ((size_t)lastopening > maxopenings) - { - do - maxopenings = maxopenings ? maxopenings*2 : 16384; - while ((size_t)lastopening > maxopenings); - openings = (short *)M_Realloc (openings, maxopenings * sizeof(*openings)); - DPrintf (DMSG_NOTIFY, "MaxOpenings increased to %zu\n", maxopenings); - } - return res; -} - - -// -// R_StoreWallRange -// A wall segment will be drawn between start and stop pixels (inclusive). -// - -void R_StoreWallRange (int start, int stop) -{ - int i; - bool maskedtexture = false; - -#ifdef RANGECHECK - if (start >= viewwidth || start >= stop) - I_FatalError ("Bad R_StoreWallRange: %i to %i", start , stop); -#endif - - // don't overflow and crash - R_CheckDrawSegs (); - - if (!rw_prepped) - { - rw_prepped = true; - R_NewWall (true); - } - - rw_offset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); - rw_light = rw_lightleft + rw_lightstep * (start - WallC.sx1); - - ds_p->CurrentPortalUniq = CurrentPortalUniq; - ds_p->sx1 = WallC.sx1; - ds_p->sx2 = WallC.sx2; - ds_p->sz1 = WallC.sz1; - ds_p->sz2 = WallC.sz2; - ds_p->cx = WallC.tleft.X;; - ds_p->cy = WallC.tleft.Y; - ds_p->cdx = WallC.tright.X - WallC.tleft.X; - ds_p->cdy = WallC.tright.Y - WallC.tleft.Y; - ds_p->tmapvals = WallT; - ds_p->siz1 = 1 / WallC.sz1; - ds_p->siz2 = 1 / WallC.sz2; - ds_p->x1 = rw_x = start; - ds_p->x2 = stop; - ds_p->curline = curline; - rw_stopx = stop; - ds_p->bFogBoundary = false; - ds_p->bFakeBoundary = false; - if(fake3D & 7) ds_p->fake = 1; - else ds_p->fake = 0; - - // killough 1/6/98, 2/1/98: remove limit on openings - ds_p->sprtopclip = ds_p->sprbottomclip = ds_p->maskedtexturecol = ds_p->bkup = ds_p->swall = -1; - - if (rw_markportal) - { - ds_p->silhouette = SIL_BOTH; - } - else if (backsector == NULL) - { - ds_p->sprtopclip = R_NewOpening (stop - start); - ds_p->sprbottomclip = R_NewOpening (stop - start); - fillshort (openings + ds_p->sprtopclip, stop-start, viewheight); - memset (openings + ds_p->sprbottomclip, -1, (stop-start)*sizeof(short)); - ds_p->silhouette = SIL_BOTH; - } - else - { - // two sided line - ds_p->silhouette = 0; - - if (rw_frontfz1 > rw_backfz1 || rw_frontfz2 > rw_backfz2 || - backsector->floorplane.PointOnSide(ViewPos) < 0) - { - ds_p->silhouette = SIL_BOTTOM; - } - - if (rw_frontcz1 < rw_backcz1 || rw_frontcz2 < rw_backcz2 || - backsector->ceilingplane.PointOnSide(ViewPos) < 0) - { - ds_p->silhouette |= SIL_TOP; - } - - // killough 1/17/98: this test is required if the fix - // for the automap bug (r_bsp.c) is used, or else some - // sprites will be displayed behind closed doors. That - // fix prevents lines behind closed doors with dropoffs - // from being displayed on the automap. - // - // killough 4/7/98: make doorclosed external variable - - { - extern int doorclosed; // killough 1/17/98, 2/8/98, 4/7/98 - if (doorclosed || (rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2)) - { - ds_p->sprbottomclip = R_NewOpening (stop - start); - memset (openings + ds_p->sprbottomclip, -1, (stop-start)*sizeof(short)); - ds_p->silhouette |= SIL_BOTTOM; - } - if (doorclosed || (rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2)) - { // killough 1/17/98, 2/8/98 - ds_p->sprtopclip = R_NewOpening (stop - start); - fillshort (openings + ds_p->sprtopclip, stop - start, viewheight); - ds_p->silhouette |= SIL_TOP; - } - } - - if(!ds_p->fake && r_3dfloors && backsector->e && backsector->e->XFloor.ffloors.Size()) { - for(i = 0; i < (int)backsector->e->XFloor.ffloors.Size(); i++) { - F3DFloor *rover = backsector->e->XFloor.ffloors[i]; - if(rover->flags & FF_RENDERSIDES && (!(rover->flags & FF_INVERTSIDES) || rover->flags & FF_ALLSIDES)) { - ds_p->bFakeBoundary |= 1; - break; - } - } - } - if(!ds_p->fake && r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size()) { - for(i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++) { - F3DFloor *rover = frontsector->e->XFloor.ffloors[i]; - if(rover->flags & FF_RENDERSIDES && (rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) { - ds_p->bFakeBoundary |= 2; - break; - } - } - } - // kg3D - no for fakes - if(!ds_p->fake) - // allocate space for masked texture tables, if needed - // [RH] Don't just allocate the space; fill it in too. - if ((TexMan(sidedef->GetTexture(side_t::mid), true)->UseType != FTexture::TEX_Null || ds_p->bFakeBoundary || IsFogBoundary (frontsector, backsector)) && - (rw_ceilstat != 12 || !sidedef->GetTexture(side_t::top).isValid()) && - (rw_floorstat != 3 || !sidedef->GetTexture(side_t::bottom).isValid()) && - (WallC.sz1 >= TOO_CLOSE_Z && WallC.sz2 >= TOO_CLOSE_Z)) - { - float *swal; - fixed_t *lwal; - int i; - - maskedtexture = true; - - // kg3D - backup for mid and fake walls - ds_p->bkup = R_NewOpening(stop - start); - memcpy(openings + ds_p->bkup, &ceilingclip[start], sizeof(short)*(stop - start)); - - ds_p->bFogBoundary = IsFogBoundary (frontsector, backsector); - if (sidedef->GetTexture(side_t::mid).isValid() || ds_p->bFakeBoundary) - { - if(sidedef->GetTexture(side_t::mid).isValid()) - ds_p->bFakeBoundary |= 4; // it is also mid texture - - // note: This should never have used the openings array to store its data! - ds_p->maskedtexturecol = R_NewOpening ((stop - start) * 2); - ds_p->swall = R_NewOpening ((stop - start) * 2); - - lwal = (fixed_t *)(openings + ds_p->maskedtexturecol); - swal = (float *)(openings + ds_p->swall); - FTexture *pic = TexMan(sidedef->GetTexture(side_t::mid), true); - double yscale = pic->Scale.Y * sidedef->GetTextureYScale(side_t::mid); - fixed_t xoffset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); - - if (pic->bWorldPanning) - { - xoffset = xs_RoundToInt(xoffset * lwallscale); - } - - for (i = start; i < stop; i++) - { - *lwal++ = lwall[i] + xoffset; - *swal++ = swall[i]; - } - - double istart = *((float *)(openings + ds_p->swall)) * yscale; - double iend = *(swal - 1) * yscale; -#if 0 - ///This was for avoiding overflow when using fixed point. It might not be needed anymore. - const double mini = 3 / 65536.0; - if (istart < mini && istart >= 0) istart = mini; - if (istart > -mini && istart < 0) istart = -mini; - if (iend < mini && iend >= 0) iend = mini; - if (iend > -mini && iend < 0) iend = -mini; -#endif - istart = 1 / istart; - iend = 1 / iend; - ds_p->yscale = (float)yscale; - ds_p->iscale = (float)istart; - if (stop - start > 0) - { - ds_p->iscalestep = float((iend - istart) / (stop - start)); - } - else - { - ds_p->iscalestep = 0; - } - } - ds_p->light = rw_light; - ds_p->lightstep = rw_lightstep; - - // Masked midtextures should get the light level from the sector they reference, - // not from the current subsector, which is what the current wallshade value - // comes from. We make an exeption for polyobjects, however, since their "home" - // sector should be whichever one they move into. - if (curline->sidedef->Flags & WALLF_POLYOBJ) - { - ds_p->shade = wallshade; - } - else - { - ds_p->shade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, curline->frontsector->lightlevel) - + r_actualextralight); - } - - if (ds_p->bFogBoundary || ds_p->maskedtexturecol != -1) - { - size_t drawsegnum = ds_p - drawsegs; - InterestingDrawsegs.Push (drawsegnum); - } - } - } - - // render it - if (markceiling) - { - if (ceilingplane) - { // killough 4/11/98: add NULL ptr checks - ceilingplane = R_CheckPlane (ceilingplane, start, stop); - } - else - { - markceiling = false; - } - } - - if (markfloor) - { - if (floorplane) - { // killough 4/11/98: add NULL ptr checks - floorplane = R_CheckPlane (floorplane, start, stop); - } - else - { - markfloor = false; - } - } - - R_RenderSegLoop (); - - if(fake3D & 7) { - ds_p++; - return; - } - - // save sprite clipping info - if ( ((ds_p->silhouette & SIL_TOP) || maskedtexture) && ds_p->sprtopclip == -1) - { - ds_p->sprtopclip = R_NewOpening (stop - start); - memcpy (openings + ds_p->sprtopclip, &ceilingclip[start], sizeof(short)*(stop-start)); - } - - if ( ((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && ds_p->sprbottomclip == -1) - { - ds_p->sprbottomclip = R_NewOpening (stop - start); - memcpy (openings + ds_p->sprbottomclip, &floorclip[start], sizeof(short)*(stop-start)); - } - - if (maskedtexture && curline->sidedef->GetTexture(side_t::mid).isValid()) - { - ds_p->silhouette |= SIL_TOP | SIL_BOTTOM; - } - - // [RH] Draw any decals bound to the seg - // [ZZ] Only if not an active mirror - if (!rw_markportal) - { - for (DBaseDecal *decal = curline->sidedef->AttachedDecals; decal != NULL; decal = decal->WallNext) - { - R_RenderDecal (curline->sidedef, decal, ds_p, 0); - } - } - - if (rw_markportal) - { - PortalDrawseg pds; - pds.src = curline->linedef; - pds.dst = curline->linedef->special == Line_Mirror? curline->linedef : curline->linedef->getPortalDestination(); - pds.x1 = ds_p->x1; - pds.x2 = ds_p->x2; - pds.len = pds.x2 - pds.x1; - pds.ceilingclip.Resize(pds.len); - memcpy(&pds.ceilingclip[0], openings + ds_p->sprtopclip, pds.len*sizeof(*openings)); - pds.floorclip.Resize(pds.len); - memcpy(&pds.floorclip[0], openings + ds_p->sprbottomclip, pds.len*sizeof(*openings)); - - for (int i = 0; i < pds.x2-pds.x1; i++) - { - if (pds.ceilingclip[i] < 0) - pds.ceilingclip[i] = 0; - if (pds.ceilingclip[i] >= viewheight) - pds.ceilingclip[i] = viewheight-1; - if (pds.floorclip[i] < 0) - pds.floorclip[i] = 0; - if (pds.floorclip[i] >= viewheight) - pds.floorclip[i] = viewheight-1; - } - - pds.mirror = curline->linedef->special == Line_Mirror; - WallPortals.Push(pds); - } - - ds_p++; -} - -int R_CreateWallSegmentY(short *outbuf, double z1, double z2, const FWallCoords *wallc) -{ - float y1 = (float)(CenterY - z1 * InvZtoScale / wallc->sz1); - float y2 = (float)(CenterY - z2 * InvZtoScale / wallc->sz2); - - if (y1 < 0 && y2 < 0) // entire line is above screen - { - memset(&outbuf[wallc->sx1], 0, (wallc->sx2 - wallc->sx1) * sizeof(outbuf[0])); - return 3; - } - else if (y1 > viewheight && y2 > viewheight) // entire line is below screen - { - fillshort(&outbuf[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); - return 12; - } - - if (wallc->sx2 <= wallc->sx1) - return 0; - - float rcp_delta = 1.0f / (wallc->sx2 - wallc->sx1); - if (y1 >= 0.0f && y2 >= 0.0f && xs_RoundToInt(y1) <= viewheight && xs_RoundToInt(y2) <= viewheight) - { - for (int x = wallc->sx1; x < wallc->sx2; x++) - { - float t = (x - wallc->sx1) * rcp_delta; - float y = y1 * (1.0f - t) + y2 * t; - outbuf[x] = (short)xs_RoundToInt(y); - } - } - else - { - for (int x = wallc->sx1; x < wallc->sx2; x++) - { - float t = (x - wallc->sx1) * rcp_delta; - float y = y1 * (1.0f - t) + y2 * t; - outbuf[x] = (short)clamp(xs_RoundToInt(y), 0, viewheight); - } - } - - return 0; -} - -int R_CreateWallSegmentYSloped(short *outbuf, const secplane_t &plane, const FWallCoords *wallc) -{ - if (!plane.isSlope()) - { - return R_CreateWallSegmentY(outbuf, plane.Zat0() - ViewPos.Z, wallc); - } - else - { - // Get Z coordinates at both ends of the line - double x, y, den, z1, z2; - if (MirrorFlags & RF_XFLIP) - { - x = curline->v2->fX(); - y = curline->v2->fY(); - if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) - { - double frac = (wallc->tleft.Y + wallc->tleft.X) / den; - x -= frac * (x - curline->v1->fX()); - y -= frac * (y - curline->v1->fY()); - } - z1 = plane.ZatPoint(x, y) - ViewPos.Z; - - if (wallc->sx2 > wallc->sx1 + 1) - { - x = curline->v1->fX(); - y = curline->v1->fY(); - if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) - { - double frac = (wallc->tright.Y - wallc->tright.X) / den; - x += frac * (curline->v2->fX() - x); - y += frac * (curline->v2->fY() - y); - } - z2 = plane.ZatPoint(x, y) - ViewPos.Z; - } - else - { - z2 = z1; - } - } - else - { - x = curline->v1->fX(); - y = curline->v1->fY(); - if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) - { - double frac = (wallc->tleft.Y + wallc->tleft.X) / den; - x += frac * (curline->v2->fX() - x); - y += frac * (curline->v2->fY() - y); - } - z1 = plane.ZatPoint(x, y) - ViewPos.Z; - - if (wallc->sx2 > wallc->sx1 + 1) - { - x = curline->v2->fX(); - y = curline->v2->fY(); - if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) - { - double frac = (wallc->tright.Y - wallc->tright.X) / den; - x -= frac * (x - curline->v1->fX()); - y -= frac * (y - curline->v1->fY()); - } - z2 = plane.ZatPoint(x, y) - ViewPos.Z; - } - else - { - z2 = z1; - } - } - - return R_CreateWallSegmentY(outbuf, z1, z2, wallc); - } -} - -void PrepWall(float *vstep, fixed_t *upos, double walxrepeat, int x1, int x2) -{ - float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - CenterX); - float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - CenterX); - float uGradient = WallT.UoverZstep; - float zGradient = WallT.InvZstep; - float xrepeat = (float)fabs(walxrepeat); - float depthScale = (float)(WallT.InvZstep * WallTMapScale2); - float depthOrg = (float)(-WallT.UoverZstep * WallTMapScale2); - - if (walxrepeat < 0.0) - { - for (int x = x1; x < x2; x++) - { - float u = uOverZ / invZ; - - upos[x] = (fixed_t)((xrepeat - u * xrepeat) * FRACUNIT); - vstep[x] = depthOrg + u * depthScale; - - uOverZ += uGradient; - invZ += zGradient; - } - } - else - { - for (int x = x1; x < x2; x++) - { - float u = uOverZ / invZ; - - upos[x] = (fixed_t)(u * xrepeat * FRACUNIT); - vstep[x] = depthOrg + u * depthScale; - - uOverZ += uGradient; - invZ += zGradient; - } - } -} - -void PrepLWall(fixed_t *upos, double walxrepeat, int x1, int x2) -{ - float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - CenterX); - float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - CenterX); - float uGradient = WallT.UoverZstep; - float zGradient = WallT.InvZstep; - float xrepeat = (float)fabs(walxrepeat); - - if (walxrepeat < 0.0f) - { - for (int x = x1; x < x2; x++) - { - float u = uOverZ / invZ * xrepeat - xrepeat; - - upos[x] = (fixed_t)(u * FRACUNIT); - - uOverZ += uGradient; - invZ += zGradient; - } - } - else - { - for (int x = x1; x < x2; x++) - { - float u = uOverZ / invZ * xrepeat; - - upos[x] = (fixed_t)(u * FRACUNIT); - - uOverZ += uGradient; - invZ += zGradient; - } - } -} - -// pass = 0: when seg is first drawn -// = 1: drawing masked textures (including sprites) -// Currently, only pass = 0 is done or used - -static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, int pass) -{ - DVector2 decal_left, decal_right, decal_pos; - int x1, x2; - double yscale; - BYTE flipx; - double zpos; - int needrepeat = 0; - sector_t *front, *back; - bool calclighting; - bool rereadcolormap; - FDynamicColormap *usecolormap; - - if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid()) - return; - - // Determine actor z - zpos = decal->Z; - front = curline->frontsector; - back = (curline->backsector != NULL) ? curline->backsector : curline->frontsector; - switch (decal->RenderFlags & RF_RELMASK) - { - default: - zpos = decal->Z; - break; - case RF_RELUPPER: - if (curline->linedef->flags & ML_DONTPEGTOP) - { - zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); - } - else - { - zpos = decal->Z + back->GetPlaneTexZ(sector_t::ceiling); - } - break; - case RF_RELLOWER: - if (curline->linedef->flags & ML_DONTPEGBOTTOM) - { - zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); - } - else - { - zpos = decal->Z + back->GetPlaneTexZ(sector_t::floor); - } - break; - case RF_RELMID: - if (curline->linedef->flags & ML_DONTPEGBOTTOM) - { - zpos = decal->Z + front->GetPlaneTexZ(sector_t::floor); - } - else - { - zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); - } - } - - WallSpriteTile = TexMan(decal->PicNum, true); - flipx = (BYTE)(decal->RenderFlags & RF_XFLIP); - - if (WallSpriteTile == NULL || WallSpriteTile->UseType == FTexture::TEX_Null) - { - return; - } - - // Determine left and right edges of sprite. Since this sprite is bound - // to a wall, we use the wall's angle instead of the decal's. This is - // pretty much the same as what R_AddLine() does. - - FWallCoords savecoord = WallC; - - double edge_right = WallSpriteTile->GetWidth(); - double edge_left = WallSpriteTile->LeftOffset; - edge_right = (edge_right - edge_left) * decal->ScaleX; - edge_left *= decal->ScaleX; - - double dcx, dcy; - decal->GetXY(wall, dcx, dcy); - decal_pos = { dcx, dcy }; - - DVector2 angvec = (curline->v2->fPos() - curline->v1->fPos()).Unit(); - - decal_left = decal_pos - edge_left * angvec - ViewPos; - decal_right = decal_pos + edge_right * angvec - ViewPos; - - if (WallC.Init(decal_left, decal_right, TOO_CLOSE_Z)) - goto done; - - x1 = WallC.sx1; - x2 = WallC.sx2; - - if (x1 >= clipper->x2 || x2 <= clipper->x1) - goto done; - - WallT.InitFromWallCoords(&WallC); - - // Get the top and bottom clipping arrays - switch (decal->RenderFlags & RF_CLIPMASK) - { - case RF_CLIPFULL: - if (curline->backsector == NULL) - { - if (pass != 0) - { - goto done; - } - mceilingclip = walltop; - mfloorclip = wallbottom; - } - else if (pass == 0) - { - mceilingclip = walltop; - mfloorclip = ceilingclip; - needrepeat = 1; - } - else - { - mceilingclip = openings + clipper->sprtopclip - clipper->x1; - mfloorclip = openings + clipper->sprbottomclip - clipper->x1; - } - break; - - case RF_CLIPUPPER: - if (pass != 0) - { - goto done; - } - mceilingclip = walltop; - mfloorclip = ceilingclip; - break; - - case RF_CLIPMID: - if (curline->backsector != NULL && pass != 2) - { - goto done; - } - mceilingclip = openings + clipper->sprtopclip - clipper->x1; - mfloorclip = openings + clipper->sprbottomclip - clipper->x1; - break; - - case RF_CLIPLOWER: - if (pass != 0) - { - goto done; - } - mceilingclip = floorclip; - mfloorclip = wallbottom; - break; - } - - yscale = decal->ScaleY; - dc_texturemid = WallSpriteTile->TopOffset + (zpos - ViewPos.Z) / yscale; - - // Clip sprite to drawseg - x1 = MAX(clipper->x1, x1); - x2 = MIN(clipper->x2, x2); - if (x1 >= x2) - { - goto done; - } - - PrepWall (swall, lwall, WallSpriteTile->GetWidth(), x1, x2); - - if (flipx) - { - int i; - int right = (WallSpriteTile->GetWidth() << FRACBITS) - 1; - - for (i = x1; i < x2; i++) - { - lwall[i] = right - lwall[i]; - } - } - - // Prepare lighting - calclighting = false; - usecolormap = basecolormap; - rereadcolormap = true; - - // Decals that are added to the scene must fade to black. - if (decal->RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) - { - usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); - rereadcolormap = false; - } - - rw_light = rw_lightleft + (x1 - savecoord.sx1) * rw_lightstep; - if (fixedlightlev >= 0) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(fixedcolormap, 0, 0); - else if (!foggy && (decal->RenderFlags & RF_FULLBRIGHT)) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, 0); - else - calclighting = true; - - // Draw it - if (decal->RenderFlags & RF_YFLIP) - { - sprflipvert = true; - yscale = -yscale; - dc_texturemid -= WallSpriteTile->GetHeight(); - } - else - { - sprflipvert = false; - } - - MaskedScaleY = float(1 / yscale); - do - { - dc_x = x1; - ESPSResult mode; - - mode = R_SetPatchStyle (decal->RenderStyle, (float)decal->Alpha, decal->Translation, decal->AlphaColor); - - // R_SetPatchStyle can modify basecolormap. - if (rereadcolormap) - { - usecolormap = basecolormap; - } - - if (mode == DontDraw) - { - needrepeat = 0; - } - else - { - int stop4; - - if (mode == DoDraw0) - { // 1 column at a time - stop4 = dc_x; - } - else // DoDraw1 - { // up to 4 columns at a time - stop4 = x2 & ~3; - } - - while ((dc_x < stop4) && (dc_x & 3)) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, wallshade); - } - R_WallSpriteColumn (false); - dc_x++; - } - - while (dc_x < stop4) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, wallshade); - } - rt_initcols(nullptr); - for (int zz = 4; zz; --zz) - { - R_WallSpriteColumn (true); - dc_x++; - } - rt_draw4cols (dc_x - 4); - } - - while (dc_x < x2) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, wallshade); - } - R_WallSpriteColumn (false); - dc_x++; - } - } - - // If this sprite is RF_CLIPFULL on a two-sided line, needrepeat will - // be set 1 if we need to draw on the lower wall. In all other cases, - // needrepeat will be 0, and the while will fail. - mceilingclip = floorclip; - mfloorclip = wallbottom; - R_FinishSetPatchStyle (); - } while (needrepeat--); - - colfunc = basecolfunc; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; - - R_FinishSetPatchStyle (); -done: - WallC = savecoord; -} - -} diff --git a/src/r_segs.h b/src/r_segs.h deleted file mode 100644 index 8f552daeee..0000000000 --- a/src/r_segs.h +++ /dev/null @@ -1,83 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Refresh module, drawing LineSegs from BSP. -// -//----------------------------------------------------------------------------- - - -#ifndef __R_SEGS_H__ -#define __R_SEGS_H__ - -namespace swrenderer -{ - -struct drawseg_t; - -void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2); - -extern short *openings; -extern ptrdiff_t lastopening; -extern size_t maxopenings; - -int R_CreateWallSegmentY (short *outbuf, double z1, double z2, const FWallCoords *wallc); -int R_CreateWallSegmentYSloped (short *outbuf, const secplane_t &plane, const FWallCoords *wallc); -inline int R_CreateWallSegmentY(short *outbuf, double z, const FWallCoords *wallc) -{ - return R_CreateWallSegmentY(outbuf, z, z, wallc); -} - -void PrepWall (float *swall, fixed_t *lwall, double walxrepeat, int x1, int x2); -void PrepLWall (fixed_t *lwall, double walxrepeat, int x1, int x2); - -ptrdiff_t R_NewOpening (ptrdiff_t len); - -void R_CheckDrawSegs (); - -void R_RenderSegLoop (); - -extern float swall[MAXWIDTH]; -extern fixed_t lwall[MAXWIDTH]; -extern float rw_light; // [RH] Scale lights with viewsize adjustments -extern float rw_lightstep; -extern float rw_lightleft; -extern fixed_t rw_offset; - -/* portal structure, this is used in r_ code in order to store drawsegs with portals (and mirrors) */ -struct PortalDrawseg -{ - line_t* src; // source line (the one drawn) this doesn't change over render loops - line_t* dst; // destination line (the one that the portal is linked with, equals 'src' for mirrors) - - int x1; // drawseg x1 - int x2; // drawseg x2 - - int len; - TArray ceilingclip; - TArray floorclip; - - bool mirror; // true if this is a mirror (src should equal dst) -}; - -extern PortalDrawseg* CurrentPortal; -extern int CurrentPortalUniq; -extern bool CurrentPortalInSkybox; -extern TArray WallPortals; - -} - -#endif diff --git a/src/r_sky.cpp b/src/r_sky.cpp index f2804bad77..c3d8de1a07 100644 --- a/src/r_sky.cpp +++ b/src/r_sky.cpp @@ -133,8 +133,8 @@ void R_InitSkyMap () skyiscale = float(r_Yaspect / freelookviewheight); skyscale = freelookviewheight / r_Yaspect; - skyiscale *= float(FieldOfView.Degrees / 90.); - skyscale *= float(90. / FieldOfView.Degrees); + skyiscale *= float(r_viewpoint.FieldOfView.Degrees / 90.); + skyscale *= float(90. / r_viewpoint.FieldOfView.Degrees); } if (skystretch) @@ -161,7 +161,7 @@ void R_InitSkyMap () // //========================================================================== -void R_UpdateSky (DWORD mstime) +void R_UpdateSky (uint32_t mstime) { // Scroll the sky double ms = (double)mstime * FRACUNIT; diff --git a/src/r_sky.h b/src/r_sky.h index 1328179836..b30acbd7e3 100644 --- a/src/r_sky.h +++ b/src/r_sky.h @@ -38,6 +38,6 @@ extern int freelookviewheight; // Called whenever the sky changes. void R_InitSkyMap (); -void R_UpdateSky (DWORD mstime); +void R_UpdateSky (uint32_t mstime); #endif //__R_SKY_H__ diff --git a/src/r_state.h b/src/r_state.h index 7151c0441b..6223c40b47 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -33,14 +33,16 @@ // for rendering. // -extern "C" int viewwidth; -extern "C" int viewheight; +extern int viewwindowx; +extern int viewwindowy; +extern int viewwidth; +extern int viewheight; // // Lookup tables for map data. // extern TArray sprites; -extern DWORD NumStdSprites; +extern uint32_t NumStdSprites; extern TArray vertexdatas; @@ -65,11 +67,6 @@ extern int numgamesubsectors; // // POV data. // -extern AActor* camera; // [RH] camera instead of viewplayer -extern sector_t* viewsector; // [RH] keep track of sector viewing from - -namespace swrenderer { extern angle_t xtoviewangle[MAXWIDTH+1]; } -extern DAngle FieldOfView; int R_FindSkin (const char *name, int pclass); // [RH] Find a skin diff --git a/src/r_swrenderer.cpp b/src/r_swrenderer.cpp deleted file mode 100644 index 87bce4013a..0000000000 --- a/src/r_swrenderer.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/* -** r_swrender.cpp -** Software renderer interface -** -**--------------------------------------------------------------------------- -** Copyright 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. -** -** 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 "r_local.h" -#include "v_palette.h" -#include "v_video.h" -#include "m_png.h" -#include "r_bsp.h" -#include "r_swrenderer.h" -#include "r_3dfloors.h" -#include "textures/textures.h" -#include "r_data/voxels.h" -#include "r_thread.h" - -namespace swrenderer -{ - -void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio); -void R_SetupColormap(player_t *); -void R_SetupFreelook(); -void R_InitRenderer(); - -} - -using namespace swrenderer; - -//========================================================================== -// -// DCanvas :: Init -// -//========================================================================== - -void FSoftwareRenderer::Init() -{ - R_InitRenderer(); -} - -//========================================================================== -// -// DCanvas :: UsesColormap -// -//========================================================================== - -bool FSoftwareRenderer::UsesColormap() const -{ - return true; -} - -//=========================================================================== -// -// Texture precaching -// -//=========================================================================== - -void FSoftwareRenderer::PrecacheTexture(FTexture *tex, int cache) -{ - if (tex != NULL) - { - if (cache & FTextureManager::HIT_Columnmode) - { - const FTexture::Span *spanp; - tex->GetColumn(0, &spanp); - } - else if (cache != 0) - { - tex->GetPixels (); - } - else - { - tex->Unload (); - } - } -} - -void FSoftwareRenderer::Precache(BYTE *texhitlist, TMap &actorhitlist) -{ - BYTE *spritelist = new BYTE[sprites.Size()]; - TMap::Iterator it(actorhitlist); - TMap::Pair *pair; - - memset(spritelist, 0, sprites.Size()); - - while (it.NextPair(pair)) - { - PClassActor *cls = pair->Key; - - for (int i = 0; i < cls->NumOwnedStates; i++) - { - spritelist[cls->OwnedStates[i].sprite] = true; - } - } - - // Precache textures (and sprites). - - for (int i = (int)(sprites.Size() - 1); i >= 0; i--) - { - if (spritelist[i]) - { - int j, k; - for (j = 0; j < sprites[i].numframes; j++) - { - const spriteframe_t *frame = &SpriteFrames[sprites[i].spriteframes + j]; - - for (k = 0; k < 16; k++) - { - FTextureID pic = frame->Texture[k]; - if (pic.isValid()) - { - texhitlist[pic.GetIndex()] = FTextureManager::HIT_Sprite; - } - } - } - } - } - delete[] spritelist; - - int cnt = TexMan.NumTextures(); - for (int i = cnt - 1; i >= 0; i--) - { - PrecacheTexture(TexMan.ByIndex(i), texhitlist[i]); - } -} - -//=========================================================================== -// -// Render the view -// -//=========================================================================== - -void FSoftwareRenderer::RenderView(player_t *player) -{ - R_BeginDrawerCommands(); - R_RenderActorView (player->mo); - // [RH] Let cameras draw onto textures that were visible this frame. - FCanvasTextureInfo::UpdateAll (); - R_EndDrawerCommands(); -} - -//========================================================================== -// -// -// -//========================================================================== - -void FSoftwareRenderer::RemapVoxels() -{ - for (unsigned i=0; iRemap(); - } -} - -//=========================================================================== -// -// Render the view to a savegame picture -// -//=========================================================================== - -void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) -{ - DCanvas *pic = new DSimpleCanvas (width, height); - PalEntry palette[256]; - - // Take a snapshot of the player's view - pic->ObjectFlags |= OF_Fixed; - pic->Lock (); - R_RenderViewToCanvas (player->mo, pic, 0, 0, width, height); - screen->GetFlashedPalette (palette); - M_CreatePNG (file, pic->GetBuffer(), palette, SS_PAL, width, height, pic->GetPitch()); - pic->Unlock (); - pic->Destroy(); - pic->ObjectFlags |= OF_YesReallyDelete; - delete pic; -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::DrawRemainingPlayerSprites() -{ - R_DrawRemainingPlayerSprites(); -} - -//=========================================================================== -// -// Get max. view angle (renderer specific information so it goes here now) -// -//=========================================================================== -#define MAX_DN_ANGLE 56 // Max looking down angle -#define MAX_UP_ANGLE 32 // Max looking up angle - -int FSoftwareRenderer::GetMaxViewPitch(bool down) -{ - return down ? MAX_DN_ANGLE : MAX_UP_ANGLE; -} - -//========================================================================== -// -// OnModeSet -// -// Called from V_SetResolution() -// -//========================================================================== - -void FSoftwareRenderer::OnModeSet () -{ - R_MultiresInit (); - - RenderTarget = screen; - screen->Lock (true); - R_SetupBuffer (); - screen->Unlock (); -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::ErrorCleanup () -{ - fakeActive = 0; - fake3D = 0; - while (CurrentSkybox) - { - R_3D_DeleteHeights(); - R_3D_LeaveSkybox(); - } - R_3D_ResetClip(); - R_3D_DeleteHeights(); -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::ClearBuffer(int color) -{ - memset(RenderTarget->GetBuffer(), color, RenderTarget->GetPitch() * RenderTarget->GetHeight()); -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) -{ - R_SWRSetWindow(windowSize, fullWidth, fullHeight, stHeight, trueratio); -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::SetupFrame(player_t *player) -{ - R_SetupColormap(player); - R_SetupFreelook(); -} - -//========================================================================== -// -// R_CopyStackedViewParameters -// -//========================================================================== - -void FSoftwareRenderer::CopyStackedViewParameters() -{ - R_CopyStackedViewParameters(); -} - -//========================================================================== -// -// -// -//========================================================================== - -void FSoftwareRenderer::RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) -{ - BYTE *Pixels = const_cast(tex->GetPixels()); - DSimpleCanvas *Canvas = tex->GetCanvas(); - - // curse Doom's overuse of global variables in the renderer. - // These get clobbered by rendering to a camera texture but they need to be preserved so the final rendering can be done with the correct palette. - unsigned char *savecolormap = fixedcolormap; - FSpecialColormap *savecm = realfixedcolormap; - - DAngle savedfov = FieldOfView; - R_SetFOV ((double)fov); - R_RenderViewToCanvas (viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), tex->bFirstUpdate); - R_SetFOV (savedfov); - if (Pixels == Canvas->GetBuffer()) - { - FTexture::FlipSquareBlockRemap (Pixels, tex->GetWidth(), tex->GetHeight(), GPalette.Remap); - } - else - { - FTexture::FlipNonSquareBlockRemap (Pixels, Canvas->GetBuffer(), tex->GetWidth(), tex->GetHeight(), Canvas->GetPitch(), GPalette.Remap); - } - tex->SetUpdated(); - fixedcolormap = savecolormap; - realfixedcolormap = savecm; -} - -//========================================================================== -// -// -// -//========================================================================== - -sector_t *FSoftwareRenderer::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) -{ - return R_FakeFlat(sec, tempsec, floorlightlevel, ceilinglightlevel, back); -} - diff --git a/src/r_swrenderer.h b/src/r_swrenderer.h deleted file mode 100644 index f9d5609a0d..0000000000 --- a/src/r_swrenderer.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __R_SWRENDERER_H -#define __R_SWRENDERER_H - -#include "r_renderer.h" - -struct FSoftwareRenderer : public FRenderer -{ - // Can be overridden so that the colormaps for sector color/fade won't be built. - virtual bool UsesColormap() const override; - - // precache one texture - void PrecacheTexture(FTexture *tex, int cache); - virtual void Precache(BYTE *texhitlist, TMap &actorhitlist) override; - - // render 3D view - virtual void RenderView(player_t *player) override; - - // Remap voxel palette - virtual void RemapVoxels() override; - - // renders view to a savegame picture - virtual void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override; - - // draws player sprites with hardware acceleration (only useful for software rendering) - virtual void DrawRemainingPlayerSprites() override; - - virtual int GetMaxViewPitch(bool down) override; - - void OnModeSet () override; - void ErrorCleanup () override; - void ClearBuffer(int color) override; - void Init() override; - void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) override; - void SetupFrame(player_t *player) override; - void CopyStackedViewParameters() override; - void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) override; - sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) override; - -}; - - -#endif diff --git a/src/r_things.cpp b/src/r_things.cpp deleted file mode 100644 index 76dae9eea3..0000000000 --- a/src/r_things.cpp +++ /dev/null @@ -1,3263 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// $Log:$ -// -// DESCRIPTION: -// Refresh of things, i.e. objects represented by sprites. -// -// This file contains some code from the Build Engine. -// -// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman -// Ken Silverman's official web site: "http://www.advsys.net/ken" -// See the included license file "BUILDLIC.TXT" for license info. -// -//----------------------------------------------------------------------------- - -#include -#include -#include - -#include "p_lnspec.h" -#include "templates.h" -#include "doomdef.h" -#include "m_swap.h" -#include "i_system.h" -#include "w_wad.h" -#include "r_local.h" -#include "c_console.h" -#include "c_cvars.h" -#include "c_dispatch.h" -#include "doomstat.h" -#include "v_video.h" -#include "sc_man.h" -#include "s_sound.h" -#include "sbar.h" -#include "gi.h" -#include "r_sky.h" -#include "cmdlib.h" -#include "g_level.h" -#include "d_net.h" -#include "colormatcher.h" -#include "d_netinf.h" -#include "p_effect.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "v_palette.h" -#include "r_data/r_translate.h" -#include "r_data/colormaps.h" -#include "r_data/voxels.h" -#include "p_local.h" -#include "p_maputl.h" -#include "g_levellocals.h" -#include "r_thread.h" -#include "events.h" - -EXTERN_CVAR(Bool, st_scale) -EXTERN_CVAR(Bool, r_shadercolormaps) -EXTERN_CVAR(Int, r_drawfuzz) -EXTERN_CVAR(Bool, r_deathcamera); -EXTERN_CVAR(Bool, r_drawplayersprites) -EXTERN_CVAR(Bool, r_drawvoxels) - -CVAR(Bool, r_fullbrightignoresectorcolor, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -//CVAR(Bool, r_splitsprites, true, CVAR_ARCHIVE) - -namespace swrenderer -{ - using namespace drawerargs; - -// [RH] A c-buffer. Used for keeping track of offscreen voxel spans. - -struct FCoverageBuffer -{ - struct Span - { - Span *NextSpan; - short Start, Stop; - }; - - FCoverageBuffer(int size); - ~FCoverageBuffer(); - - void Clear(); - void InsertSpan(int listnum, int start, int stop); - Span *AllocSpan(); - - FMemArena SpanArena; - Span **Spans; // [0..NumLists-1] span lists - Span *FreeSpans; - unsigned int NumLists; -}; - -extern double globaluclip, globaldclip; -extern float MaskedScaleY; - -#define MINZ double((2048*4) / double(1 << 20)) -#define BASEXCENTER (160) -#define BASEYCENTER (100) - -// -// Sprite rotation 0 is facing the viewer, -// rotation 1 is one angle turn CLOCKWISE around the axis. -// This is not the same as the angle, -// which increases counter clockwise (protractor). -// -double pspritexscale; -double pspritexiscale; -double pspriteyscale; -fixed_t sky1scale; // [RH] Sky 1 scale factor -fixed_t sky2scale; // [RH] Sky 2 scale factor - -// Used to store a psprite's drawing information if it needs to be drawn later. -struct vispsp_t -{ - vissprite_t *vis; - FDynamicColormap *basecolormap; - int x1; -}; -TArray vispsprites; -unsigned int vispspindex; - -static int spriteshade; - -FTexture *WallSpriteTile; - -// constant arrays -// used for psprite clipping and initializing clipping -short zeroarray[MAXWIDTH]; -short screenheightarray[MAXWIDTH]; - -// -// INITIALIZATION FUNCTIONS -// - -int OffscreenBufferWidth, OffscreenBufferHeight; -BYTE *OffscreenColorBuffer; -FCoverageBuffer *OffscreenCoverageBuffer; - -// - -// GAME FUNCTIONS -// -int MaxVisSprites; -vissprite_t **vissprites; -vissprite_t **firstvissprite; -vissprite_t **vissprite_p; -vissprite_t **lastvissprite; -int newvissprite; -bool DrewAVoxel; - -static vissprite_t **spritesorter; -static int spritesortersize = 0; -static int vsprcount; - -static void R_ProjectWallSprite(AActor *thing, const DVector3 &pos, FTextureID picnum, const DVector2 &scale, INTBOOL flip); - - - -void R_DeinitSprites() -{ - // Free vissprites - for (int i = 0; i < MaxVisSprites; ++i) - { - delete vissprites[i]; - } - free (vissprites); - vissprites = NULL; - vissprite_p = lastvissprite = NULL; - MaxVisSprites = 0; - - // Free vissprites sorter - if (spritesorter != NULL) - { - delete[] spritesorter; - spritesortersize = 0; - spritesorter = NULL; - } - - // Free offscreen buffer - if (OffscreenColorBuffer != NULL) - { - delete[] OffscreenColorBuffer; - OffscreenColorBuffer = NULL; - } - if (OffscreenCoverageBuffer != NULL) - { - delete OffscreenCoverageBuffer; - OffscreenCoverageBuffer = NULL; - } - OffscreenBufferHeight = OffscreenBufferWidth = 0; -} - -// -// R_ClearSprites -// Called at frame start. -// -void R_ClearSprites (void) -{ - vissprite_p = firstvissprite; - DrewAVoxel = false; -} - - -// -// R_NewVisSprite -// -vissprite_t *R_NewVisSprite (void) -{ - if (vissprite_p == lastvissprite) - { - ptrdiff_t firstvisspritenum = firstvissprite - vissprites; - ptrdiff_t prevvisspritenum = vissprite_p - vissprites; - - MaxVisSprites = MaxVisSprites ? MaxVisSprites * 2 : 128; - vissprites = (vissprite_t **)M_Realloc (vissprites, MaxVisSprites * sizeof(vissprite_t)); - lastvissprite = &vissprites[MaxVisSprites]; - firstvissprite = &vissprites[firstvisspritenum]; - vissprite_p = &vissprites[prevvisspritenum]; - DPrintf (DMSG_NOTIFY, "MaxVisSprites increased to %d\n", MaxVisSprites); - - // Allocate sprites from the new pile - for (vissprite_t **p = vissprite_p; p < lastvissprite; ++p) - { - *p = new vissprite_t; - } - } - - vissprite_p++; - return *(vissprite_p-1); -} - -// -// R_DrawMaskedColumn -// Used for sprites and masked mid textures. -// Masked means: partly transparent, i.e. stored -// in posts/runs of opaque pixels. -// -short* mfloorclip; -short* mceilingclip; - -double spryscale; -double sprtopscreen; - -bool sprflipvert; - -void R_DrawMaskedColumn (FTexture *tex, fixed_t col, bool useRt, bool unmasked) -{ - const FTexture::Span *span; - const BYTE *column; - - column = tex->GetColumn(col >> FRACBITS, &span); - - FTexture::Span unmaskedSpan[2]; - if (unmasked) - { - span = unmaskedSpan; - unmaskedSpan[0].TopOffset = 0; - unmaskedSpan[0].Length = tex->GetHeight(); - unmaskedSpan[1].TopOffset = 0; - unmaskedSpan[1].Length = 0; - } - - while (span->Length != 0) - { - const int length = span->Length; - const int top = span->TopOffset; - - // calculate unclipped screen coordinates for post - dc_yl = (int)(sprtopscreen + spryscale * top + 0.5); - dc_yh = (int)(sprtopscreen + spryscale * (top + length) + 0.5) - 1; - - if (sprflipvert) - { - swapvalues (dc_yl, dc_yh); - } - - if (dc_yh >= mfloorclip[dc_x]) - { - dc_yh = mfloorclip[dc_x] - 1; - } - if (dc_yl < mceilingclip[dc_x]) - { - dc_yl = mceilingclip[dc_x]; - } - - if (dc_yl <= dc_yh) - { - dc_texturefrac = FLOAT2FIXED((dc_yl + 0.5 - sprtopscreen) / spryscale); - dc_source = column; - dc_dest = (ylookup[dc_yl] + dc_x) + dc_destorg; - dc_count = dc_yh - dc_yl + 1; - - fixed_t maxfrac = ((top + length) << FRACBITS) - 1; - dc_texturefrac = MAX(dc_texturefrac, 0); - dc_texturefrac = MIN(dc_texturefrac, maxfrac); - if (dc_iscale > 0) - dc_count = MIN(dc_count, (maxfrac - dc_texturefrac + dc_iscale - 1) / dc_iscale); - else if (dc_iscale < 0) - dc_count = MIN(dc_count, (dc_texturefrac - dc_iscale) / (-dc_iscale)); - - if (useRt) - hcolfunc_pre(); - else - colfunc (); - } - span++; - } - - if (sprflipvert && useRt) - rt_flip_posts(); -} - -// [ZZ] -// R_ClipSpriteColumnWithPortals -// - -static TArray portaldrawsegs; - -static inline void R_CollectPortals() -{ - // This function collects all drawsegs that may be of interest to R_ClipSpriteColumnWithPortals - // Having that function over the entire list of drawsegs can break down performance quite drastically. - // This is doing the costly stuff only once so that R_ClipSpriteColumnWithPortals can - // a) exit early if no relevant info is found and - // b) skip most of the collected drawsegs which have no portal attached. - portaldrawsegs.Clear(); - for (drawseg_t* seg = ds_p; seg-- > firstdrawseg; ) // copied code from killough below - { - // I don't know what makes this happen (some old top-down portal code or possibly skybox code? something adds null lines...) - // crashes at the first frame of the first map of Action2.wad - if (!seg->curline) continue; - - line_t* line = seg->curline->linedef; - // ignore minisegs from GL nodes. - if (!line) continue; - - // check if this line will clip sprites to itself - if (!line->isVisualPortal() && line->special != Line_Mirror) - continue; - - // don't clip sprites with portal's back side (it's transparent) - if (seg->curline->sidedef != line->sidedef[0]) - continue; - - portaldrawsegs.Push(seg); - } -} - -static inline bool R_ClipSpriteColumnWithPortals(vissprite_t* spr) -{ - // [ZZ] 10.01.2016: don't clip sprites from the root of a skybox. - if (CurrentPortalInSkybox) - return false; - - for (drawseg_t *seg : portaldrawsegs) - { - // ignore segs from other portals - if (seg->CurrentPortalUniq != CurrentPortalUniq) - continue; - - // (all checks that are already done in R_CollectPortals have been removed for performance reasons.) - - // don't clip if the sprite is in front of the portal - if (!P_PointOnLineSidePrecise(spr->gpos.X, spr->gpos.Y, seg->curline->linedef)) - continue; - - // now if current column is covered by this drawseg, we clip it away - if ((dc_x >= seg->x1) && (dc_x < seg->x2)) - return true; - } - - return false; -} - - -// -// R_DrawVisSprite -// mfloorclip and mceilingclip should also be set. -// -void R_DrawVisSprite (vissprite_t *vis) -{ - fixed_t frac; - FTexture *tex; - int x2, stop4; - fixed_t xiscale; - ESPSResult mode; - bool ispsprite = (!vis->sector && vis->gpos != FVector3(0, 0, 0)); - - if (vis->xscale == 0 || fabs(vis->yscale) < (1.0f / 32000.0f)) - { // scaled to 0; can't see - return; - } - - fixed_t centeryfrac = FLOAT2FIXED(CenterY); - R_SetColorMapLight(vis->colormap, 0.0f, 0); - - mode = R_SetPatchStyle (vis->RenderStyle, vis->Style.Alpha, vis->Translation, vis->FillColor); - - if (vis->RenderStyle == LegacyRenderStyles[STYLE_Shaded]) - { // For shaded sprites, R_SetPatchStyle sets a dc_colormap to an alpha table, but - // it is the brightest one. We need to get back to the proper light level for - // this sprite. - R_SetColorMapLight(dc_colormap, 0, vis->ColormapNum << FRACBITS); - } - - if (mode != DontDraw) - { - if (mode == DoDraw0) - { - // One column at a time - stop4 = vis->x1; - } - else // DoDraw1 - { - // Up to four columns at a time - stop4 = vis->x2 & ~3; - } - - tex = vis->pic; - spryscale = vis->yscale; - sprflipvert = false; - dc_iscale = FLOAT2FIXED(1 / vis->yscale); - frac = vis->startfrac; - xiscale = vis->xiscale; - dc_texturemid = vis->texturemid; - - if (vis->renderflags & RF_YFLIP) - { - sprflipvert = true; - spryscale = -spryscale; - dc_iscale = -dc_iscale; - dc_texturemid -= vis->pic->GetHeight(); - sprtopscreen = CenterY + dc_texturemid * spryscale; - } - else - { - sprflipvert = false; - sprtopscreen = CenterY - dc_texturemid * spryscale; - } - - dc_x = vis->x1; - x2 = vis->x2; - - if (dc_x < x2) - { - while ((dc_x < stop4) && (dc_x & 3)) - { - if (ispsprite || !R_ClipSpriteColumnWithPortals(vis)) - R_DrawMaskedColumn (tex, frac, false); - dc_x++; - frac += xiscale; - } - - while (dc_x < stop4) - { - rt_initcols(nullptr); - for (int zz = 4; zz; --zz) - { - if (ispsprite || !R_ClipSpriteColumnWithPortals(vis)) - R_DrawMaskedColumn (tex, frac, true); - dc_x++; - frac += xiscale; - } - rt_draw4cols (dc_x - 4); - } - - while (dc_x < x2) - { - if (ispsprite || !R_ClipSpriteColumnWithPortals(vis)) - R_DrawMaskedColumn (tex, frac, false); - dc_x++; - frac += xiscale; - } - } - } - - R_FinishSetPatchStyle (); - - NetUpdate (); -} - -void R_DrawWallSprite(vissprite_t *spr) -{ - int x1, x2; - double iyscale; - - x1 = MAX(spr->x1, spr->wallc.sx1); - x2 = MIN(spr->x2, spr->wallc.sx2); - if (x1 >= x2) - return; - WallT.InitFromWallCoords(&spr->wallc); - PrepWall(swall, lwall, spr->pic->GetWidth() << FRACBITS, x1, x2); - iyscale = 1 / spr->yscale; - dc_texturemid = (spr->gzt - ViewPos.Z) * iyscale; - if (spr->renderflags & RF_XFLIP) - { - int right = (spr->pic->GetWidth() << FRACBITS) - 1; - - for (int i = x1; i < x2; i++) - { - lwall[i] = right - lwall[i]; - } - } - // Prepare lighting - bool calclighting = false; - FDynamicColormap *usecolormap = basecolormap; - bool rereadcolormap = true; - - // Decals that are added to the scene must fade to black. - if (spr->RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) - { - usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); - rereadcolormap = false; - } - - int shade = LIGHT2SHADE(spr->sector->lightlevel + r_actualextralight); - GlobVis = r_WallVisibility; - rw_lightleft = float (GlobVis / spr->wallc.sz1); - rw_lightstep = float((GlobVis / spr->wallc.sz2 - rw_lightleft) / (spr->wallc.sx2 - spr->wallc.sx1)); - rw_light = rw_lightleft + (x1 - spr->wallc.sx1) * rw_lightstep; - if (fixedlightlev >= 0) - R_SetColorMapLight(usecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(fixedcolormap, 0, 0); - else if (!foggy && (spr->renderflags & RF_FULLBRIGHT)) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, 0); - else - calclighting = true; - - // Draw it - WallSpriteTile = spr->pic; - if (spr->renderflags & RF_YFLIP) - { - sprflipvert = true; - iyscale = -iyscale; - dc_texturemid -= spr->pic->GetHeight(); - } - else - { - sprflipvert = false; - } - - MaskedScaleY = (float)iyscale; - - dc_x = x1; - ESPSResult mode; - - mode = R_SetPatchStyle (spr->RenderStyle, spr->Style.Alpha, spr->Translation, spr->FillColor); - - // R_SetPatchStyle can modify basecolormap. - if (rereadcolormap) - { - usecolormap = basecolormap; - } - - if (mode == DontDraw) - { - return; - } - else - { - int stop4; - - if (mode == DoDraw0) - { // 1 column at a time - stop4 = dc_x; - } - else // DoDraw1 - { // up to 4 columns at a time - stop4 = x2 & ~3; - } - - while ((dc_x < stop4) && (dc_x & 3)) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, shade); - } - if (!R_ClipSpriteColumnWithPortals(spr)) - R_WallSpriteColumn(false); - dc_x++; - } - - while (dc_x < stop4) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, shade); - } - rt_initcols(nullptr); - for (int zz = 4; zz; --zz) - { - if (!R_ClipSpriteColumnWithPortals(spr)) - R_WallSpriteColumn(true); - dc_x++; - } - rt_draw4cols(dc_x - 4); - } - - while (dc_x < x2) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, shade); - } - if (!R_ClipSpriteColumnWithPortals(spr)) - R_WallSpriteColumn(false); - dc_x++; - } - } - R_FinishSetPatchStyle(); -} - -void R_WallSpriteColumn (bool useRt) -{ - float iscale = swall[dc_x] * MaskedScaleY; - dc_iscale = FLOAT2FIXED(iscale); - spryscale = 1 / iscale; - if (sprflipvert) - sprtopscreen = CenterY + dc_texturemid * spryscale; - else - sprtopscreen = CenterY - dc_texturemid * spryscale; - - dc_texturefrac = 0; - R_DrawMaskedColumn(WallSpriteTile, lwall[dc_x], useRt); - rw_light += rw_lightstep; -} - -void R_DrawVisVoxel(vissprite_t *spr, int minslabz, int maxslabz, short *cliptop, short *clipbot) -{ - ESPSResult mode; - int flags = 0; - - // Do setup for blending. - R_SetColorMapLight(spr->colormap, 0.0f, 0); - mode = R_SetPatchStyle(spr->RenderStyle, spr->Style.Alpha, spr->Translation, spr->FillColor); - - if (mode == DontDraw) - { - return; - } - if (colfunc == fuzzcolfunc || colfunc == R_FillColumn) - { - flags = DVF_OFFSCREEN | DVF_SPANSONLY; - } - else if (colfunc != basecolfunc) - { - flags = DVF_OFFSCREEN; - } - if (flags != 0) - { - R_CheckOffscreenBuffer(RenderTarget->GetWidth(), RenderTarget->GetHeight(), !!(flags & DVF_SPANSONLY)); - } - if (spr->bInMirror) - { - flags |= DVF_MIRRORED; - } - - // Render the voxel, either directly to the screen or offscreen. - R_DrawVoxel(spr->pa.vpos, spr->pa.vang, spr->gpos, spr->Angle, - spr->xscale, FLOAT2FIXED(spr->yscale), spr->voxel, spr->colormap, cliptop, clipbot, - minslabz, maxslabz, flags); - - // Blend the voxel, if that's what we need to do. - if ((flags & ~DVF_MIRRORED) != 0) - { - for (int x = 0; x < viewwidth; ++x) - { - if (!(flags & DVF_SPANSONLY) && (x & 3) == 0) - { - rt_initcols(OffscreenColorBuffer + x * OffscreenBufferHeight); - } - for (FCoverageBuffer::Span *span = OffscreenCoverageBuffer->Spans[x]; span != NULL; span = span->NextSpan) - { - if (flags & DVF_SPANSONLY) - { - dc_x = x; - dc_yl = span->Start; - dc_yh = span->Stop - 1; - dc_count = span->Stop - span->Start; - dc_dest = ylookup[span->Start] + x + dc_destorg; - colfunc(); - } - else - { - rt_span_coverage(x, span->Start, span->Stop - 1); - } - } - if (!(flags & DVF_SPANSONLY) && (x & 3) == 3) - { - rt_draw4cols(x - 3); - } - } - } - - R_FinishSetPatchStyle(); - NetUpdate(); -} - -// -// R_ProjectSprite -// Generates a vissprite for a thing if it might be visible. -// -void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector) -{ - double tr_x; - double tr_y; - - double gzt; // killough 3/27/98 - double gzb; // [RH] use bottom of sprite, not actor - double tx;// , tx2; - double tz; - - double xscale = 1, yscale = 1; - - int x1; - int x2; - - FTextureID picnum; - FTexture *tex; - FVoxelDef *voxel; - - vissprite_t* vis; - - fixed_t iscale; - - sector_t* heightsec; // killough 3/27/98 - - // Don't waste time projecting sprites that are definitely not visible. - if (thing == NULL || - (thing->renderflags & RF_INVISIBLE) || - !thing->RenderStyle.IsVisible(thing->Alpha) || - !thing->IsVisibleToPlayer() || - !thing->IsInsideVisibleAngles()) - { - return; - } - - // [ZZ] Or less definitely not visible (hue) - // [ZZ] 10.01.2016: don't try to clip stuff inside a skybox against the current portal. - if (!CurrentPortalInSkybox && CurrentPortal && !!P_PointOnLineSidePrecise(thing->Pos(), CurrentPortal->dst)) - return; - - // [RH] Interpolate the sprite's position to make it look smooth - DVector3 pos = thing->InterpolatedPosition(r_TicFracF); - pos.Z += thing->GetBobOffset(r_TicFracF); - - tex = NULL; - voxel = NULL; - - int spritenum = thing->sprite; - DVector2 spriteScale = thing->Scale; - int renderflags = thing->renderflags; - if (spriteScale.Y < 0) - { - spriteScale.Y = -spriteScale.Y; - renderflags ^= RF_YFLIP; - } - if (thing->player != NULL) - { - P_CheckPlayerSprite(thing, spritenum, spriteScale); - } - - if (thing->picnum.isValid()) - { - picnum = thing->picnum; - - tex = TexMan(picnum); - if (tex->UseType == FTexture::TEX_Null) - { - return; - } - - if (tex->Rotations != 0xFFFF) - { - // choose a different rotation based on player view - spriteframe_t *sprframe = &SpriteFrames[tex->Rotations]; - DAngle ang = (pos - ViewPos).Angle(); - angle_t rot; - if (sprframe->Texture[0] == sprframe->Texture[1]) - { - if (thing->flags7 & MF7_SPRITEANGLE) - rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; - else - rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; - } - else - { - if (thing->flags7 & MF7_SPRITEANGLE) - rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - else - rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - } - picnum = sprframe->Texture[rot]; - if (sprframe->Flip & (1 << rot)) - { - renderflags ^= RF_XFLIP; - } - tex = TexMan[picnum]; // Do not animate the rotation - } - } - else - { - // decide which texture to use for the sprite - if ((unsigned)spritenum >= sprites.Size ()) - { - DPrintf (DMSG_ERROR, "R_ProjectSprite: invalid sprite number %u\n", spritenum); - return; - } - spritedef_t *sprdef = &sprites[spritenum]; - if (thing->frame >= sprdef->numframes) - { - // If there are no frames at all for this sprite, don't draw it. - return; - } - else - { - //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; - // choose a different rotation based on player view - spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; - DAngle ang = (pos - ViewPos).Angle(); - angle_t rot; - if (sprframe->Texture[0] == sprframe->Texture[1]) - { - if (thing->flags7 & MF7_SPRITEANGLE) - rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; - else - rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; - } - else - { - if (thing->flags7 & MF7_SPRITEANGLE) - rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - else - rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - } - picnum = sprframe->Texture[rot]; - if (sprframe->Flip & (1 << rot)) - { - renderflags ^= RF_XFLIP; - } - tex = TexMan[picnum]; // Do not animate the rotation - if (r_drawvoxels) - { - voxel = sprframe->Voxel; - } - } - } - if (spriteScale.X < 0) - { - spriteScale.X = -spriteScale.X; - renderflags ^= RF_XFLIP; - } - if (voxel == NULL && (tex == NULL || tex->UseType == FTexture::TEX_Null)) - { - return; - } - - if ((renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) - { - R_ProjectWallSprite(thing, pos, picnum, spriteScale, renderflags); - return; - } - - // transform the origin point - tr_x = pos.X - ViewPos.X; - tr_y = pos.Y - ViewPos.Y; - - tz = tr_x * ViewTanCos + tr_y * ViewTanSin; - - // thing is behind view plane? - if (voxel == NULL && tz < MINZ) - return; - - tx = tr_x * ViewSin - tr_y * ViewCos; - - // [RH] Flip for mirrors - if (MirrorFlags & RF_XFLIP) - { - tx = -tx; - } - //tx2 = tx >> 4; - - // too far off the side? - // if it's a voxel, it can be further off the side - if ((voxel == NULL && (fabs(tx / 64) > fabs(tz))) || - (voxel != NULL && (fabs(tx / 128) > fabs(tz)))) - { - return; - } - - if (voxel == NULL) - { - // [RH] Added scaling - int scaled_to = tex->GetScaledTopOffset(); - int scaled_bo = scaled_to - tex->GetScaledHeight(); - gzt = pos.Z + spriteScale.Y * scaled_to; - gzb = pos.Z + spriteScale.Y * scaled_bo; - } - else - { - xscale = spriteScale.X * voxel->Scale; - yscale = spriteScale.Y * voxel->Scale; - double piv = voxel->Voxel->Mips[0].Pivot.Z; - gzt = pos.Z + yscale * piv - thing->Floorclip; - gzb = pos.Z + yscale * (piv - voxel->Voxel->Mips[0].SizeZ); - if (gzt <= gzb) - return; - } - - // killough 3/27/98: exclude things totally separated - // from the viewer, by either water or fake ceilings - // killough 4/11/98: improve sprite clipping for underwater/fake ceilings - - heightsec = thing->Sector->GetHeightSec(); - - if (heightsec != NULL) // only clip things which are in special sectors - { - if (fakeside == FAKED_AboveCeiling) - { - if (gzt < heightsec->ceilingplane.ZatPoint(pos)) - return; - } - else if (fakeside == FAKED_BelowFloor) - { - if (gzb >= heightsec->floorplane.ZatPoint(pos)) - return; - } - else - { - if (gzt < heightsec->floorplane.ZatPoint(pos)) - return; - if (!(heightsec->MoreFlags & SECF_FAKEFLOORONLY) && gzb >= heightsec->ceilingplane.ZatPoint(pos)) - return; - } - } - - if (voxel == NULL) - { - xscale = CenterX / tz; - - // [RH] Reject sprites that are off the top or bottom of the screen - if (globaluclip * tz > ViewPos.Z - gzb || globaldclip * tz < ViewPos.Z - gzt) - { - return; - } - - // [RH] Flip for mirrors - renderflags ^= MirrorFlags & RF_XFLIP; - - // calculate edges of the shape - const double thingxscalemul = spriteScale.X / tex->Scale.X; - - tx -= ((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; - double dtx1 = tx * xscale; - x1 = centerx + xs_RoundToInt(dtx1); - - // off the right side? - if (x1 >= WindowRight) - return; - - tx += tex->GetWidth() * thingxscalemul; - x2 = centerx + xs_RoundToInt(tx * xscale); - - // off the left side or too small? - if ((x2 < WindowLeft || x2 <= x1)) - return; - - xscale = spriteScale.X * xscale / tex->Scale.X; - iscale = (fixed_t)(FRACUNIT / xscale); // Round towards zero to avoid wrapping in edge cases - - double yscale = spriteScale.Y / tex->Scale.Y; - - // store information in a vissprite - vis = R_NewVisSprite(); - - vis->CurrentPortalUniq = CurrentPortalUniq; - vis->xscale = FLOAT2FIXED(xscale); - vis->yscale = float(InvZtoScale * yscale / tz); - vis->idepth = float(1 / tz); - vis->floorclip = thing->Floorclip / yscale; - vis->texturemid = tex->TopOffset - (ViewPos.Z - pos.Z + thing->Floorclip) / yscale; - vis->x1 = x1 < WindowLeft ? WindowLeft : x1; - vis->x2 = x2 > WindowRight ? WindowRight : x2; - vis->Angle = thing->Angles.Yaw; - - if (renderflags & RF_XFLIP) - { - vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; - vis->xiscale = -iscale; - } - else - { - vis->startfrac = 0; - vis->xiscale = iscale; - } - - vis->startfrac += (fixed_t)(vis->xiscale * (vis->x1 - centerx + 0.5 - dtx1)); - } - else - { - vis = R_NewVisSprite(); - - vis->CurrentPortalUniq = CurrentPortalUniq; - vis->xscale = FLOAT2FIXED(xscale); - vis->yscale = (float)yscale; - vis->x1 = WindowLeft; - vis->x2 = WindowRight; - vis->idepth = 1 / MINZ; - vis->floorclip = thing->Floorclip; - - pos.Z -= thing->Floorclip; - - vis->Angle = thing->Angles.Yaw + voxel->AngleOffset; - - int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin; - if (voxelspin != 0) - { - DAngle ang = double(I_FPSTime()) * voxelspin / 1000; - vis->Angle -= ang; - } - - vis->pa.vpos = { (float)ViewPos.X, (float)ViewPos.Y, (float)ViewPos.Z }; - vis->pa.vang = FAngle((float)ViewAngle.Degrees); - } - - // killough 3/27/98: save sector for special clipping later - vis->heightsec = heightsec; - vis->sector = thing->Sector; - - vis->depth = (float)tz; - vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; - vis->gzb = (float)gzb; // [RH] use gzb, not thing->z - vis->gzt = (float)gzt; // killough 3/27/98 - vis->deltax = float(pos.X - ViewPos.X); - vis->deltay = float(pos.Y - ViewPos.Y); - vis->renderflags = renderflags; - if(thing->flags5 & MF5_BRIGHT) - vis->renderflags |= RF_FULLBRIGHT; // kg3D - vis->RenderStyle = thing->RenderStyle; - vis->FillColor = thing->fillcolor; - vis->Translation = thing->Translation; // [RH] thing translation table - vis->FakeFlatStat = fakeside; - vis->Style.Alpha = float(thing->Alpha); - vis->fakefloor = fakefloor; - vis->fakeceiling = fakeceiling; - vis->ColormapNum = 0; - vis->bInMirror = MirrorFlags & RF_XFLIP; - vis->bSplitSprite = false; - - if (voxel != NULL) - { - vis->voxel = voxel->Voxel; - vis->bIsVoxel = true; - vis->bWallSprite = false; - DrewAVoxel = true; - } - else - { - vis->pic = tex; - vis->bIsVoxel = false; - vis->bWallSprite = false; - } - - // The software renderer cannot invert the source without inverting the overlay - // too. That means if the source is inverted, we need to do the reverse of what - // the invert overlay flag says to do. - INTBOOL invertcolormap = (vis->RenderStyle.Flags & STYLEF_InvertOverlay); - - if (vis->RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - FDynamicColormap *mybasecolormap = basecolormap; - if (current_sector->sectornum != thing->Sector->sectornum) // compare sectornums to account for R_FakeFlat copies. - { - // Todo: The actor is from a different sector so we have to retrieve the proper basecolormap for that sector. - } - - // Sprites that are added to the scene must fade to black. - if (vis->RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); - } - - if (vis->RenderStyle.Flags & STYLEF_FadeToBlack) - { - if (invertcolormap) - { // Fade to white - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255,255,255), mybasecolormap->Desaturate); - invertcolormap = false; - } - else - { // Fade to black - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0,0,0), mybasecolormap->Desaturate); - } - } - - // get light level - if (fixedcolormap != NULL) - { // fixed map - vis->colormap = fixedcolormap; - } - else - { - if (invertcolormap) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - vis->colormap = mybasecolormap->Maps + fixedlightlev; - } - else if (!foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT))) - { // full bright - vis->colormap = (r_fullbrightignoresectorcolor) ? FullNormalLight.Maps : mybasecolormap->Maps; - } - else - { // diminished light - vis->ColormapNum = GETPALOOKUP( - r_SpriteVisibility / MAX(tz, MINZ), spriteshade); - vis->colormap = mybasecolormap->Maps + (vis->ColormapNum << COLORMAPSHIFT); - } - } -} - -static void R_ProjectWallSprite(AActor *thing, const DVector3 &pos, FTextureID picnum, const DVector2 &scale, int renderflags) -{ - FWallCoords wallc; - double x1, x2; - DVector2 left, right; - double gzb, gzt, tz; - FTexture *pic = TexMan(picnum, true); - DAngle ang = thing->Angles.Yaw + 90; - double angcos = ang.Cos(); - double angsin = ang.Sin(); - vissprite_t *vis; - - // Determine left and right edges of sprite. The sprite's angle is its normal, - // so the edges are 90 degrees each side of it. - x2 = pic->GetScaledWidth(); - x1 = pic->GetScaledLeftOffset(); - - x1 *= scale.X; - x2 *= scale.X; - - left.X = pos.X - x1 * angcos - ViewPos.X; - left.Y = pos.Y - x1 * angsin - ViewPos.Y; - right.X = left.X + x2 * angcos; - right.Y = right.Y + x2 * angsin; - - // Is it off-screen? - if (wallc.Init(left, right, TOO_CLOSE_Z)) - return; - - if (wallc.sx1 >= WindowRight || wallc.sx2 <= WindowLeft) - return; - - // Sprite sorting should probably treat these as walls, not sprites, - // but right now, I just want to get them drawing. - tz = (pos.X - ViewPos.X) * ViewTanCos + (pos.Y - ViewPos.Y) * ViewTanSin; - - int scaled_to = pic->GetScaledTopOffset(); - int scaled_bo = scaled_to - pic->GetScaledHeight(); - gzt = pos.Z + scale.Y * scaled_to; - gzb = pos.Z + scale.Y * scaled_bo; - - vis = R_NewVisSprite(); - vis->CurrentPortalUniq = CurrentPortalUniq; - vis->x1 = wallc.sx1 < WindowLeft ? WindowLeft : wallc.sx1; - vis->x2 = wallc.sx2 >= WindowRight ? WindowRight : wallc.sx2; - vis->yscale = (float)scale.Y; - vis->idepth = float(1 / tz); - vis->depth = (float)tz; - vis->sector = thing->Sector; - vis->heightsec = NULL; - vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; - vis->gzb = (float)gzb; - vis->gzt = (float)gzt; - vis->deltax = float(pos.X - ViewPos.X); - vis->deltay = float(pos.Y - ViewPos.Y); - vis->renderflags = renderflags; - if(thing->flags5 & MF5_BRIGHT) vis->renderflags |= RF_FULLBRIGHT; // kg3D - vis->RenderStyle = thing->RenderStyle; - vis->FillColor = thing->fillcolor; - vis->Translation = thing->Translation; - vis->FakeFlatStat = 0; - vis->Style.Alpha = float(thing->Alpha); - vis->fakefloor = NULL; - vis->fakeceiling = NULL; - vis->ColormapNum = 0; - vis->bInMirror = MirrorFlags & RF_XFLIP; - vis->pic = pic; - vis->bIsVoxel = false; - vis->bWallSprite = true; - vis->ColormapNum = GETPALOOKUP( - r_SpriteVisibility / MAX(tz, MINZ), spriteshade); - vis->colormap = basecolormap->Maps + (vis->ColormapNum << COLORMAPSHIFT); - vis->wallc = wallc; -} - -// -// R_AddSprites -// During BSP traversal, this adds sprites by sector. -// -// killough 9/18/98: add lightlevel as parameter, fixing underwater lighting -// [RH] Save which side of heightsec sprite is on here. -void R_AddSprites (sector_t *sec, int lightlevel, int fakeside) -{ - F3DFloor *fakeceiling = NULL; - F3DFloor *fakefloor = NULL; - - // 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 (sec->touching_renderthings == nullptr || sec->validcount == validcount) - return; - - // Well, now it will be done. - sec->validcount = validcount; - - spriteshade = LIGHT2SHADE(lightlevel + r_actualextralight); - - // Handle all things in sector. - for(auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext) - { - auto thing = p->m_thing; - if (thing->validcount == validcount) continue; - thing->validcount = validcount; - - FIntCVar *cvar = thing->GetClass()->distancecheck; - if (cvar != NULL && *cvar >= 0) - { - double dist = (thing->Pos() - ViewPos).LengthSquared(); - double check = (double)**cvar; - if (dist >= check * check) - { - continue; - } - } - - // find fake level - for(auto rover : thing->Sector->e->XFloor.ffloors) - { - if(!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES)) continue; - if(!(rover->flags & FF_SOLID) || rover->alpha != 255) continue; - if(!fakefloor) - { - if(!rover->top.plane->isSlope()) - { - if(rover->top.plane->ZatPoint(0., 0.) <= thing->Z()) fakefloor = rover; - } - } - if(!rover->bottom.plane->isSlope()) - { - if(rover->bottom.plane->ZatPoint(0., 0.) >= thing->Top()) fakeceiling = rover; - } - } - R_ProjectSprite (thing, fakeside, fakefloor, fakeceiling, sec); - fakeceiling = NULL; - fakefloor = NULL; - } -} - -// -// R_DrawPSprite -// -void R_DrawPSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac) -{ - double tx; - int x1; - int x2; - double sx, sy; - spritedef_t* sprdef; - spriteframe_t* sprframe; - FTextureID picnum; - WORD flip; - FTexture* tex; - vissprite_t* vis; - bool noaccel; - static TArray avis; - - if (avis.Size() < vispspindex + 1) - avis.Reserve(avis.Size() - vispspindex + 1); - - // decide which patch to use - if ((unsigned)pspr->GetSprite() >= (unsigned)sprites.Size()) - { - DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite number %i\n", pspr->GetSprite()); - return; - } - sprdef = &sprites[pspr->GetSprite()]; - if (pspr->GetFrame() >= sprdef->numframes) - { - DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite frame %i : %i\n", pspr->GetSprite(), pspr->GetFrame()); - return; - } - sprframe = &SpriteFrames[sprdef->spriteframes + pspr->GetFrame()]; - - picnum = sprframe->Texture[0]; - flip = sprframe->Flip & 1; - tex = TexMan(picnum); - - if (tex->UseType == FTexture::TEX_Null) - return; - - if (pspr->firstTic) - { // Can't interpolate the first tic. - pspr->firstTic = false; - pspr->oldx = pspr->x; - pspr->oldy = pspr->y; - } - - sx = pspr->oldx + (pspr->x - pspr->oldx) * ticfrac; - sy = pspr->oldy + (pspr->y - pspr->oldy) * ticfrac + WEAPON_FUDGE_Y; - - if (pspr->Flags & PSPF_ADDBOB) - { - sx += bobx; - sy += boby; - } - - if (pspr->Flags & PSPF_ADDWEAPON && pspr->GetID() != PSP_WEAPON) - { - sx += wx; - sy += wy; - } - - // calculate edges of the shape - tx = sx - BASEXCENTER; - - tx -= tex->GetScaledLeftOffset(); - x1 = xs_RoundToInt(CenterX + tx * pspritexscale); - - // off the right side - if (x1 > viewwidth) - return; - - tx += tex->GetScaledWidth(); - x2 = xs_RoundToInt(CenterX + tx * pspritexscale); - - // off the left side - if (x2 <= 0) - return; - - // store information in a vissprite - vis = &avis[vispspindex]; - memset(vis, 0, sizeof(*vis)); - vis->renderflags = owner->renderflags; - vis->floorclip = 0; - vis->sector = nullptr; - - vis->texturemid = (BASEYCENTER - sy) * tex->Scale.Y + tex->TopOffset; - - if (camera->player && (RenderTarget != screen || - viewheight == RenderTarget->GetHeight() || - (RenderTarget->GetWidth() > (BASEXCENTER * 2) && !st_scale))) - { // Adjust PSprite for fullscreen views - AWeapon *weapon = dyn_cast(pspr->GetCaller()); - if (weapon != nullptr && weapon->YAdjust != 0) - { - if (RenderTarget != screen || viewheight == RenderTarget->GetHeight()) - { - vis->texturemid -= weapon->YAdjust; - } - else - { - vis->texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust; - } - } - } - if (pspr->GetID() < PSP_TARGETCENTER) - { // Move the weapon down for 1280x1024. - vis->texturemid -= AspectPspriteOffset(WidescreenRatio); - } - vis->x1 = x1 < 0 ? 0 : x1; - vis->x2 = x2 >= viewwidth ? viewwidth : x2; - vis->xscale = FLOAT2FIXED(pspritexscale / tex->Scale.X); - vis->yscale = float(pspriteyscale / tex->Scale.Y); - vis->Translation = 0; // [RH] Use default colors - vis->pic = tex; - vis->ColormapNum = 0; - - if (!(flip) != !(pspr->Flags & PSPF_FLIP)) - { - vis->xiscale = -FLOAT2FIXED(pspritexiscale * tex->Scale.X); - vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; - } - else - { - vis->xiscale = FLOAT2FIXED(pspritexiscale * tex->Scale.X); - vis->startfrac = 0; - } - - if (vis->x1 > x1) - vis->startfrac += vis->xiscale*(vis->x1 - x1); - - noaccel = false; - FDynamicColormap *colormap_to_use = nullptr; - if (pspr->GetID() < PSP_TARGETCENTER) - { - vis->Style.Alpha = float(owner->Alpha); - vis->RenderStyle = owner->RenderStyle; - - // The software renderer cannot invert the source without inverting the overlay - // too. That means if the source is inverted, we need to do the reverse of what - // the invert overlay flag says to do. - INTBOOL invertcolormap = (vis->RenderStyle.Flags & STYLEF_InvertOverlay); - - if (vis->RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - FDynamicColormap *mybasecolormap = basecolormap; - - if (vis->RenderStyle.Flags & STYLEF_FadeToBlack) - { - if (invertcolormap) - { // Fade to white - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255, 255, 255), mybasecolormap->Desaturate); - invertcolormap = false; - } - else - { // Fade to black - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0, 0, 0), mybasecolormap->Desaturate); - } - } - - if (realfixedcolormap != nullptr) - { // fixed color - vis->colormap = realfixedcolormap->Colormap; - } - else - { - if (invertcolormap) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - vis->colormap = (r_fullbrightignoresectorcolor) ? (FullNormalLight.Maps + fixedlightlev) : (mybasecolormap->Maps + fixedlightlev); - } - else if (!foggy && pspr->GetState()->GetFullbright()) - { // full bright - vis->colormap = (r_fullbrightignoresectorcolor) ? FullNormalLight.Maps : mybasecolormap->Maps; // [RH] use basecolormap - } - else - { // local light - vis->colormap = mybasecolormap->Maps + (GETPALOOKUP(0, spriteshade) << COLORMAPSHIFT); - } - } - if (camera->Inventory != nullptr) - { - vis->Style.RenderStyle = STYLE_Count; - vis->Style.Invert = false; - camera->AlterWeaponSprite(&vis->Style); - if (vis->Style.Invert) - { - vis->colormap = SpecialColormaps[0].Colormap; - - // Has the basecolormap changed? If so, we can't hardware accelerate it, - // since we don't know what it is anymore. - if (vis->colormap < mybasecolormap->Maps || - vis->colormap >= mybasecolormap->Maps + NUMCOLORMAPS * 256) - { - noaccel = true; - } - } - if (vis->Style.RenderStyle != STYLE_Count) vis->RenderStyle = vis->Style.RenderStyle; - } - // If we're drawing with a special colormap, but shaders for them are disabled, do - // not accelerate. - if (!r_shadercolormaps && (vis->colormap >= SpecialColormaps[0].Colormap && - vis->colormap <= SpecialColormaps.Last().Colormap)) - { - noaccel = true; - } - // If drawing with a BOOM colormap, disable acceleration. - if (mybasecolormap == &NormalLight && NormalLight.Maps != realcolormaps) - { - noaccel = true; - } - // If the main colormap has fixed lights, and this sprite is being drawn with that - // colormap, disable acceleration so that the lights can remain fixed. - if (!noaccel && realfixedcolormap == nullptr && - NormalLightHasFixedLights && mybasecolormap == &NormalLight && - vis->pic->UseBasePalette()) - { - noaccel = true; - } - // [SP] If emulating GZDoom fullbright, disable acceleration - if (r_fullbrightignoresectorcolor && fixedlightlev >= 0) - mybasecolormap = &FullNormalLight; - if (r_fullbrightignoresectorcolor && !foggy && pspr->GetState()->GetFullbright()) - mybasecolormap = &FullNormalLight; - colormap_to_use = mybasecolormap; - } - else - { - colormap_to_use = basecolormap; - vis->colormap = basecolormap->Maps; - vis->RenderStyle = STYLE_Normal; - } - - // Check for hardware-assisted 2D. If it's available, and this sprite is not - // fuzzy, don't draw it until after the switch to 2D mode. - if (!noaccel && RenderTarget == screen && (DFrameBuffer *)screen->Accel2D) - { - FRenderStyle style = vis->RenderStyle; - style.CheckFuzz(); - if (style.BlendOp != STYLEOP_Fuzz) - { - if (vispsprites.Size() < vispspindex + 1) - vispsprites.Reserve(vispsprites.Size() - vispspindex + 1); - - vispsprites[vispspindex].vis = vis; - vispsprites[vispspindex].basecolormap = colormap_to_use; - vispsprites[vispspindex].x1 = x1; - vispspindex++; - return; - } - } - R_DrawVisSprite(vis); -} - -//========================================================================== -// -// R_DrawPlayerSprites -// -//========================================================================== - -void R_DrawPlayerSprites () -{ - int i; - int lightnum; - DPSprite* psp; - DPSprite* weapon; - sector_t* sec = NULL; - static sector_t tempsec; - int floorlight, ceilinglight; - F3DFloor *rover; - - if (!r_drawplayersprites || - !camera || - !camera->player || - (players[consoleplayer].cheats & CF_CHASECAM) || - (r_deathcamera && camera->health <= 0)) - return; - - if (fixedlightlev < 0 && viewsector->e && viewsector->e->XFloor.lightlist.Size()) - { - for (i = viewsector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) - { - if (ViewPos.Z <= viewsector->e->XFloor.lightlist[i].plane.Zat0()) - { - rover = viewsector->e->XFloor.lightlist[i].caster; - if (rover) - { - if (rover->flags & FF_DOUBLESHADOW && ViewPos.Z <= rover->bottom.plane->Zat0()) - break; - sec = rover->model; - if (rover->flags & FF_FADEWALLS) - basecolormap = sec->ColorMap; - else - basecolormap = viewsector->e->XFloor.lightlist[i].extra_colormap; - } - break; - } - } - if(!sec) - { - sec = viewsector; - basecolormap = sec->ColorMap; - } - floorlight = ceilinglight = sec->lightlevel; - } - else - { // This used to use camera->Sector but due to interpolation that can be incorrect - // when the interpolated viewpoint is in a different sector than the camera. - sec = R_FakeFlat (viewsector, &tempsec, &floorlight, - &ceilinglight, false); - - // [RH] set basecolormap - basecolormap = sec->ColorMap; - } - - // [RH] set foggy flag - foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); - r_actualextralight = foggy ? 0 : extralight << 4; - - // get light level - lightnum = ((floorlight + ceilinglight) >> 1) + r_actualextralight; - spriteshade = LIGHT2SHADE(lightnum) - 24*FRACUNIT; - - // clip to screen bounds - mfloorclip = screenheightarray; - mceilingclip = zeroarray; - - if (camera->player != NULL) - { - double centerhack = CenterY; - double wx, wy; - float bobx, boby; - - CenterY = viewheight / 2; - - P_BobWeapon (camera->player, &bobx, &boby, r_TicFracF); - - // Interpolate the main weapon layer once so as to be able to add it to other layers. - if ((weapon = camera->player->FindPSprite(PSP_WEAPON)) != nullptr) - { - if (weapon->firstTic) - { - wx = weapon->x; - wy = weapon->y; - } - else - { - wx = weapon->oldx + (weapon->x - weapon->oldx) * r_TicFracF; - wy = weapon->oldy + (weapon->y - weapon->oldy) * r_TicFracF; - } - } - else - { - wx = 0; - wy = 0; - } - - // add all active psprites - psp = camera->player->psprites; - while (psp) - { - // [RH] Don't draw the targeter's crosshair if the player already has a crosshair set. - // It's possible this psprite's caller is now null but the layer itself hasn't been destroyed - // because it didn't tick yet (if we typed 'take all' while in the console for example). - // In this case let's simply not draw it to avoid crashing. - if ((psp->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && psp->GetCaller() != nullptr) - { - R_DrawPSprite(psp, camera, bobx, boby, wx, wy, r_TicFracF); - } - - psp = psp->GetNext(); - } - - CenterY = centerhack; - } -} - -//========================================================================== -// -// R_DrawRemainingPlayerSprites -// -// Called from D_Display to draw sprites that were not drawn by -// R_DrawPlayerSprites(). -// -//========================================================================== - -void R_DrawRemainingPlayerSprites() -{ - for (unsigned int i = 0; i < vispspindex; i++) - { - vissprite_t *vis; - - vis = vispsprites[i].vis; - FDynamicColormap *colormap = vispsprites[i].basecolormap; - bool flip = vis->xiscale < 0; - FSpecialColormap *special = NULL; - PalEntry overlay = 0; - FColormapStyle colormapstyle; - bool usecolormapstyle = false; - - if (vis->colormap >= SpecialColormaps[0].Colormap && - vis->colormap < SpecialColormaps[SpecialColormaps.Size()].Colormap) - { - // Yuck! There needs to be a better way to store colormaps in the vissprite... :( - ptrdiff_t specialmap = (vis->colormap - SpecialColormaps[0].Colormap) / sizeof(FSpecialColormap); - special = &SpecialColormaps[specialmap]; - } - else if (colormap->Color == PalEntry(255,255,255) && - colormap->Desaturate == 0) - { - overlay = colormap->Fade; - overlay.a = BYTE(((vis->colormap - colormap->Maps) >> 8) * 255 / NUMCOLORMAPS); - } - else - { - usecolormapstyle = true; - colormapstyle.Color = colormap->Color; - colormapstyle.Fade = colormap->Fade; - colormapstyle.Desaturate = colormap->Desaturate; - colormapstyle.FadeLevel = ((vis->colormap - colormap->Maps) >> 8) / float(NUMCOLORMAPS); - } - screen->DrawTexture(vis->pic, - viewwindowx + vispsprites[i].x1, - viewwindowy + viewheight/2 - vis->texturemid * vis->yscale - 0.5, - DTA_DestWidthF, FIXED2DBL(vis->pic->GetWidth() * vis->xscale), - DTA_DestHeightF, vis->pic->GetHeight() * vis->yscale, - DTA_TranslationIndex, vis->Translation, - DTA_FlipX, flip, - DTA_TopOffset, 0, - DTA_LeftOffset, 0, - DTA_ClipLeft, viewwindowx, - DTA_ClipTop, viewwindowy, - DTA_ClipRight, viewwindowx + viewwidth, - DTA_ClipBottom, viewwindowy + viewheight, - DTA_Alpha, vis->Style.Alpha, - DTA_RenderStyle, vis->RenderStyle, - DTA_FillColor, vis->FillColor, - DTA_SpecialColormap, special, - DTA_ColorOverlay, overlay.d, - DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : NULL, - TAG_DONE); - } - - vispspindex = 0; -} - -// -// R_SortVisSprites -// -// [RH] The old code for this function used a bubble sort, which was far less -// than optimal with large numbers of sprites. I changed it to use the -// stdlib qsort() function instead, and now it is a *lot* faster; the -// more vissprites that need to be sorted, the better the performance -// gain compared to the old function. -// -// Sort vissprites by depth, far to near - -// This is the standard version, which does a simple test based on depth. -static bool sv_compare(vissprite_t *a, vissprite_t *b) -{ - return a->idepth > b->idepth; -} - -// This is an alternate version, for when one or more voxel is in view. -// It does a 2D distance test based on whichever one is furthest from -// the viewpoint. -static bool sv_compare2d(vissprite_t *a, vissprite_t *b) -{ - return DVector2(a->deltax, a->deltay).LengthSquared() < - DVector2(b->deltax, b->deltay).LengthSquared(); -} - -#if 0 -static drawseg_t **drawsegsorter; -static int drawsegsortersize = 0; - -// Sort vissprites by leftmost column, left to right -static int sv_comparex (const void *arg1, const void *arg2) -{ - return (*(vissprite_t **)arg2)->x1 - (*(vissprite_t **)arg1)->x1; -} - -// Sort drawsegs by rightmost column, left to right -static int sd_comparex (const void *arg1, const void *arg2) -{ - return (*(drawseg_t **)arg2)->x2 - (*(drawseg_t **)arg1)->x2; -} - -// Split up vissprites that intersect drawsegs -void R_SplitVisSprites () -{ - size_t start, stop; - size_t numdrawsegs = ds_p - firstdrawseg; - size_t numsprites; - size_t spr, dseg, dseg2; - - if (!r_splitsprites) - return; - - if (numdrawsegs == 0 || vissprite_p - firstvissprite == 0) - return; - - // Sort drawsegs from left to right - if (numdrawsegs > drawsegsortersize) - { - if (drawsegsorter != NULL) - delete[] drawsegsorter; - drawsegsortersize = numdrawsegs * 2; - drawsegsorter = new drawseg_t *[drawsegsortersize]; - } - for (dseg = dseg2 = 0; dseg < numdrawsegs; ++dseg) - { - // Drawsegs that don't clip any sprites don't need to be considered. - if (firstdrawseg[dseg].silhouette) - { - drawsegsorter[dseg2++] = &firstdrawseg[dseg]; - } - } - numdrawsegs = dseg2; - if (numdrawsegs == 0) - { - return; - } - qsort (drawsegsorter, numdrawsegs, sizeof(drawseg_t *), sd_comparex); - - // Now sort vissprites from left to right, and walk them simultaneously - // with the drawsegs, splitting any that intersect. - start = firstvissprite - vissprites; - - int p = 0; - do - { - p++; - R_SortVisSprites (sv_comparex, start); - stop = vissprite_p - vissprites; - numsprites = stop - start; - - spr = dseg = 0; - do - { - vissprite_t *vis = spritesorter[spr], *vis2; - - // Skip drawsegs until we get to one that doesn't end before the sprite - // begins. - while (dseg < numdrawsegs && drawsegsorter[dseg]->x2 <= vis->x1) - { - dseg++; - } - // Now split the sprite against any drawsegs it intersects - for (dseg2 = dseg; dseg2 < numdrawsegs; dseg2++) - { - drawseg_t *ds = drawsegsorter[dseg2]; - - if (ds->x1 > vis->x2 || ds->x2 < vis->x1) - continue; - - if ((vis->idepth < ds->siz1) != (vis->idepth < ds->siz2)) - { // The drawseg is crossed; find the x where the intersection occurs - int cross = Scale (vis->idepth - ds->siz1, ds->sx2 - ds->sx1, ds->siz2 - ds->siz1) + ds->sx1 + 1; - -/* if (cross < ds->x1 || cross > ds->x2) - { // The original seg is crossed, but the drawseg is not - continue; - } -*/ if (cross <= vis->x1 || cross >= vis->x2) - { // Don't create 0-sized sprites - continue; - } - - vis->bSplitSprite = true; - - // Create a new vissprite for the right part of the sprite - vis2 = R_NewVisSprite (); - *vis2 = *vis; - vis2->startfrac += vis2->xiscale * (cross - vis2->x1); - vis->x2 = cross-1; - vis2->x1 = cross; - //vis2->alpha /= 2; - //vis2->RenderStyle = STYLE_Add; - - if (vis->idepth < ds->siz1) - { // Left is in back, right is in front - vis->sector = ds->curline->backsector; - vis2->sector = ds->curline->frontsector; - } - else - { // Right is in front, left is in back - vis->sector = ds->curline->frontsector; - vis2->sector = ds->curline->backsector; - } - } - } - } - while (dseg < numdrawsegs && ++spr < numsprites); - - // Repeat for any new sprites that were added. - } - while (start = stop, stop != vissprite_p - vissprites); -} -#endif - -#ifdef __GNUC__ -static void swap(vissprite_t *&a, vissprite_t *&b) -{ - vissprite_t *t = a; - a = b; - b = t; -} -#endif - -void R_SortVisSprites (bool (*compare)(vissprite_t *, vissprite_t *), size_t first) -{ - int i; - vissprite_t **spr; - - vsprcount = int(vissprite_p - &vissprites[first]); - - if (vsprcount == 0) - return; - - if (spritesortersize < MaxVisSprites) - { - if (spritesorter != NULL) - delete[] spritesorter; - spritesorter = new vissprite_t *[MaxVisSprites]; - spritesortersize = MaxVisSprites; - } - - if (!(i_compatflags & COMPATF_SPRITESORT)) - { - for (i = 0, spr = firstvissprite; i < vsprcount; i++, spr++) - { - spritesorter[i] = *spr; - } - } - else - { - // If the compatibility option is on sprites of equal distance need to - // be sorted in inverse order. This is most easily achieved by - // filling the sort array backwards before the sort. - for (i = 0, spr = firstvissprite + vsprcount-1; i < vsprcount; i++, spr--) - { - spritesorter[i] = *spr; - } - } - - std::stable_sort(&spritesorter[0], &spritesorter[vsprcount], compare); -} - -// -// R_DrawSprite -// -void R_DrawSprite (vissprite_t *spr) -{ - static short clipbot[MAXWIDTH]; - static short cliptop[MAXWIDTH]; - drawseg_t *ds; - int i; - int x1, x2; - int r1, r2; - short topclip, botclip; - short *clip1, *clip2; - lighttable_t *colormap = spr->colormap; - F3DFloor *rover; - FDynamicColormap *mybasecolormap; - - // [RH] Check for particles - if (!spr->bIsVoxel && spr->pic == NULL) - { - // kg3D - reject invisible parts - if ((fake3D & FAKE3D_CLIPBOTTOM) && spr->gpos.Z <= sclipBottom) return; - if ((fake3D & FAKE3D_CLIPTOP) && spr->gpos.Z >= sclipTop) return; - R_DrawParticle (spr); - return; - } - - x1 = spr->x1; - x2 = spr->x2; - - // [RH] Quickly reject sprites with bad x ranges. - if (x1 >= x2) - return; - - // [RH] Sprites split behind a one-sided line can also be discarded. - if (spr->sector == NULL) - return; - - // kg3D - reject invisible parts - if ((fake3D & FAKE3D_CLIPBOTTOM) && spr->gzt <= sclipBottom) return; - if ((fake3D & FAKE3D_CLIPTOP) && spr->gzb >= sclipTop) return; - - // kg3D - correct colors now - if (!fixedcolormap && fixedlightlev < 0 && spr->sector->e && spr->sector->e->XFloor.lightlist.Size()) - { - if (!(fake3D & FAKE3D_CLIPTOP)) - { - sclipTop = spr->sector->ceilingplane.ZatPoint(ViewPos); - } - sector_t *sec = NULL; - for (i = spr->sector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) - { - if (sclipTop <= spr->sector->e->XFloor.lightlist[i].plane.Zat0()) - { - rover = spr->sector->e->XFloor.lightlist[i].caster; - if (rover) - { - if (rover->flags & FF_DOUBLESHADOW && sclipTop <= rover->bottom.plane->Zat0()) - { - break; - } - sec = rover->model; - if (rover->flags & FF_FADEWALLS) - { - mybasecolormap = sec->ColorMap; - } - else - { - mybasecolormap = spr->sector->e->XFloor.lightlist[i].extra_colormap; - } - } - break; - } - } - // found new values, recalculate - if (sec) - { - INTBOOL invertcolormap = (spr->RenderStyle.Flags & STYLEF_InvertOverlay); - - if (spr->RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - // Sprites that are added to the scene must fade to black. - if (spr->RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); - } - - if (spr->RenderStyle.Flags & STYLEF_FadeToBlack) - { - if (invertcolormap) - { // Fade to white - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255,255,255), mybasecolormap->Desaturate); - invertcolormap = false; - } - else - { // Fade to black - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0,0,0), mybasecolormap->Desaturate); - } - } - - // get light level - if (invertcolormap) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - spr->colormap = mybasecolormap->Maps + fixedlightlev; - } - else if (!foggy && (spr->renderflags & RF_FULLBRIGHT)) - { // full bright - spr->colormap = (r_fullbrightignoresectorcolor) ? FullNormalLight.Maps : mybasecolormap->Maps; - } - else - { // diminished light - spriteshade = LIGHT2SHADE(sec->lightlevel + r_actualextralight); - spr->colormap = mybasecolormap->Maps + (GETPALOOKUP( - r_SpriteVisibility / MAX(MINZ, (double)spr->depth), spriteshade) << COLORMAPSHIFT); - } - } - } - - // [RH] Initialize the clipping arrays to their largest possible range - // instead of using a special "not clipped" value. This eliminates - // visual anomalies when looking down and should be faster, too. - topclip = 0; - botclip = viewheight; - - // killough 3/27/98: - // Clip the sprite against deep water and/or fake ceilings. - // [RH] rewrote this to be based on which part of the sector is really visible - - double scale = InvZtoScale * spr->idepth; - double hzb = -DBL_MAX, hzt = DBL_MAX; - - if (spr->bIsVoxel && spr->floorclip != 0) - { - hzb = spr->gzb; - } - - if (spr->heightsec && !(spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) - { // only things in specially marked sectors - if (spr->FakeFlatStat != FAKED_AboveCeiling) - { - double hz = spr->heightsec->floorplane.ZatPoint(spr->gpos); - int h = xs_RoundToInt(CenterY - (hz - ViewPos.Z) * scale); - - if (spr->FakeFlatStat == FAKED_BelowFloor) - { // seen below floor: clip top - if (!spr->bIsVoxel && h > topclip) - { - topclip = short(MIN(h, viewheight)); - } - hzt = MIN(hzt, hz); - } - else - { // seen in the middle: clip bottom - if (!spr->bIsVoxel && h < botclip) - { - botclip = MAX (0, h); - } - hzb = MAX(hzb, hz); - } - } - if (spr->FakeFlatStat != FAKED_BelowFloor && !(spr->heightsec->MoreFlags & SECF_FAKEFLOORONLY)) - { - double hz = spr->heightsec->ceilingplane.ZatPoint(spr->gpos); - int h = xs_RoundToInt(CenterY - (hz - ViewPos.Z) * scale); - - if (spr->FakeFlatStat == FAKED_AboveCeiling) - { // seen above ceiling: clip bottom - if (!spr->bIsVoxel && h < botclip) - { - botclip = MAX (0, h); - } - hzb = MAX(hzb, hz); - } - else - { // seen in the middle: clip top - if (!spr->bIsVoxel && h > topclip) - { - topclip = MIN(h, viewheight); - } - hzt = MIN(hzt, hz); - } - } - } - // killough 3/27/98: end special clipping for deep water / fake ceilings - else if (!spr->bIsVoxel && spr->floorclip) - { // [RH] Move floorclip stuff from R_DrawVisSprite to here - //int clip = ((FLOAT2FIXED(CenterY) - FixedMul (spr->texturemid - (spr->pic->GetHeight() << FRACBITS) + spr->floorclip, spr->yscale)) >> FRACBITS); - int clip = xs_RoundToInt(CenterY - (spr->texturemid - spr->pic->GetHeight() + spr->floorclip) * spr->yscale); - if (clip < botclip) - { - botclip = MAX(0, clip); - } - } - - if (fake3D & FAKE3D_CLIPBOTTOM) - { - if (!spr->bIsVoxel) - { - double hz = sclipBottom; - if (spr->fakefloor) - { - double floorz = spr->fakefloor->top.plane->Zat0(); - if (ViewPos.Z > floorz && floorz == sclipBottom ) - { - hz = spr->fakefloor->bottom.plane->Zat0(); - } - } - int h = xs_RoundToInt(CenterY - (hz - ViewPos.Z) * scale); - if (h < botclip) - { - botclip = MAX(0, h); - } - } - hzb = MAX(hzb, sclipBottom); - } - if (fake3D & FAKE3D_CLIPTOP) - { - if (!spr->bIsVoxel) - { - double hz = sclipTop; - if (spr->fakeceiling != NULL) - { - double ceilingZ = spr->fakeceiling->bottom.plane->Zat0(); - if (ViewPos.Z < ceilingZ && ceilingZ == sclipTop) - { - hz = spr->fakeceiling->top.plane->Zat0(); - } - } - int h = xs_RoundToInt(CenterY - (hz - ViewPos.Z) * scale); - if (h > topclip) - { - topclip = short(MIN(h, viewheight)); - } - } - hzt = MIN(hzt, sclipTop); - } - -#if 0 - // [RH] Sprites that were split by a drawseg should also be clipped - // by the sector's floor and ceiling. (Not sure how/if to handle this - // with fake floors, since those already do clipping.) - if (spr->bSplitSprite && - (spr->heightsec == NULL || (spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC))) - { - fixed_t h = spr->sector->floorplane.ZatPoint (spr->gx, spr->gy); - h = (centeryfrac - FixedMul (h-viewz, scale)) >> FRACBITS; - if (h < botclip) - { - botclip = MAX (0, h); - } - h = spr->sector->ceilingplane.ZatPoint (spr->gx, spr->gy); - h = (centeryfrac - FixedMul (h-viewz, scale)) >> FRACBITS; - if (h > topclip) - { - topclip = short(MIN(h, viewheight)); - } - } -#endif - - if (topclip >= botclip) - { - spr->colormap = colormap; - return; - } - - i = x2 - x1; - clip1 = clipbot + x1; - clip2 = cliptop + x1; - do - { - *clip1++ = botclip; - *clip2++ = topclip; - } while (--i); - - // Scan drawsegs from end to start for obscuring segs. - // The first drawseg that is closer than the sprite is the clip seg. - - // Modified by Lee Killough: - // (pointer check was originally nonportable - // and buggy, by going past LEFT end of array): - - // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) old buggy code - - for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough - { - // [ZZ] portal handling here - //if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) - // continue; - // [ZZ] WARNING: uncommenting the two above lines, totally breaks sprite clipping - - // kg3D - no clipping on fake segs - if (ds->fake) continue; - // determine if the drawseg obscures the sprite - if (ds->x1 >= x2 || ds->x2 <= x1 || - (!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == -1 && - !ds->bFogBoundary) ) - { - // does not cover sprite - continue; - } - - r1 = MAX (ds->x1, x1); - r2 = MIN (ds->x2, x2); - - float neardepth, fardepth; - if (!spr->bWallSprite) - { - if (ds->sz1 < ds->sz2) - { - neardepth = ds->sz1, fardepth = ds->sz2; - } - else - { - neardepth = ds->sz2, fardepth = ds->sz1; - } - } - // Check if sprite is in front of draw seg: - if ((!spr->bWallSprite && neardepth > spr->depth) || ((spr->bWallSprite || fardepth > spr->depth) && - (spr->gpos.Y - ds->curline->v1->fY()) * (ds->curline->v2->fX() - ds->curline->v1->fX()) - - (spr->gpos.X - ds->curline->v1->fX()) * (ds->curline->v2->fY() - ds->curline->v1->fY()) <= 0)) - { - // seg is behind sprite, so draw the mid texture if it has one - if (ds->CurrentPortalUniq == CurrentPortalUniq && // [ZZ] instead, portal uniq check is made here - (ds->maskedtexturecol != -1 || ds->bFogBoundary)) - R_RenderMaskedSegRange (ds, r1, r2); - continue; - } - - // clip this piece of the sprite - // killough 3/27/98: optimized and made much shorter - // [RH] Optimized further (at least for VC++; - // other compilers should be at least as good as before) - - if (ds->silhouette & SIL_BOTTOM) //bottom sil - { - clip1 = clipbot + r1; - clip2 = openings + ds->sprbottomclip + r1 - ds->x1; - i = r2 - r1; - do - { - if (*clip1 > *clip2) - *clip1 = *clip2; - clip1++; - clip2++; - } while (--i); - } - - if (ds->silhouette & SIL_TOP) // top sil - { - clip1 = cliptop + r1; - clip2 = openings + ds->sprtopclip + r1 - ds->x1; - i = r2 - r1; - do - { - if (*clip1 < *clip2) - *clip1 = *clip2; - clip1++; - clip2++; - } while (--i); - } - } - - // all clipping has been performed, so draw the sprite - - if (!spr->bIsVoxel) - { - mfloorclip = clipbot; - mceilingclip = cliptop; - if (!spr->bWallSprite) - { - R_DrawVisSprite(spr); - } - else - { - R_DrawWallSprite(spr); - } - } - else - { - // If it is completely clipped away, don't bother drawing it. - if (cliptop[x2] >= clipbot[x2]) - { - for (i = x1; i < x2; ++i) - { - if (cliptop[i] < clipbot[i]) - { - break; - } - } - if (i == x2) - { - spr->colormap = colormap; - return; - } - } - // Add everything outside the left and right edges to the clipping array - // for R_DrawVisVoxel(). - if (x1 > 0) - { - fillshort(cliptop, x1, viewheight); - } - if (x2 < viewwidth - 1) - { - fillshort(cliptop + x2, viewwidth - x2, viewheight); - } - int minvoxely = spr->gzt <= hzt ? 0 : xs_RoundToInt((spr->gzt - hzt) / spr->yscale); - int maxvoxely = spr->gzb > hzb ? INT_MAX : xs_RoundToInt((spr->gzt - hzb) / spr->yscale); - R_DrawVisVoxel(spr, minvoxely, maxvoxely, cliptop, clipbot); - } - spr->colormap = colormap; -} - -// kg3D: -// R_DrawMasked contains sorting -// original renamed to R_DrawMaskedSingle - -void R_DrawMaskedSingle (bool renew) -{ - drawseg_t *ds; - int i; - -#if 0 - R_SplitVisSprites (); -#endif - - for (i = vsprcount; i > 0; i--) - { - if (spritesorter[i-1]->CurrentPortalUniq != CurrentPortalUniq) - continue; // probably another time - R_DrawSprite (spritesorter[i-1]); - } - - // render any remaining masked mid textures - - // Modified by Lee Killough: - // (pointer check was originally nonportable - // and buggy, by going past LEFT end of array): - - // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) old buggy code - - if (renew) - { - fake3D |= FAKE3D_REFRESHCLIP; - } - for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough - { - // [ZZ] the same as above - if (ds->CurrentPortalUniq != CurrentPortalUniq) - continue; - // kg3D - no fake segs - if (ds->fake) continue; - if (ds->maskedtexturecol != -1 || ds->bFogBoundary) - { - R_RenderMaskedSegRange (ds, ds->x1, ds->x2); - } - } -} - -void R_DrawHeightPlanes(double height); // kg3D - fake planes - -void R_DrawMasked (void) -{ - R_CollectPortals(); - R_SortVisSprites (DrewAVoxel ? sv_compare2d : sv_compare, firstvissprite - vissprites); - - if (height_top == NULL) - { // kg3D - no visible 3D floors, normal rendering - R_DrawMaskedSingle(false); - } - else - { // kg3D - correct sorting - HeightLevel *hl; - - // ceilings - for (hl = height_cur; hl != NULL && hl->height >= ViewPos.Z; hl = hl->prev) - { - if (hl->next) - { - fake3D = FAKE3D_CLIPBOTTOM | FAKE3D_CLIPTOP; - sclipTop = hl->next->height; - } - else - { - fake3D = FAKE3D_CLIPBOTTOM; - } - sclipBottom = hl->height; - R_DrawMaskedSingle(true); - R_DrawHeightPlanes(hl->height); - } - - // floors - fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPTOP; - sclipTop = height_top->height; - R_DrawMaskedSingle(true); - hl = height_top; - for (hl = height_top; hl != NULL && hl->height < ViewPos.Z; hl = hl->next) - { - R_DrawHeightPlanes(hl->height); - if (hl->next) - { - fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPTOP | FAKE3D_CLIPBOTTOM; - sclipTop = hl->next->height; - } - else - { - fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPBOTTOM; - } - sclipBottom = hl->height; - R_DrawMaskedSingle(true); - } - R_3D_DeleteHeights(); - fake3D = 0; - } - R_DrawPlayerSprites (); -} - - -void R_ProjectParticle (particle_t *particle, const sector_t *sector, int shade, int fakeside) -{ - double tr_x, tr_y; - double tx, ty; - double tz, tiz; - double xscale, yscale; - int x1, x2, y1, y2; - vissprite_t* vis; - sector_t* heightsec = NULL; - BYTE* map; - - // [ZZ] Particle not visible through the portal plane - if (CurrentPortal && !!P_PointOnLineSide(particle->Pos, CurrentPortal->dst)) - return; - - // transform the origin point - tr_x = particle->Pos.X - ViewPos.X; - tr_y = particle->Pos.Y - ViewPos.Y; - - tz = tr_x * ViewTanCos + tr_y * ViewTanSin; - - // particle is behind view plane? - if (tz < MINZ) - return; - - tx = tr_x * ViewSin - tr_y * ViewCos; - - // Flip for mirrors - if (MirrorFlags & RF_XFLIP) - { - tx = viewwidth - tx - 1; - } - - // too far off the side? - if (tz <= fabs(tx)) - return; - - tiz = 1 / tz; - xscale = centerx * tiz; - - // calculate edges of the shape - double psize = particle->size / 8.0; - - x1 = MAX(WindowLeft, centerx + xs_RoundToInt((tx - psize) * xscale)); - x2 = MIN(WindowRight, centerx + xs_RoundToInt((tx + psize) * xscale)); - - if (x1 >= x2) - return; - - yscale = YaspectMul * xscale; - ty = particle->Pos.Z - ViewPos.Z; - y1 = xs_RoundToInt(CenterY - (ty + psize) * yscale); - y2 = xs_RoundToInt(CenterY - (ty - psize) * yscale); - - // Clip the particle now. Because it's a point and projected as its subsector is - // entered, we don't need to clip it to drawsegs like a normal sprite. - - // Clip particles behind walls. - if (y1 < ceilingclip[x1]) y1 = ceilingclip[x1]; - if (y1 < ceilingclip[x2-1]) y1 = ceilingclip[x2-1]; - if (y2 >= floorclip[x1]) y2 = floorclip[x1] - 1; - if (y2 >= floorclip[x2-1]) y2 = floorclip[x2-1] - 1; - - if (y1 > y2) - return; - - // Clip particles above the ceiling or below the floor. - heightsec = sector->GetHeightSec(); - - const secplane_t *topplane; - const secplane_t *botplane; - FTextureID toppic; - FTextureID botpic; - - if (heightsec) // only clip things which are in special sectors - { - if (fakeside == FAKED_AboveCeiling) - { - topplane = §or->ceilingplane; - botplane = &heightsec->ceilingplane; - toppic = sector->GetTexture(sector_t::ceiling); - botpic = heightsec->GetTexture(sector_t::ceiling); - map = heightsec->ColorMap->Maps; - } - else if (fakeside == FAKED_BelowFloor) - { - topplane = &heightsec->floorplane; - botplane = §or->floorplane; - toppic = heightsec->GetTexture(sector_t::floor); - botpic = sector->GetTexture(sector_t::floor); - map = heightsec->ColorMap->Maps; - } - else - { - topplane = &heightsec->ceilingplane; - botplane = &heightsec->floorplane; - toppic = heightsec->GetTexture(sector_t::ceiling); - botpic = heightsec->GetTexture(sector_t::floor); - map = sector->ColorMap->Maps; - } - } - else - { - topplane = §or->ceilingplane; - botplane = §or->floorplane; - toppic = sector->GetTexture(sector_t::ceiling); - botpic = sector->GetTexture(sector_t::floor); - map = sector->ColorMap->Maps; - } - - if (botpic != skyflatnum && particle->Pos.Z < botplane->ZatPoint (particle->Pos)) - return; - if (toppic != skyflatnum && particle->Pos.Z >= topplane->ZatPoint (particle->Pos)) - return; - - // store information in a vissprite - vis = R_NewVisSprite (); - vis->CurrentPortalUniq = CurrentPortalUniq; - vis->heightsec = heightsec; - vis->xscale = FLOAT2FIXED(xscale); - vis->yscale = (float)xscale; -// vis->yscale *= InvZtoScale; - vis->depth = (float)tz; - vis->idepth = float(1 / tz); - vis->gpos = { (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z }; - vis->y1 = y1; - vis->y2 = y2; - vis->x1 = x1; - vis->x2 = x2; - vis->Translation = 0; - vis->startfrac = 255 & (particle->color >>24); - vis->pic = NULL; - vis->bIsVoxel = false; - vis->renderflags = short(particle->alpha * 255); - vis->FakeFlatStat = fakeside; - vis->floorclip = 0; - vis->ColormapNum = 0; - - if (fixedlightlev >= 0) - { - vis->colormap = map + fixedlightlev; - } - else if (fixedcolormap) - { - vis->colormap = fixedcolormap; - } - else if (particle->bright) - { - vis->colormap = (r_fullbrightignoresectorcolor) ? FullNormalLight.Maps : map; - } - else - { - // Particles are slightly more visible than regular sprites. - vis->ColormapNum = GETPALOOKUP(tiz * r_SpriteVisibility * 0.5, shade); - vis->colormap = map + (vis->ColormapNum << COLORMAPSHIFT); - } -} - -static void R_DrawMaskedSegsBehindParticle (const vissprite_t *vis) -{ - const int x1 = vis->x1; - const int x2 = vis->x2; - - // Draw any masked textures behind this particle so that when the - // particle is drawn, it will be in front of them. - for (unsigned int p = InterestingDrawsegs.Size(); p-- > FirstInterestingDrawseg; ) - { - drawseg_t *ds = &drawsegs[InterestingDrawsegs[p]]; - // kg3D - no fake segs - if(ds->fake) continue; - if (ds->x1 >= x2 || ds->x2 <= x1) - { - continue; - } - if ((ds->siz2 - ds->siz1) * ((x2 + x1)/2 - ds->sx1) / (ds->sx2 - ds->sx1) + ds->siz1 < vis->idepth) - { - // [ZZ] only draw stuff that's inside the same portal as the particle, other portals will care for themselves - if (ds->CurrentPortalUniq == vis->CurrentPortalUniq) - R_RenderMaskedSegRange (ds, MAX(ds->x1, x1), MIN(ds->x2, x2)); - } - } -} - -void R_DrawParticle_C (vissprite_t *vis) -{ - DWORD *bg2rgb; - int spacing; - BYTE *dest; - DWORD fg; - BYTE color = vis->colormap[vis->startfrac]; - int yl = vis->y1; - int ycount = vis->y2 - yl + 1; - int x1 = vis->x1; - int countbase = vis->x2 - x1; - - R_DrawMaskedSegsBehindParticle (vis); - - DrawerCommandQueue::WaitForWorkers(); - - // vis->renderflags holds translucency level (0-255) - { - fixed_t fglevel, bglevel; - DWORD *fg2rgb; - - fglevel = ((vis->renderflags + 1) << 8) & ~0x3ff; - bglevel = FRACUNIT-fglevel; - fg2rgb = Col2RGB8[fglevel>>10]; - bg2rgb = Col2RGB8[bglevel>>10]; - fg = fg2rgb[color]; - } - - /* - - spacing = RenderTarget->GetPitch() - countbase; - dest = ylookup[yl] + x1 + dc_destorg; - - do - { - int count = countbase; - do - { - DWORD bg = bg2rgb[*dest]; - bg = (fg+bg) | 0x1f07c1f; - *dest++ = RGB32k.All[bg & (bg>>15)]; - } while (--count); - dest += spacing; - } while (--ycount);*/ - - // original was row-wise - // width = countbase - // height = ycount - - spacing = RenderTarget->GetPitch(); - - for (int x = x1; x < (x1+countbase); x++) - { - dc_x = x; - if (R_ClipSpriteColumnWithPortals(vis)) - continue; - dest = ylookup[yl] + x + dc_destorg; - for (int y = 0; y < ycount; y++) - { - DWORD bg = bg2rgb[*dest]; - bg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k.All[bg & (bg>>15)]; - dest += spacing; - } - } -} - -extern double BaseYaspectMul;; - -inline int sgn(int v) -{ - return v < 0 ? -1 : v > 0 ? 1 : 0; -} - -void R_DrawVoxel(const FVector3 &globalpos, FAngle viewangle, - const FVector3 &dasprpos, DAngle dasprang, - fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, - lighttable_t *colormap, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags) -{ - int i, j, k, x, y, syoff, ggxstart, ggystart, nxoff; - fixed_t cosang, sinang, sprcosang, sprsinang; - int backx, backy, gxinc, gyinc; - int daxscalerecip, dayscalerecip, cnt, gxstart, gystart, dazscale; - int lx, rx, nx, ny, x1=0, y1=0, x2=0, y2=0, yinc=0; - int yoff, xs=0, ys=0, xe, ye, xi=0, yi=0, cbackx, cbacky, dagxinc, dagyinc; - kvxslab_t *voxptr, *voxend; - FVoxelMipLevel *mip; - int z1a[64], z2a[64], yplc[64]; - - const int nytooclose = centerxwide * 2100, nytoofar = 32768*32768 - 1048576; - const int xdimenscale = FLOAT2FIXED(centerxwide * YaspectMul / 160); - const double centerxwide_f = centerxwide; - const double centerxwidebig_f = centerxwide_f * 65536*65536*8; - - // Convert to Build's coordinate system. - fixed_t globalposx = xs_Fix<4>::ToFix(globalpos.X); - fixed_t globalposy = xs_Fix<4>::ToFix(-globalpos.Y); - fixed_t globalposz = xs_Fix<8>::ToFix(-globalpos.Z); - - fixed_t dasprx = xs_Fix<4>::ToFix(dasprpos.X); - fixed_t daspry = xs_Fix<4>::ToFix(-dasprpos.Y); - fixed_t dasprz = xs_Fix<8>::ToFix(-dasprpos.Z); - - // Shift the scales from 16 bits of fractional precision to 6. - // Also do some magic voodoo scaling to make them the right size. - daxscale = daxscale / (0xC000 >> 6); - dayscale = dayscale / (0xC000 >> 6); - if (daxscale <= 0 || dayscale <= 0) - { - // won't be visible. - return; - } - - angle_t viewang = viewangle.BAMs(); - cosang = FLOAT2FIXED(viewangle.Cos()) >> 2; - sinang = FLOAT2FIXED(-viewangle.Sin()) >> 2; - sprcosang = FLOAT2FIXED(dasprang.Cos()) >> 2; - sprsinang = FLOAT2FIXED(-dasprang.Sin()) >> 2; - - R_SetupDrawSlab(colormap); - - // Select mip level - i = abs(DMulScale6(dasprx - globalposx, cosang, daspry - globalposy, sinang)); - i = DivScale6(i, MIN(daxscale, dayscale)); - j = xs_Fix<13>::ToFix(FocalLengthX); - for (k = 0; i >= j && k < voxobj->NumMips; ++k) - { - i >>= 1; - } - if (k >= voxobj->NumMips) k = voxobj->NumMips - 1; - - mip = &voxobj->Mips[k]; if (mip->SlabData == NULL) return; - - minslabz >>= k; - maxslabz >>= k; - - daxscale <<= (k+8); dayscale <<= (k+8); - dazscale = FixedDiv(dayscale, FLOAT2FIXED(BaseYaspectMul)); - daxscale = fixed_t(daxscale / YaspectMul); - daxscale = Scale(daxscale, xdimenscale, centerxwide << 9); - dayscale = Scale(dayscale, FixedMul(xdimenscale, viewingrangerecip), centerxwide << 9); - - daxscalerecip = (1<<30) / daxscale; - dayscalerecip = (1<<30) / dayscale; - - fixed_t piv_x = fixed_t(mip->Pivot.X*256.); - fixed_t piv_y = fixed_t(mip->Pivot.Y*256.); - fixed_t piv_z = fixed_t(mip->Pivot.Z*256.); - - x = FixedMul(globalposx - dasprx, daxscalerecip); - y = FixedMul(globalposy - daspry, daxscalerecip); - backx = (DMulScale10(x, sprcosang, y, sprsinang) + piv_x) >> 8; - backy = (DMulScale10(y, sprcosang, x, -sprsinang) + piv_y) >> 8; - cbackx = clamp(backx, 0, mip->SizeX - 1); - cbacky = clamp(backy, 0, mip->SizeY - 1); - - sprcosang = MulScale14(daxscale, sprcosang); - sprsinang = MulScale14(daxscale, sprsinang); - - x = (dasprx - globalposx) - DMulScale18(piv_x, sprcosang, piv_y, -sprsinang); - y = (daspry - globalposy) - DMulScale18(piv_y, sprcosang, piv_x, sprsinang); - - cosang = FixedMul(cosang, dayscalerecip); - sinang = FixedMul(sinang, dayscalerecip); - - gxstart = y*cosang - x*sinang; - gystart = x*cosang + y*sinang; - gxinc = DMulScale10(sprsinang, cosang, sprcosang, -sinang); - gyinc = DMulScale10(sprcosang, cosang, sprsinang, sinang); - if ((abs(globalposz - dasprz) >> 10) >= abs(dazscale)) return; - - x = 0; y = 0; j = MAX(mip->SizeX, mip->SizeY); - fixed_t *ggxinc = (fixed_t *)alloca((j + 1) * sizeof(fixed_t) * 2); - fixed_t *ggyinc = ggxinc + (j + 1); - for (i = 0; i <= j; i++) - { - ggxinc[i] = x; x += gxinc; - ggyinc[i] = y; y += gyinc; - } - - syoff = DivScale21(globalposz - dasprz, FixedMul(dazscale, 0xE800)) + (piv_z << 7); - yoff = (abs(gxinc) + abs(gyinc)) >> 1; - - for (cnt = 0; cnt < 8; cnt++) - { - switch (cnt) - { - case 0: xs = 0; ys = 0; xi = 1; yi = 1; break; - case 1: xs = mip->SizeX-1; ys = 0; xi = -1; yi = 1; break; - case 2: xs = 0; ys = mip->SizeY-1; xi = 1; yi = -1; break; - case 3: xs = mip->SizeX-1; ys = mip->SizeY-1; xi = -1; yi = -1; break; - case 4: xs = 0; ys = cbacky; xi = 1; yi = 2; break; - case 5: xs = mip->SizeX-1; ys = cbacky; xi = -1; yi = 2; break; - case 6: xs = cbackx; ys = 0; xi = 2; yi = 1; break; - case 7: xs = cbackx; ys = mip->SizeY-1; xi = 2; yi = -1; break; - } - xe = cbackx; ye = cbacky; - if (cnt < 4) - { - if ((xi < 0) && (xe >= xs)) continue; - if ((xi > 0) && (xe <= xs)) continue; - if ((yi < 0) && (ye >= ys)) continue; - if ((yi > 0) && (ye <= ys)) continue; - } - else - { - if ((xi < 0) && (xe > xs)) continue; - if ((xi > 0) && (xe < xs)) continue; - if ((yi < 0) && (ye > ys)) continue; - if ((yi > 0) && (ye < ys)) continue; - xe += xi; ye += yi; - } - - i = sgn(ys - backy) + sgn(xs - backx) * 3 + 4; - switch(i) - { - case 6: case 7: x1 = 0; y1 = 0; break; - case 8: case 5: x1 = gxinc; y1 = gyinc; break; - case 0: case 3: x1 = gyinc; y1 = -gxinc; break; - case 2: case 1: x1 = gxinc+gyinc; y1 = gyinc-gxinc; break; - } - switch(i) - { - case 2: case 5: x2 = 0; y2 = 0; break; - case 0: case 1: x2 = gxinc; y2 = gyinc; break; - case 8: case 7: x2 = gyinc; y2 = -gxinc; break; - case 6: case 3: x2 = gxinc+gyinc; y2 = gyinc-gxinc; break; - } - BYTE oand = (1 << int(xs 0) { dagxinc = gxinc; dagyinc = FixedMul(gyinc, viewingrangerecip); } - else { dagxinc = -gxinc; dagyinc = -FixedMul(gyinc, viewingrangerecip); } - - /* Fix for non 90 degree viewing ranges */ - nxoff = FixedMul(x2 - x1, viewingrangerecip); - x1 = FixedMul(x1, viewingrangerecip); - - ggxstart = gxstart + ggyinc[ys]; - ggystart = gystart - ggxinc[ys]; - - for (x = xs; x != xe; x += xi) - { - BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; - short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)]; - - nx = FixedMul(ggxstart + ggxinc[x], viewingrangerecip) + x1; - ny = ggystart + ggyinc[x]; - for (y = ys; y != ye; y += yi, nx += dagyinc, ny -= dagxinc) - { - if ((ny <= nytooclose) || (ny >= nytoofar)) continue; - voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]); - voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]); - if (voxptr >= voxend) continue; - - lx = xs_RoundToInt(nx * centerxwide_f / (ny + y1)) + centerx; - if (lx < 0) lx = 0; - rx = xs_RoundToInt((nx + nxoff) * centerxwide_f / (ny + y2)) + centerx; - if (rx > viewwidth) rx = viewwidth; - if (rx <= lx) continue; - - if (flags & DVF_MIRRORED) - { - int t = viewwidth - lx; - lx = viewwidth - rx; - rx = t; - } - - fixed_t l1 = xs_RoundToInt(centerxwidebig_f / (ny - yoff)); - fixed_t l2 = xs_RoundToInt(centerxwidebig_f / (ny + yoff)); - for (; voxptr < voxend; voxptr = (kvxslab_t *)((BYTE *)voxptr + voxptr->zleng + 3)) - { - const BYTE *col = voxptr->col; - int zleng = voxptr->zleng; - int ztop = voxptr->ztop; - fixed_t z1, z2; - - if (ztop < minslabz) - { - int diff = minslabz - ztop; - ztop = minslabz; - col += diff; - zleng -= diff; - } - if (ztop + zleng > maxslabz) - { - int diff = ztop + zleng - maxslabz; - zleng -= diff; - } - if (zleng <= 0) continue; - - j = (ztop << 15) - syoff; - if (j < 0) - { - k = j + (zleng << 15); - if (k < 0) - { - if ((voxptr->backfacecull & oand32) == 0) continue; - z2 = MulScale32(l2, k) + centery; /* Below slab */ - } - else - { - if ((voxptr->backfacecull & oand) == 0) continue; /* Middle of slab */ - z2 = MulScale32(l1, k) + centery; - } - z1 = MulScale32(l1, j) + centery; - } - else - { - if ((voxptr->backfacecull & oand16) == 0) continue; - z1 = MulScale32(l2, j) + centery; /* Above slab */ - z2 = MulScale32(l1, j + (zleng << 15)) + centery; - } - - if (z2 <= z1) continue; - - if (zleng == 1) - { - yinc = 0; - } - else - { - if (z2-z1 >= 1024) yinc = FixedDiv(zleng, z2 - z1); - else yinc = (((1 << 24) - 1) / (z2 - z1)) * zleng >> 8; - } - // [RH] Clip each column separately, not just by the first one. - for (int stripwidth = MIN(countof(z1a), rx - lx), lxt = lx; - lxt < rx; - (lxt += countof(z1a)), stripwidth = MIN(countof(z1a), rx - lxt)) - { - // Calculate top and bottom pixels locations - for (int xxx = 0; xxx < stripwidth; ++xxx) - { - if (zleng == 1) - { - yplc[xxx] = 0; - z1a[xxx] = MAX(z1, daumost[lxt + xxx]); - } - else - { - if (z1 < daumost[lxt + xxx]) - { - yplc[xxx] = yinc * (daumost[lxt + xxx] - z1); - z1a[xxx] = daumost[lxt + xxx]; - } - else - { - yplc[xxx] = 0; - z1a[xxx] = z1; - } - } - z2a[xxx] = MIN(z2, dadmost[lxt + xxx]); - } - // Find top and bottom pixels that match and draw them as one strip - for (int xxl = 0, xxr; xxl < stripwidth; ) - { - if (z1a[xxl] >= z2a[xxl]) - { // No column here - xxl++; - continue; - } - int z1 = z1a[xxl]; - int z2 = z2a[xxl]; - // How many columns share the same extents? - for (xxr = xxl + 1; xxr < stripwidth; ++xxr) - { - if (z1a[xxr] != z1 || z2a[xxr] != z2) - break; - } - - if (!(flags & DVF_OFFSCREEN)) - { - // Draw directly to the screen. - R_DrawSlab(xxr - xxl, yplc[xxl], z2 - z1, yinc, col, ylookup[z1] + lxt + xxl + dc_destorg); - } - else - { - // Record the area covered and possibly draw to an offscreen buffer. - dc_yl = z1; - dc_yh = z2 - 1; - dc_count = z2 - z1; - dc_iscale = yinc; - for (int x = xxl; x < xxr; ++x) - { - OffscreenCoverageBuffer->InsertSpan(lxt + x, z1, z2); - if (!(flags & DVF_SPANSONLY)) - { - dc_x = lxt + x; - rt_initcols(OffscreenColorBuffer + (dc_x & ~3) * OffscreenBufferHeight); - dc_source = col; - dc_texturefrac = yplc[xxl]; - hcolfunc_pre(); - } - } - } - xxl = xxr; - } - } - } - } - } - } -} - -//========================================================================== -// -// FCoverageBuffer Constructor -// -//========================================================================== - -FCoverageBuffer::FCoverageBuffer(int lists) - : Spans(NULL), FreeSpans(NULL) -{ - NumLists = lists; - Spans = new Span *[lists]; - memset(Spans, 0, sizeof(Span*)*lists); -} - -//========================================================================== -// -// FCoverageBuffer Destructor -// -//========================================================================== - -FCoverageBuffer::~FCoverageBuffer() -{ - if (Spans != NULL) - { - delete[] Spans; - } -} - -//========================================================================== -// -// FCoverageBuffer :: Clear -// -//========================================================================== - -void FCoverageBuffer::Clear() -{ - SpanArena.FreeAll(); - memset(Spans, 0, sizeof(Span*)*NumLists); - FreeSpans = NULL; -} - -//========================================================================== -// -// FCoverageBuffer :: InsertSpan -// -// start is inclusive. -// stop is exclusive. -// -//========================================================================== - -void FCoverageBuffer::InsertSpan(int listnum, int start, int stop) -{ - assert(unsigned(listnum) < NumLists); - assert(start < stop); - - Span **span_p = &Spans[listnum]; - Span *span; - - if (*span_p == NULL || (*span_p)->Start > stop) - { // This list is empty or the first entry is after this one, so we can just insert the span. - goto addspan; - } - - // Insert the new span in order, merging with existing ones. - while (*span_p != NULL) - { - if ((*span_p)->Stop < start) // ===== (existing span) - { // Span ends before this one starts. // ++++ (new span) - span_p = &(*span_p)->NextSpan; - continue; - } - - // Does the new span overlap or abut the existing one? - if ((*span_p)->Start <= start) - { - if ((*span_p)->Stop >= stop) // ============= - { // The existing span completely covers this one. // +++++ - return; - } -extend: // Extend the existing span with the new one. // ====== - span = *span_p; // +++++++ - span->Stop = stop; // (or) +++++ - - // Free up any spans we just covered up. - span_p = &(*span_p)->NextSpan; - while (*span_p != NULL && (*span_p)->Start <= stop && (*span_p)->Stop <= stop) - { - Span *span = *span_p; // ====== ====== - *span_p = span->NextSpan; // +++++++++++++ - span->NextSpan = FreeSpans; - FreeSpans = span; - } - if (*span_p != NULL && (*span_p)->Start <= stop) // ======= ======== - { // Our new span connects two existing spans. // ++++++++++++++ - // They should all be collapsed into a single span. - span->Stop = (*span_p)->Stop; - span = *span_p; - *span_p = span->NextSpan; - span->NextSpan = FreeSpans; - FreeSpans = span; - } - goto check; - } - else if ((*span_p)->Start <= stop) // ===== - { // The new span extends the existing span from // ++++ - // the beginning. // (or) ++++ - (*span_p)->Start = start; - if ((*span_p)->Stop < stop) - { // The new span also extends the existing span // ====== - // at the bottom // ++++++++++++++ - goto extend; - } - goto check; - } - else // ====== - { // No overlap, so insert a new span. // +++++ - goto addspan; - } - } - // Append a new span to the end of the list. -addspan: - span = AllocSpan(); - span->NextSpan = *span_p; - span->Start = start; - span->Stop = stop; - *span_p = span; -check: -#ifdef _DEBUG - // Validate the span list: Spans must be in order, and there must be - // at least one pixel between spans. - for (span = Spans[listnum]; span != NULL; span = span->NextSpan) - { - assert(span->Start < span->Stop); - if (span->NextSpan != NULL) - { - assert(span->Stop < span->NextSpan->Start); - } - } -#endif - ; -} - -//========================================================================== -// -// FCoverageBuffer :: AllocSpan -// -//========================================================================== - -FCoverageBuffer::Span *FCoverageBuffer::AllocSpan() -{ - Span *span; - - if (FreeSpans != NULL) - { - span = FreeSpans; - FreeSpans = span->NextSpan; - } - else - { - span = (Span *)SpanArena.Alloc(sizeof(Span)); - } - return span; -} - -//========================================================================== -// -// R_CheckOffscreenBuffer -// -// Allocates the offscreen coverage buffer and optionally the offscreen -// color buffer. If they already exist but are the wrong size, they will -// be reallocated. -// -//========================================================================== - -void R_CheckOffscreenBuffer(int width, int height, bool spansonly) -{ - if (OffscreenCoverageBuffer == NULL) - { - assert(OffscreenColorBuffer == NULL && "The color buffer cannot exist without the coverage buffer"); - OffscreenCoverageBuffer = new FCoverageBuffer(width); - } - else if (OffscreenCoverageBuffer->NumLists != (unsigned)width) - { - delete OffscreenCoverageBuffer; - OffscreenCoverageBuffer = new FCoverageBuffer(width); - if (OffscreenColorBuffer != NULL) - { - delete[] OffscreenColorBuffer; - OffscreenColorBuffer = NULL; - } - } - else - { - OffscreenCoverageBuffer->Clear(); - } - - if (!spansonly) - { - if (OffscreenColorBuffer == NULL) - { - OffscreenColorBuffer = new BYTE[width * height]; - } - else if (OffscreenBufferWidth != width || OffscreenBufferHeight != height) - { - delete[] OffscreenColorBuffer; - OffscreenColorBuffer = new BYTE[width * height]; - } - } - OffscreenBufferWidth = width; - OffscreenBufferHeight = height; -} - -} diff --git a/src/r_things.h b/src/r_things.h deleted file mode 100644 index 07676dc447..0000000000 --- a/src/r_things.h +++ /dev/null @@ -1,157 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Rendering of moving objects, sprites. -// -//----------------------------------------------------------------------------- - - -#ifndef __R_THINGS__ -#define __R_THINGS__ - -#include "r_bsp.h" - -struct particle_t; -struct FVoxel; - -namespace swrenderer -{ - -// A vissprite_t is a thing -// that will be drawn during a refresh. -// I.e. a sprite object that is partly visible. - -struct vissprite_t -{ - struct posang - { - FVector3 vpos; // view origin - FAngle vang; // view angle - }; - - short x1, x2; - FVector3 gpos; // origin in world coordinates - union - { - struct - { - float gzb, gzt; // global bottom / top for silhouette clipping - }; - struct - { - int y1, y2; // top / bottom of particle on screen - }; - }; - DAngle Angle; - fixed_t xscale; - float yscale; - float depth; - float idepth; // 1/z - float deltax, deltay; - DWORD FillColor; - double floorclip; - union - { - FTexture *pic; - struct FVoxel *voxel; - }; - union - { - // Used by face sprites - struct - { - double texturemid; - fixed_t startfrac; // horizontal position of x1 - fixed_t xiscale; // negative if flipped - }; - // Used by wall sprites - FWallCoords wallc; - // Used by voxels - posang pa; - }; - sector_t *heightsec; // killough 3/27/98: height sector for underwater/fake ceiling - sector_t *sector; // [RH] sector this sprite is in - F3DFloor *fakefloor; - F3DFloor *fakeceiling; - BYTE bIsVoxel:1; // [RH] Use voxel instead of pic - BYTE bWallSprite:1; // [RH] This is a wall sprite - BYTE bSplitSprite:1; // [RH] Sprite was split by a drawseg - BYTE bInMirror:1; // [RH] Sprite is "inside" a mirror - BYTE FakeFlatStat; // [RH] which side of fake/floor ceiling sprite is on - BYTE ColormapNum; // Which colormap is rendered (needed for shaded drawer) - short renderflags; - DWORD Translation; // [RH] for color translation - lighttable_t *colormap; - FRenderStyle RenderStyle; - visstyle_t Style; - int CurrentPortalUniq; // [ZZ] to identify the portal that this thing is in. used for clipping. - - vissprite_t() {} -}; - -void R_DrawParticle_C (vissprite_t *); -void R_ProjectParticle (particle_t *, const sector_t *sector, int shade, int fakeside); - -extern int MaxVisSprites; - -extern vissprite_t **vissprites, **firstvissprite; -extern vissprite_t **vissprite_p; - -// Constant arrays used for psprite clipping -// and initializing clipping. -extern short zeroarray[MAXWIDTH]; -extern short screenheightarray[MAXWIDTH]; - -// vars for R_DrawMaskedColumn -extern short* mfloorclip; -extern short* mceilingclip; -extern double spryscale; -extern double sprtopscreen; -extern bool sprflipvert; - -extern double pspritexscale; -extern double pspritexiscale; -extern double pspriteyscale; - -extern FTexture *WallSpriteTile; - - -void R_DrawMaskedColumn (FTexture *texture, fixed_t column, bool useRt, bool unmasked = false); -void R_WallSpriteColumn (bool useRt); - -void R_CacheSprite (spritedef_t *sprite); -void R_SortVisSprites (int (*compare)(const void *, const void *), size_t first); -void R_AddSprites (sector_t *sec, int lightlevel, int fakeside); -void R_DrawSprites (); -void R_ClearSprites (); -void R_DrawMasked (); -void R_DrawRemainingPlayerSprites (); - -void R_CheckOffscreenBuffer(int width, int height, bool spansonly); - -enum { DVF_OFFSCREEN = 1, DVF_SPANSONLY = 2, DVF_MIRRORED = 4 }; - -void R_DrawVoxel(const FVector3 &viewpos, FAngle viewangle, - const FVector3 &sprpos, DAngle dasprang, - fixed_t daxscale, fixed_t dayscale, struct FVoxel *voxobj, - lighttable_t *colormap, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags); - -void R_ClipVisSprite (vissprite_t *vis, int xl, int xh); - -} - -#endif diff --git a/src/r_utility.cpp b/src/r_utility.cpp index a422c3126b..0662269a60 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -101,55 +101,48 @@ CUSTOM_CVAR(Float, r_quakeintensity, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) else if (self > 1.f) self = 1.f; } -DCanvas *RenderTarget; // [RH] canvas to render to - int viewwindowx; int viewwindowy; +int viewwidth; +int viewheight; -DVector3 ViewPos; -DVector3 ViewActorPos; // the actual position of the viewing actor, without interpolation and quake offsets. -DAngle ViewAngle; -DAngle ViewPitch; -DAngle ViewRoll; -DVector3 ViewPath[2]; - -extern "C" +FRenderViewpoint::FRenderViewpoint() { - int viewwidth; - int viewheight; - int centerx; - int centery; - int centerxwide; + player = nullptr; + Pos = { 0.0, 0.0, 0.0 }; + ActorPos = { 0.0, 0.0, 0.0 }; + Angles = { 0.0, 0.0, 0.0 }; + Path[0] = { 0.0, 0.0, 0.0 }; + Path[1] = { 0.0, 0.0, 0.0 }; + Cos = 0.0; + Sin = 0.0; + TanCos = 0.0; + TanSin = 0.0; + camera = nullptr; + sector = nullptr; + FieldOfView = 90.; // Angles in the SCREENWIDTH wide window + TicFrac = 0.0; + FrameTime = 0; + extralight = 0; + showviewer = false; } +FRenderViewpoint r_viewpoint; +FViewWindow r_viewwindow; + int otic; -sector_t *viewsector; - -double ViewCos, ViewTanCos; -double ViewSin, ViewTanSin; - -AActor *camera; // [RH] camera to draw from. doesn't have to be a player - -double r_TicFracF; // same as floating point -uint32_t r_FrameTime; // [RH] Time this frame started drawing (in ms) bool r_NoInterpolate; -bool r_showviewer; angle_t LocalViewAngle; int LocalViewPitch; bool LocalKeyboardTurner; -float WidescreenRatio; int setblocks; -int extralight; bool setsizeneeded; -double FocalTangent; unsigned int R_OldBlend = ~0; int validcount = 1; // increment every time a check is made -DAngle FieldOfView = 90.; // Angles in the SCREENWIDTH wide window - FCanvasTextureInfo *FCanvasTextureInfo::List; DVector3a view; @@ -167,14 +160,14 @@ static void R_Shutdown (); // //========================================================================== -void R_SetFOV (DAngle fov) +void R_SetFOV (FRenderViewpoint &viewpoint, DAngle fov) { if (fov < 5.) fov = 5.; else if (fov > 170.) fov = 170.; - if (fov != FieldOfView) + if (fov != viewpoint.FieldOfView) { - FieldOfView = fov; + viewpoint.FieldOfView = fov; setsizeneeded = true; } } @@ -200,10 +193,8 @@ void R_SetViewSize (int blocks) // //========================================================================== -void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, bool renderingToCanvas) +void R_SetWindow (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, int windowSize, int fullWidth, int fullHeight, int stHeight, bool renderingToCanvas) { - float trueratio; - if (windowSize >= 11) { viewwidth = fullWidth; @@ -224,12 +215,11 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, b if (renderingToCanvas) { - WidescreenRatio = fullWidth / (float)fullHeight; - trueratio = WidescreenRatio; + viewwindow.WidescreenRatio = fullWidth / (float)fullHeight; } else { - WidescreenRatio = ActiveRatio(fullWidth, fullHeight, &trueratio); + viewwindow.WidescreenRatio = ActiveRatio(fullWidth, fullHeight); } DrawFSHUD = (windowSize == 11); @@ -237,29 +227,28 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, b // [RH] Sky height fix for screens not 200 (or 240) pixels tall R_InitSkyMap (); - centery = viewheight/2; - centerx = viewwidth/2; - if (AspectTallerThanWide(WidescreenRatio)) + viewwindow.centery = viewheight/2; + viewwindow.centerx = viewwidth/2; + if (AspectTallerThanWide(viewwindow.WidescreenRatio)) { - centerxwide = centerx; + viewwindow.centerxwide = viewwindow.centerx; } else { - centerxwide = centerx * AspectMultiplier(WidescreenRatio) / 48; + viewwindow.centerxwide = viewwindow.centerx * AspectMultiplier(viewwindow.WidescreenRatio) / 48; } - DAngle fov = FieldOfView; + DAngle fov = viewpoint.FieldOfView; // For widescreen displays, increase the FOV so that the middle part of the // screen that would be visible on a 4:3 display has the requested FOV. - if (centerxwide != centerx) + if (viewwindow.centerxwide != viewwindow.centerx) { // centerxwide is what centerx would be if the display was not widescreen - fov = DAngle::ToDegrees(2 * atan(centerx * tan(fov.Radians()/2) / double(centerxwide))); + fov = DAngle::ToDegrees(2 * atan(viewwindow.centerx * tan(fov.Radians()/2) / double(viewwindow.centerxwide))); if (fov > 170.) fov = 170.; } - FocalTangent = tan(fov.Radians() / 2); - Renderer->SetWindow(windowSize, fullWidth, fullHeight, stHeight, trueratio); + viewwindow.FocalTangent = tan(fov.Radians() / 2); } //========================================================================== @@ -268,12 +257,12 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, b // //========================================================================== -void R_ExecuteSetViewSize () +void R_ExecuteSetViewSize (FRenderViewpoint &viewpoint, FViewWindow &viewwindow) { setsizeneeded = false; V_SetBorderNeedRefresh(); - R_SetWindow (setblocks, SCREENWIDTH, SCREENHEIGHT, gST_Y); + R_SetWindow (viewpoint, viewwindow, setblocks, SCREENWIDTH, SCREENHEIGHT, gST_Y); // Handle resize, e.g. smaller view windows with border and/or status bar. viewwindowx = (screen->GetWidth() - viewwidth) >> 1; @@ -369,7 +358,7 @@ static void R_Shutdown () //CVAR (Int, tf, 0, 0) EXTERN_CVAR (Bool, cl_noprediction) -void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *iview) +void R_InterpolateView (FRenderViewpoint &viewpoint, player_t *player, double Frac, InterpolationViewer *iview) { if (NoInterpolateView) { @@ -392,7 +381,7 @@ void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *ivie // What needs be done is to store the portal transitions of the camera actor as waypoints // and then find out on which part of the path the current view lies. // Needless to say, this doesn't work for chasecam mode. - if (!r_showviewer) + if (!viewpoint.showviewer) { double pathlen = 0; double zdiff = 0; @@ -434,34 +423,34 @@ void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *ivie oviewangle += adiff; nviewangle -= totaladiff - adiff; DVector2 viewpos = start.pos + (fragfrac * (end.pos - start.pos)); - ViewPos = { viewpos, oviewz + Frac * (nviewz - oviewz) }; + viewpoint.Pos = { viewpos, oviewz + Frac * (nviewz - oviewz) }; break; } } InterpolationPath.Pop(); - ViewPath[0] = iview->Old.Pos; - ViewPath[1] = ViewPath[0] + (InterpolationPath[0].pos - ViewPath[0]).XY().MakeResize(pathlen); + viewpoint.Path[0] = iview->Old.Pos; + viewpoint.Path[1] = viewpoint.Path[0] + (InterpolationPath[0].pos - viewpoint.Path[0]).XY().MakeResize(pathlen); } } else { DVector2 disp = Displacements.getOffset(oldgroup, newgroup); - ViewPos = iview->Old.Pos + (iview->New.Pos - iview->Old.Pos - disp) * Frac; - ViewPath[0] = ViewPath[1] = iview->New.Pos; + viewpoint.Pos = iview->Old.Pos + (iview->New.Pos - iview->Old.Pos - disp) * Frac; + viewpoint.Path[0] = viewpoint.Path[1] = iview->New.Pos; } } else { - ViewPos = iview->New.Pos; - ViewPath[0] = ViewPath[1] = iview->New.Pos; + viewpoint.Pos = iview->New.Pos; + viewpoint.Path[0] = viewpoint.Path[1] = iview->New.Pos; } if (player != NULL && !(player->cheats & CF_INTERPVIEW) && player - players == consoleplayer && - camera == player->mo && + viewpoint.camera == player->mo && !demoplayback && - iview->New.Pos.X == camera->X() && - iview->New.Pos.Y == camera->Y() && + iview->New.Pos.X == viewpoint.camera->X() && + iview->New.Pos.Y == viewpoint.camera->Y() && !(player->cheats & (CF_TOTALLYFROZEN|CF_FROZEN)) && player->playerstate == PST_LIVE && player->mo->reactiontime == 0 && @@ -470,41 +459,41 @@ void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *ivie (!netgame || !cl_noprediction) && !LocalKeyboardTurner) { - ViewAngle = (nviewangle + AngleToFloat(LocalViewAngle & 0xFFFF0000)).Normalized180(); + viewpoint.Angles.Yaw = (nviewangle + AngleToFloat(LocalViewAngle & 0xFFFF0000)).Normalized180(); DAngle delta = player->centering ? DAngle(0.) : AngleToFloat(int(LocalViewPitch & 0xFFFF0000)); - ViewPitch = clamp((iview->New.Angles.Pitch - delta).Normalized180(), player->MinPitch, player->MaxPitch); - ViewRoll = iview->New.Angles.Roll.Normalized180(); + viewpoint.Angles.Pitch = clamp((iview->New.Angles.Pitch - delta).Normalized180(), player->MinPitch, player->MaxPitch); + viewpoint.Angles.Roll = iview->New.Angles.Roll.Normalized180(); } else { - ViewPitch = (iview->Old.Angles.Pitch + deltaangle(iview->Old.Angles.Pitch, iview->New.Angles.Pitch) * Frac).Normalized180(); - ViewAngle = (oviewangle + deltaangle(oviewangle, nviewangle) * Frac).Normalized180(); - ViewRoll = (iview->Old.Angles.Roll + deltaangle(iview->Old.Angles.Roll, iview->New.Angles.Roll) * Frac).Normalized180(); + viewpoint.Angles.Pitch = (iview->Old.Angles.Pitch + deltaangle(iview->Old.Angles.Pitch, iview->New.Angles.Pitch) * Frac).Normalized180(); + viewpoint.Angles.Yaw = (oviewangle + deltaangle(oviewangle, nviewangle) * Frac).Normalized180(); + viewpoint.Angles.Roll = (iview->Old.Angles.Roll + deltaangle(iview->Old.Angles.Roll, iview->New.Angles.Roll) * Frac).Normalized180(); } // Due to interpolation this is not necessarily the same as the sector the camera is in. - viewsector = R_PointInSubsector(ViewPos)->sector; + viewpoint.sector = R_PointInSubsector(viewpoint.Pos)->sector; bool moved = false; - while (!viewsector->PortalBlocksMovement(sector_t::ceiling)) + while (!viewpoint.sector->PortalBlocksMovement(sector_t::ceiling)) { - if (ViewPos.Z > viewsector->GetPortalPlaneZ(sector_t::ceiling)) + if (viewpoint.Pos.Z > viewpoint.sector->GetPortalPlaneZ(sector_t::ceiling)) { - ViewPos += viewsector->GetPortalDisplacement(sector_t::ceiling); - ViewActorPos += viewsector->GetPortalDisplacement(sector_t::ceiling); - viewsector = R_PointInSubsector(ViewPos)->sector; + viewpoint.Pos += viewpoint.sector->GetPortalDisplacement(sector_t::ceiling); + viewpoint.ActorPos += viewpoint.sector->GetPortalDisplacement(sector_t::ceiling); + viewpoint.sector = R_PointInSubsector(viewpoint.Pos)->sector; moved = true; } else break; } if (!moved) { - while (!viewsector->PortalBlocksMovement(sector_t::floor)) + while (!viewpoint.sector->PortalBlocksMovement(sector_t::floor)) { - if (ViewPos.Z < viewsector->GetPortalPlaneZ(sector_t::floor)) + if (viewpoint.Pos.Z < viewpoint.sector->GetPortalPlaneZ(sector_t::floor)) { - ViewPos += viewsector->GetPortalDisplacement(sector_t::floor); - ViewActorPos += viewsector->GetPortalDisplacement(sector_t::floor); - viewsector = R_PointInSubsector(ViewPos)->sector; + viewpoint.Pos += viewpoint.sector->GetPortalDisplacement(sector_t::floor); + viewpoint.ActorPos += viewpoint.sector->GetPortalDisplacement(sector_t::floor); + viewpoint.sector = R_PointInSubsector(viewpoint.Pos)->sector; moved = true; } else break; @@ -530,13 +519,13 @@ void R_ResetViewInterpolation () // //========================================================================== -void R_SetViewAngle () +void R_SetViewAngle (FRenderViewpoint &viewpoint, const FViewWindow &viewwindow) { - ViewSin = ViewAngle.Sin(); - ViewCos = ViewAngle.Cos(); + viewpoint.Sin = viewpoint.Angles.Yaw.Sin(); + viewpoint.Cos = viewpoint.Angles.Yaw.Cos(); - ViewTanSin = FocalTangent * ViewSin; - ViewTanCos = FocalTangent * ViewCos; + viewpoint.TanSin = viewwindow.FocalTangent * viewpoint.Sin; + viewpoint.TanCos = viewwindow.FocalTangent * viewpoint.Cos; } //========================================================================== @@ -685,13 +674,15 @@ static double QuakePower(double factor, double intensity, double offset) // //========================================================================== -void R_SetupFrame (AActor *actor) +void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor *actor) { if (actor == NULL) { I_Error ("Tried to render from a NULL actor."); } + PO_LinkToSubsectors(); + player_t *player = actor->player; unsigned int newblend; InterpolationViewer *iview; @@ -699,23 +690,23 @@ void R_SetupFrame (AActor *actor) if (player != NULL && player->mo == actor) { // [RH] Use camera instead of viewplayer - camera = player->camera; - if (camera == NULL) + viewpoint.camera = player->camera; + if (viewpoint.camera == NULL) { - camera = player->camera = player->mo; + viewpoint.camera = player->camera = player->mo; } } else { - camera = actor; + viewpoint.camera = actor; } - if (camera == NULL) + if (viewpoint.camera == NULL) { I_Error ("You lost your body. Bad dehacked work is likely to blame."); } - iview = FindPastViewer (camera); + iview = FindPastViewer (viewpoint.camera); int nowtic = I_GetTime (false); if (iview->otic != -1 && nowtic > iview->otic) @@ -725,38 +716,38 @@ void R_SetupFrame (AActor *actor) } if (player != NULL && gamestate != GS_TITLELEVEL && - ((player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0))) + ((player->cheats & CF_CHASECAM) || (r_deathcamera && viewpoint.camera->health <= 0))) { sector_t *oldsector = R_PointInSubsector(iview->Old.Pos)->sector; // [RH] Use chasecam view DVector3 campos; DAngle camangle; - P_AimCamera (camera, campos, camangle, viewsector, unlinked); // fixme: This needs to translate the angle, too. + P_AimCamera (viewpoint.camera, campos, camangle, viewpoint.sector, unlinked); // fixme: This needs to translate the angle, too. iview->New.Pos = campos; iview->New.Angles.Yaw = camangle; - r_showviewer = true; + viewpoint.showviewer = true; // Interpolating this is a very complicated thing because nothing keeps track of the aim camera's movement, so whenever we detect a portal transition // it's probably best to just reset the interpolation for this move. // Note that this can still cause problems with unusually linked portals - if (viewsector->PortalGroup != oldsector->PortalGroup || (unlinked && ((iview->New.Pos.XY() - iview->Old.Pos.XY()).LengthSquared()) > 256*256)) + if (viewpoint.sector->PortalGroup != oldsector->PortalGroup || (unlinked && ((iview->New.Pos.XY() - iview->Old.Pos.XY()).LengthSquared()) > 256*256)) { iview->otic = nowtic; iview->Old = iview->New; r_NoInterpolate = true; } - ViewActorPos = campos; + viewpoint.ActorPos = campos; } else { - ViewActorPos = iview->New.Pos = { camera->Pos().XY(), camera->player ? camera->player->viewz : camera->Z() + camera->GetCameraHeight() }; - viewsector = camera->Sector; - r_showviewer = false; + viewpoint.ActorPos = iview->New.Pos = { viewpoint.camera->Pos().XY(), viewpoint.camera->player ? viewpoint.camera->player->viewz : viewpoint.camera->Z() + viewpoint.camera->GetCameraHeight() }; + viewpoint.sector = viewpoint.camera->Sector; + viewpoint.showviewer = false; } - iview->New.Angles = camera->Angles; - if (camera->player != 0) + iview->New.Angles = viewpoint.camera->Angles; + if (viewpoint.camera->player != 0) { - player = camera->player; + player = viewpoint.camera->player; } if (iview->otic == -1 || r_NoInterpolate) @@ -765,33 +756,33 @@ void R_SetupFrame (AActor *actor) iview->otic = nowtic; } - r_TicFracF = I_GetTimeFrac (&r_FrameTime); + viewpoint.TicFrac = I_GetTimeFrac (&viewpoint.FrameTime); if (cl_capfps || r_NoInterpolate) { - r_TicFracF = 1.; + viewpoint.TicFrac = 1.; } - R_InterpolateView (player, r_TicFracF, iview); + R_InterpolateView (viewpoint, player, viewpoint.TicFrac, iview); - R_SetViewAngle (); + R_SetViewAngle (viewpoint, viewwindow); - interpolator.DoInterpolations (r_TicFracF); + interpolator.DoInterpolations (viewpoint.TicFrac); // Keep the view within the sector's floor and ceiling - if (viewsector->PortalBlocksMovement(sector_t::ceiling)) + if (viewpoint.sector->PortalBlocksMovement(sector_t::ceiling)) { - double theZ = viewsector->ceilingplane.ZatPoint(ViewPos) - 4; - if (ViewPos.Z > theZ) + double theZ = viewpoint.sector->ceilingplane.ZatPoint(viewpoint.Pos) - 4; + if (viewpoint.Pos.Z > theZ) { - ViewPos.Z = theZ; + viewpoint.Pos.Z = theZ; } } - if (viewsector->PortalBlocksMovement(sector_t::floor)) + if (viewpoint.sector->PortalBlocksMovement(sector_t::floor)) { - double theZ = viewsector->floorplane.ZatPoint(ViewPos) + 4; - if (ViewPos.Z < theZ) + double theZ = viewpoint.sector->floorplane.ZatPoint(viewpoint.Pos) + 4; + if (viewpoint.Pos.Z < theZ) { - ViewPos.Z = theZ; + viewpoint.Pos.Z = theZ; } } @@ -800,62 +791,62 @@ void R_SetupFrame (AActor *actor) FQuakeJiggers jiggers; memset(&jiggers, 0, sizeof(jiggers)); - if (DEarthquake::StaticGetQuakeIntensities(camera, jiggers) > 0) + if (DEarthquake::StaticGetQuakeIntensities(viewpoint.camera, jiggers) > 0) { double quakefactor = r_quakeintensity; DAngle an; if (jiggers.RollIntensity != 0 || jiggers.RollWave != 0) { - ViewRoll += QuakePower(quakefactor, jiggers.RollIntensity, jiggers.RollWave); + viewpoint.Angles.Roll += QuakePower(quakefactor, jiggers.RollIntensity, jiggers.RollWave); } if (jiggers.RelIntensity.X != 0 || jiggers.RelOffset.X != 0) { - an = camera->Angles.Yaw; + an = viewpoint.camera->Angles.Yaw; double power = QuakePower(quakefactor, jiggers.RelIntensity.X, jiggers.RelOffset.X); - ViewPos += an.ToVector(power); + viewpoint.Pos += an.ToVector(power); } if (jiggers.RelIntensity.Y != 0 || jiggers.RelOffset.Y != 0) { - an = camera->Angles.Yaw + 90; + an = viewpoint.camera->Angles.Yaw + 90; double power = QuakePower(quakefactor, jiggers.RelIntensity.Y, jiggers.RelOffset.Y); - ViewPos += an.ToVector(power); + viewpoint.Pos += an.ToVector(power); } // FIXME: Relative Z is not relative if (jiggers.RelIntensity.Z != 0 || jiggers.RelOffset.Z != 0) { - ViewPos.Z += QuakePower(quakefactor, jiggers.RelIntensity.Z, jiggers.RelOffset.Z); + viewpoint.Pos.Z += QuakePower(quakefactor, jiggers.RelIntensity.Z, jiggers.RelOffset.Z); } if (jiggers.Intensity.X != 0 || jiggers.Offset.X != 0) { - ViewPos.X += QuakePower(quakefactor, jiggers.Intensity.X, jiggers.Offset.X); + viewpoint.Pos.X += QuakePower(quakefactor, jiggers.Intensity.X, jiggers.Offset.X); } if (jiggers.Intensity.Y != 0 || jiggers.Offset.Y != 0) { - ViewPos.Y += QuakePower(quakefactor, jiggers.Intensity.Y, jiggers.Offset.Y); + viewpoint.Pos.Y += QuakePower(quakefactor, jiggers.Intensity.Y, jiggers.Offset.Y); } if (jiggers.Intensity.Z != 0 || jiggers.Offset.Z != 0) { - ViewPos.Z += QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z); + viewpoint.Pos.Z += QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z); } } } - extralight = camera->player ? camera->player->extralight : 0; + viewpoint.extralight = viewpoint.camera->player ? viewpoint.camera->player->extralight : 0; // killough 3/20/98, 4/4/98: select colormap based on player status // [RH] Can also select a blend newblend = 0; - TArray &lightlist = viewsector->e->XFloor.lightlist; + TArray &lightlist = viewpoint.sector->e->XFloor.lightlist; if (lightlist.Size() > 0) { for(unsigned int i = 0; i < lightlist.Size(); i++) { secplane_t *plane; int viewside; - plane = (i < lightlist.Size()-1) ? &lightlist[i+1].plane : &viewsector->floorplane; - viewside = plane->PointOnSide(ViewPos); + plane = (i < lightlist.Size()-1) ? &lightlist[i+1].plane : &viewpoint.sector->floorplane; + viewside = plane->PointOnSide(viewpoint.Pos); // Reverse the direction of the test if the plane was downward facing. // We want to know if the view is above it, whatever its orientation may be. if (plane->fC() < 0) @@ -874,12 +865,12 @@ void R_SetupFrame (AActor *actor) } else { - const sector_t *s = viewsector->GetHeightSec(); + const sector_t *s = viewpoint.sector->GetHeightSec(); if (s != NULL) { - newblend = s->floorplane.PointOnSide(ViewPos) < 0 + newblend = s->floorplane.PointOnSide(viewpoint.Pos) < 0 ? s->bottommap - : s->ceilingplane.PointOnSide(ViewPos) < 0 + : s->ceilingplane.PointOnSide(viewpoint.Pos) < 0 ? s->topmap : s->midmap; if (APART(newblend) == 0 && newblend >= numfakecmaps) @@ -899,22 +890,19 @@ void R_SetupFrame (AActor *actor) BaseBlendG = GPART(newblend); BaseBlendB = BPART(newblend); BaseBlendA = APART(newblend) / 255.f; - NormalLight.Maps = realcolormaps; + NormalLight.Maps = realcolormaps.Maps; } else { - NormalLight.Maps = realcolormaps + NUMCOLORMAPS*256*newblend; + NormalLight.Maps = realcolormaps.Maps + NUMCOLORMAPS*256*newblend; BaseBlendR = BaseBlendG = BaseBlendB = 0; BaseBlendA = 0.f; } } - Renderer->CopyStackedViewParameters(); - Renderer->SetupFrame(player); - validcount++; - if (RenderTarget == screen && r_clearbuffer != 0) + if (r_clearbuffer != 0) { int color; int hom = r_clearbuffer; @@ -939,7 +927,7 @@ void R_SetupFrame (AActor *actor) { color = pr_hom(); } - Renderer->ClearBuffer(color); + Renderer->SetClearColor(color); } } @@ -1075,7 +1063,7 @@ void FCanvasTextureInfo::Serialize(FSerializer &arc) { if (arc.BeginArray("canvastextures")) { - AActor *viewpoint; + AActor *viewpoint = nullptr; int fov; FTextureID picnum; while (arc.BeginObject(nullptr)) diff --git a/src/r_utility.h b/src/r_utility.h index 053af0924c..8a938cc01a 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -12,39 +12,61 @@ class FSerializer; // There a 0-31, i.e. 32 LUT in the COLORMAP lump. #define NUMCOLORMAPS 32 -extern DCanvas *RenderTarget; +struct FRenderViewpoint +{ + FRenderViewpoint(); -extern DVector3 ViewPos; -extern DVector3 ViewActorPos; -extern DAngle ViewAngle; -extern DAngle ViewPitch; -extern DAngle ViewRoll; -extern DVector3 ViewPath[2]; + player_t *player; // For which player is this viewpoint being renderered? (can be null for camera textures) + DVector3 Pos; // Camera position + DVector3 ActorPos; // Camera actor's position + DRotator Angles; // Camera angles + DVector3 Path[2]; // View path for portal calculations + double Cos; // cos(Angles.Yaw) + double Sin; // sin(Angles.Yaw) + double TanCos; // FocalTangent * cos(Angles.Yaw) + double TanSin; // FocalTangent * sin(Angles.Yaw) + + AActor *camera; // camera actor + sector_t *sector; // [RH] keep track of sector viewing from + DAngle FieldOfView; // current field of view + + double TicFrac; // fraction of tic for interpolation + uint32_t FrameTime; // current frame's time in tics. + + int extralight; // extralight to be added to this viewpoint + bool showviewer; // show the camera actor? +}; + +extern FRenderViewpoint r_viewpoint; + +//----------------------------------- +struct FViewWindow +{ + double FocalTangent = 0.0; + int centerx = 0; + int centerxwide = 0; + int centery = 0; + float WidescreenRatio = 0.0f; +}; + +extern FViewWindow r_viewwindow; + +//----------------------------------- -extern "C" int centerx, centerxwide; -extern "C" int centery; extern int setblocks; - -extern double ViewTanCos; -extern double ViewTanSin; -extern double FocalTangent; - extern bool r_NoInterpolate; extern int validcount; extern angle_t LocalViewAngle; // [RH] Added to consoleplayer's angle extern int LocalViewPitch; // [RH] Used directly instead of consoleplayer's pitch extern bool LocalKeyboardTurner; // [RH] The local player used the keyboard to turn, so interpolate -extern float WidescreenRatio; -extern double r_TicFracF; -extern uint32_t r_FrameTime; -extern int extralight; extern unsigned int R_OldBlend; const double r_Yaspect = 200.0; // Why did I make this a variable? It's never set anywhere. + //========================================================================== // // R_PointOnSide @@ -88,22 +110,23 @@ bool R_GetViewInterpolationStatus(); void R_ClearInterpolationPath(); void R_AddInterpolationPoint(const DVector3a &vec); void R_SetViewSize (int blocks); -void R_SetFOV (DAngle fov); -void R_SetupFrame (AActor * camera); -void R_SetViewAngle (); +void R_SetFOV (FRenderViewpoint &viewpoint, DAngle fov); +void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor * camera); +void R_SetViewAngle (FRenderViewpoint &viewpoint, const FViewWindow &viewwindow); // Called by startup code. void R_Init (void); -void R_ExecuteSetViewSize (void); +void R_ExecuteSetViewSize (FRenderViewpoint &viewpoint, FViewWindow &viewwindow); // Called by M_Responder. void R_SetViewSize (int blocks); -void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, bool renderingToCanvas = false); +void R_SetWindow (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, int windowSize, int fullWidth, int fullHeight, int stHeight, bool renderingToCanvas = false); extern void R_FreePastViewers (); extern void R_ClearPastViewer (AActor *actor); +class FCanvasTexture; // This list keeps track of the cameras that draw into canvas textures. struct FCanvasTextureInfo { diff --git a/src/r_walldraw.cpp b/src/r_walldraw.cpp deleted file mode 100644 index c13786bbaf..0000000000 --- a/src/r_walldraw.cpp +++ /dev/null @@ -1,582 +0,0 @@ -/* -** Wall drawing stuff free of Build pollution -** Copyright (c) 2016 Magnus Norddahl -** -** This software is provided 'as-is', without any express or implied -** warranty. In no event will the authors be held liable for any damages -** arising from the use of this software. -** -** Permission is granted to anyone to use this software for any purpose, -** including commercial applications, and to alter it and redistribute it -** freely, subject to the following restrictions: -** -** 1. The origin of this software must not be misrepresented; you must not -** claim that you wrote the original software. If you use this software -** in a product, an acknowledgment in the product documentation would be -** appreciated but is not required. -** 2. Altered source versions must be plainly marked as such, and must not be -** misrepresented as being the original software. -** 3. This notice may not be removed or altered from any source distribution. -** -*/ - -#include -#include - -#include "doomdef.h" -#include "doomstat.h" -#include "doomdata.h" - -#include "r_local.h" -#include "r_sky.h" -#include "v_video.h" - -#include "m_swap.h" -#include "a_sharedglobal.h" -#include "d_net.h" -#include "g_level.h" -#include "r_draw.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "v_palette.h" -#include "r_data/colormaps.h" - -namespace swrenderer -{ - using namespace drawerargs; - - extern FTexture *rw_pic; - extern int wallshade; - -struct WallSampler -{ - WallSampler() { } - WallSampler(int y1, float swal, double yrepeat, fixed_t xoffset, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)); - - uint32_t uv_pos; - uint32_t uv_step; - uint32_t uv_max; - - const BYTE *source; - uint32_t height; -}; - -WallSampler::WallSampler(int y1, float swal, double yrepeat, fixed_t xoffset, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)) -{ - height = texture->GetHeight(); - - int uv_fracbits = 32 - texture->HeightBits; - if (uv_fracbits != 32) - { - uv_max = height << uv_fracbits; - - // Find start uv in [0-base_height[ range. - // Not using xs_ToFixed because it rounds the result and we need something that always rounds down to stay within the range. - double uv_stepd = swal * yrepeat; - double v = (dc_texturemid + uv_stepd * (y1 - CenterY + 0.5)) / height; - v = v - floor(v); - v *= height; - v *= (1 << uv_fracbits); - - uv_pos = (uint32_t)v; - uv_step = xs_ToFixed(uv_fracbits, uv_stepd); - if (uv_step == 0) // To prevent divide by zero elsewhere - uv_step = 1; - } - else - { // Hack for one pixel tall textures - uv_pos = 0; - uv_step = 0; - uv_max = 1; - } - - source = getcol(texture, xoffset >> FRACBITS); -} - -// Draw a column with support for non-power-of-two ranges -static void Draw1Column(int x, int y1, int y2, WallSampler &sampler, void(*draw1column)()) -{ - if (sampler.uv_max == 0 || sampler.uv_step == 0) // power of two - { - int count = y2 - y1; - - dc_source = sampler.source; - dc_dest = (ylookup[y1] + x) + dc_destorg; - dc_count = count; - dc_iscale = sampler.uv_step; - dc_texturefrac = sampler.uv_pos; - draw1column(); - - uint64_t step64 = sampler.uv_step; - uint64_t pos64 = sampler.uv_pos; - sampler.uv_pos = (uint32_t)(pos64 + step64 * count); - } - else - { - uint32_t uv_pos = sampler.uv_pos; - - uint32_t left = y2 - y1; - while (left > 0) - { - uint32_t available = sampler.uv_max - uv_pos; - uint32_t next_uv_wrap = available / sampler.uv_step; - if (available % sampler.uv_step != 0) - next_uv_wrap++; - uint32_t count = MIN(left, next_uv_wrap); - - dc_source = sampler.source; - dc_dest = (ylookup[y1] + x) + dc_destorg; - dc_count = count; - dc_iscale = sampler.uv_step; - dc_texturefrac = uv_pos; - draw1column(); - - left -= count; - uv_pos += sampler.uv_step * count; - if (uv_pos >= sampler.uv_max) - uv_pos -= sampler.uv_max; - } - - sampler.uv_pos = uv_pos; - } -} - -// Draw four columns with support for non-power-of-two ranges -static void Draw4Columns(int x, int y1, int y2, WallSampler *sampler, void(*draw4columns)()) -{ - if (sampler[0].uv_max == 0 || sampler[0].uv_step == 0) // power of two, no wrap handling needed - { - int count = y2 - y1; - for (int i = 0; i < 4; i++) - { - dc_wall_source[i] = sampler[i].source; - dc_wall_texturefrac[i] = sampler[i].uv_pos; - dc_wall_iscale[i] = sampler[i].uv_step; - - uint64_t step64 = sampler[i].uv_step; - uint64_t pos64 = sampler[i].uv_pos; - sampler[i].uv_pos = (uint32_t)(pos64 + step64 * count); - } - dc_dest = (ylookup[y1] + x) + dc_destorg; - dc_count = count; - draw4columns(); - } - else - { - dc_dest = (ylookup[y1] + x) + dc_destorg; - for (int i = 0; i < 4; i++) - { - dc_wall_source[i] = sampler[i].source; - } - - uint32_t left = y2 - y1; - while (left > 0) - { - // Find which column wraps first - uint32_t count = left; - for (int i = 0; i < 4; i++) - { - uint32_t available = sampler[i].uv_max - sampler[i].uv_pos; - uint32_t next_uv_wrap = available / sampler[i].uv_step; - if (available % sampler[i].uv_step != 0) - next_uv_wrap++; - count = MIN(next_uv_wrap, count); - } - - // Draw until that column wraps - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] = sampler[i].uv_pos; - dc_wall_iscale[i] = sampler[i].uv_step; - } - dc_count = count; - draw4columns(); - - // Wrap the uv position - for (int i = 0; i < 4; i++) - { - sampler[i].uv_pos += sampler[i].uv_step * count; - if (sampler[i].uv_pos >= sampler[i].uv_max) - sampler[i].uv_pos -= sampler[i].uv_max; - } - - left -= count; - } - } -} - -typedef void(*DrawColumnFuncPtr)(); - -static void ProcessWallWorker( - int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, - const BYTE *(*getcol)(FTexture *tex, int x), DrawColumnFuncPtr draw1column, DrawColumnFuncPtr draw4columns) -{ - if (rw_pic->UseType == FTexture::TEX_Null) - return; - - fixed_t xoffset = rw_offset; - - int fracbits = 32 - rw_pic->HeightBits; - if (fracbits == 32) - { // Hack for one pixel tall textures - fracbits = 0; - yrepeat = 0; - dc_texturemid = 0; - } - - dc_wall_fracbits = fracbits; - - bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); - if (fixed) - { - dc_wall_colormap[0] = dc_colormap; - dc_wall_colormap[1] = dc_colormap; - dc_wall_colormap[2] = dc_colormap; - dc_wall_colormap[3] = dc_colormap; - } - - if (fixedcolormap) - dc_colormap = fixedcolormap; - else - dc_colormap = basecolormap->Maps; - - float light = rw_light; - - // Calculate where 4 column alignment begins and ends: - int aligned_x1 = clamp((x1 + 3) / 4 * 4, x1, x2); - int aligned_x2 = clamp(x2 / 4 * 4, x1, x2); - - // First unaligned columns: - for (int x = x1; x < aligned_x1; x++, light += rw_lightstep) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); - - WallSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, rw_pic, getcol); - Draw1Column(x, y1, y2, sampler, draw1column); - } - - // The aligned columns - for (int x = aligned_x1; x < aligned_x2; x += 4) - { - // Find y1, y2, light and uv values for four columns: - int y1[4] = { uwal[x], uwal[x + 1], uwal[x + 2], uwal[x + 3] }; - int y2[4] = { dwal[x], dwal[x + 1], dwal[x + 2], dwal[x + 3] }; - - float lights[4]; - for (int i = 0; i < 4; i++) - { - lights[i] = light; - light += rw_lightstep; - } - - WallSampler sampler[4]; - for (int i = 0; i < 4; i++) - sampler[i] = WallSampler(y1[i], swal[x + i], yrepeat, lwal[x + i] + xoffset, rw_pic, getcol); - - // Figure out where we vertically can start and stop drawing 4 columns in one go - int middle_y1 = y1[0]; - int middle_y2 = y2[0]; - for (int i = 1; i < 4; i++) - { - middle_y1 = MAX(y1[i], middle_y1); - middle_y2 = MIN(y2[i], middle_y2); - } - - // If we got an empty column in our set we cannot draw 4 columns in one go: - bool empty_column_in_set = false; - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - empty_column_in_set = true; - } - - if (empty_column_in_set || middle_y2 <= middle_y1) - { - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - continue; - - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); - Draw1Column(x + i, y1[i], y2[i], sampler[i], draw1column); - } - continue; - } - - // Draw the first rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); - - if (y1[i] < middle_y1) - Draw1Column(x + i, y1[i], middle_y1, sampler[i], draw1column); - } - - // Draw the area where all 4 columns are active - if (!fixed) - { - for (int i = 0; i < 4; i++) - { - dc_wall_colormap[i] = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); - } - } - Draw4Columns(x, middle_y1, middle_y2, sampler, draw4columns); - - // Draw the last rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); - - if (middle_y2 < y2[i]) - Draw1Column(x + i, middle_y2, y2[i], sampler[i], draw1column); - } - } - - // The last unaligned columns: - for (int x = aligned_x2; x < x2; x++, light += rw_lightstep) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); - - WallSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, rw_pic, getcol); - Draw1Column(x, y1, y2, sampler, draw1column); - } - - NetUpdate(); -} - -static void ProcessNormalWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) -{ - ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, R_DrawWallCol1, R_DrawWallCol4); -} - -static void ProcessMaskedWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) -{ - if (!rw_pic->bMasked) // Textures that aren't masked can use the faster ProcessNormalWall. - { - ProcessNormalWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - } - else - { - ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, R_DrawWallMaskedCol1, R_DrawWallMaskedCol4); - } -} - -static void ProcessTranslucentWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) -{ - void (*drawcol1)(); - void (*drawcol4)(); - if (!R_GetTransMaskDrawers(&drawcol1, &drawcol4)) - { - // The current translucency is unsupported, so draw with regular ProcessMaskedWall instead. - ProcessMaskedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - } - else - { - ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, drawcol1, drawcol4); - } -} - -static void ProcessStripedWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) -{ - FDynamicColormap *startcolormap = basecolormap; - int startshade = wallshade; - bool fogginess = foggy; - - short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; - short *up, *down; - - up = uwal; - down = most1; - - assert(WallC.sx1 <= x1); - assert(WallC.sx2 >= x2); - - // kg3D - fake floors instead of zdoom light list - for (unsigned int i = 0; i < frontsector->e->XFloor.lightlist.Size(); i++) - { - int j = R_CreateWallSegmentYSloped (most3, frontsector->e->XFloor.lightlist[i].plane, &WallC); - if (j != 3) - { - for (int j = x1; j < x2; ++j) - { - down[j] = clamp (most3[j], up[j], dwal[j]); - } - ProcessNormalWall (x1, x2, up, down, swal, lwal, yrepeat); - up = down; - down = (down == most1) ? most2 : most1; - } - - lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(fogginess, - *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - } - - ProcessNormalWall (x1, x2, up, dwal, swal, lwal, yrepeat); - basecolormap = startcolormap; - wallshade = startshade; -} - -static void ProcessWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, bool mask) -{ - if (mask) - { - if (colfunc == basecolfunc) - { - ProcessMaskedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - else - { - ProcessTranslucentWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - } - else - { - if (fixedcolormap != NULL || fixedlightlev >= 0 || !(frontsector->e && frontsector->e->XFloor.lightlist.Size())) - { - ProcessNormalWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - else - { - ProcessStripedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - } -} - -//============================================================================= -// -// ProcessWallNP2 -// -// This is a wrapper around ProcessWall that helps it tile textures whose heights -// are not powers of 2. It divides the wall into texture-sized strips and calls -// ProcessNormalWall for each of those. Since only one repetition of the texture fits -// in each strip, ProcessWall will not tile. -// -//============================================================================= - -static void ProcessWallNP2(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, double top, double bot, bool mask) -{ - short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; - short *up, *down; - double texheight = rw_pic->GetHeight(); - double partition; - double scaledtexheight = texheight / yrepeat; - - if (yrepeat >= 0) - { // normal orientation: draw strips from top to bottom - partition = top - fmod(top - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); - if (partition == top) - { - partition -= scaledtexheight; - } - up = uwal; - down = most1; - dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; - while (partition > bot) - { - int j = R_CreateWallSegmentY(most3, partition - ViewPos.Z, &WallC); - if (j != 3) - { - for (int j = x1; j < x2; ++j) - { - down[j] = clamp(most3[j], up[j], dwal[j]); - } - ProcessWall(x1, x2, up, down, swal, lwal, yrepeat, mask); - up = down; - down = (down == most1) ? most2 : most1; - } - partition -= scaledtexheight; - dc_texturemid -= texheight; - } - ProcessWall(x1, x2, up, dwal, swal, lwal, yrepeat, mask); - } - else - { // upside down: draw strips from bottom to top - partition = bot - fmod(bot - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); - up = most1; - down = dwal; - dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; - while (partition < top) - { - int j = R_CreateWallSegmentY(most3, partition - ViewPos.Z, &WallC); - if (j != 12) - { - for (int j = x1; j < x2; ++j) - { - up[j] = clamp(most3[j], uwal[j], down[j]); - } - ProcessWall(x1, x2, up, down, swal, lwal, yrepeat, mask); - down = up; - up = (up == most1) ? most2 : most1; - } - partition -= scaledtexheight; - dc_texturemid -= texheight; - } - ProcessWall(x1, x2, uwal, down, swal, lwal, yrepeat, mask); - } -} - -void R_DrawDrawSeg(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) -{ - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - double frontcz1 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v1); - double frontfz1 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v1); - double frontcz2 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v2); - double frontfz2 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v2); - double top = MAX(frontcz1, frontcz2); - double bot = MIN(frontfz1, frontfz2); - if (fake3D & FAKE3D_CLIPTOP) - { - top = MIN(top, sclipTop); - } - if (fake3D & FAKE3D_CLIPBOTTOM) - { - bot = MAX(bot, sclipBottom); - } - ProcessWallNP2(x1, x2, uwal, dwal, swal, lwal, yrepeat, top, bot, true); - } - else - { - ProcessWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, true); - } -} - - -void R_DrawWallSegment(FTexture *rw_pic, int x1, int x2, short *walltop, short *wallbottom, float *swall, fixed_t *lwall, double yscale, double top, double bottom, bool mask) -{ - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - ProcessWallNP2(x1, x2, walltop, wallbottom, swall, lwall, yscale, top, bottom, false); - } - else - { - ProcessWall(x1, x2, walltop, wallbottom, swall, lwall, yscale, false); - } -} - -void R_DrawSkySegment(visplane_t *pl, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - ProcessNormalWall(pl->left, pl->right, uwal, dwal, swal, lwal, yrepeat, getcol); -} - - - -} \ No newline at end of file diff --git a/src/resourcefiles/file_7z.cpp b/src/resourcefiles/file_7z.cpp index 9cc52f6c9d..d6bfa68f65 100644 --- a/src/resourcefiles/file_7z.cpp +++ b/src/resourcefiles/file_7z.cpp @@ -32,10 +32,8 @@ ** ** */ -#ifdef _WIN32 -#define USE_WINDOWS_DWORD -#endif +// Note that 7z made the unwise decision to include windows.h :( #include "7z.h" #include "7zCrc.h" diff --git a/src/resourcefiles/resourcefile.h b/src/resourcefiles/resourcefile.h index a36d6ad490..dbcc1dceee 100644 --- a/src/resourcefiles/resourcefile.h +++ b/src/resourcefiles/resourcefile.h @@ -41,7 +41,7 @@ struct FResourceLump char Name[9]; uint32_t dwName; // These are for accessing the first 4 or 8 chars of - QWORD qwName; // Name as a unit without breaking strict aliasing rules + uint64_t qwName; // Name as a unit without breaking strict aliasing rules }; uint8_t Flags; int8_t RefCount; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 544149ca3a..8e5ac73494 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -491,14 +491,14 @@ void S_PrecacheLevel () actor->MarkPrecacheSounds(); } } - for (auto i : gameinfo.PrecachedSounds) + for (auto snd : gameinfo.PrecachedSounds) { - level.info->PrecacheSounds[i].MarkUsed(); + FSoundID(snd).MarkUsed(); } // Precache all extra sounds requested by this map. - for (i = 0; i < level.info->PrecacheSounds.Size(); ++i) + for (auto snd : level.info->PrecacheSounds) { - level.info->PrecacheSounds[i].MarkUsed(); + FSoundID(snd).MarkUsed(); } // Don't unload sounds that are playing right now. for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) @@ -2353,7 +2353,7 @@ void S_SerializeSounds(FSerializer &arc) for (unsigned int i = chans.Size(); i-- != 0; ) { // Replace start time with sample position. - QWORD start = chans[i]->StartTime.AsOne; + uint64_t start = chans[i]->StartTime.AsOne; chans[i]->StartTime.AsOne = GSnd ? GSnd->GetPosition(chans[i]) : 0; arc(nullptr, *chans[i]); chans[i]->StartTime.AsOne = start; diff --git a/src/s_sound.h b/src/s_sound.h index 541a507010..5bfc790db6 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -23,10 +23,10 @@ #include "doomtype.h" #include "i_soundinternal.h" -#include "dobject.h" class AActor; class FScanner; +class FSerializer; // // SoundFX struct. diff --git a/src/scripting/backend/scopebarrier.h b/src/scripting/backend/scopebarrier.h index da0bc6d1d6..fdcfedfbac 100644 --- a/src/scripting/backend/scopebarrier.h +++ b/src/scripting/backend/scopebarrier.h @@ -2,6 +2,9 @@ #include "zstring.h" +class PClass; +class VMFunction; + // // [ZZ] this really should be in codegen.h, but vmexec needs to access it struct FScopeBarrier diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index a09134d584..c90cc17e84 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -50,7 +50,6 @@ #include "zstring.h" #include "d_event.h" #include "g_levellocals.h" -#include "vm.h" #include "p_checkposition.h" #include "r_sky.h" #include "v_font.h" diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 3a4ffb02cd..8a26a7d3fb 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -844,11 +844,6 @@ struct VMRegisters VMValue *param; }; -struct VMException : public DObject -{ - DECLARE_CLASS(VMException, DObject); -}; - union FVoidObj { DObject *o; diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 900d06ffaa..1b68dc26d2 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -820,7 +820,7 @@ begin: ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); } // Creating actors here must be outright prohibited, - if (cls->IsDescendantOf(RUNTIME_CLASS(AActor))) + if (cls->IsDescendantOf(NAME_Actor)) { ThrowAbortException(X_OTHER, "Cannot create actors with 'new'"); } diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index 81c7ff6d35..63a0984985 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -44,7 +44,7 @@ static const char *BuiltInTypeNames[] = { "sint8", "uint8", "sint16", "uint16", - "sint32", "uint32", + "sint32", "uint32_t", "intauto", "bool", diff --git a/src/serializer.h b/src/serializer.h index 7527eeb3c3..cea8df36d8 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -6,6 +6,7 @@ #include "tarray.h" #include "r_defs.h" #include "resourcefiles/file_zip.h" +#include "tflags.h" extern bool save_full; @@ -14,6 +15,15 @@ struct usercmd_t; struct FWriter; struct FReader; +class PClass; +class PClassActor; +struct FStrifeDialogueNode; +class FFont; +struct FState; +struct FDoorAnimation; +class FSoundID; +struct FPolyObj; +union FRenderStyle; inline bool nullcmp(const void *buffer, size_t length) { @@ -279,10 +289,7 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, PalEntry &pe, P return Serialize(arc, key, pe.d, def? &def->d : nullptr); } -inline FSerializer &Serialize(FSerializer &arc, const char *key, FRenderStyle &style, FRenderStyle *def) -{ - return arc.Array(key, &style.BlendOp, def ? &def->BlendOp : nullptr, 4); -} +FSerializer &Serialize(FSerializer &arc, const char *key, FRenderStyle &style, FRenderStyle *def); template FSerializer &Serialize(FSerializer &arc, const char *key, TFlags &flags, TFlags *def) diff --git a/src/sfmt/SFMT.cpp b/src/sfmt/SFMT.cpp index aaece85754..102497a8c0 100644 --- a/src/sfmt/SFMT.cpp +++ b/src/sfmt/SFMT.cpp @@ -81,10 +81,10 @@ inline static int idxof(int i) { */ #ifdef ONLY64 inline static void rshift128(w128_t *out, w128_t const *in, int shift) { - QWORD th, tl, oh, ol; + uint64_t th, tl, oh, ol; - th = ((QWORD)in->u[2] << 32) | ((QWORD)in->u[3]); - tl = ((QWORD)in->u[0] << 32) | ((QWORD)in->u[1]); + th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]); + tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]); oh = th >> (shift * 8); ol = tl >> (shift * 8); @@ -96,10 +96,10 @@ inline static void rshift128(w128_t *out, w128_t const *in, int shift) { } #else inline static void rshift128(w128_t *out, w128_t const *in, int shift) { - QWORD th, tl, oh, ol; + uint64_t th, tl, oh, ol; - th = ((QWORD)in->u[3] << 32) | ((QWORD)in->u[2]); - tl = ((QWORD)in->u[1] << 32) | ((QWORD)in->u[0]); + th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]); + tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]); oh = th >> (shift * 8); ol = tl >> (shift * 8); @@ -120,10 +120,10 @@ inline static void rshift128(w128_t *out, w128_t const *in, int shift) { */ #ifdef ONLY64 inline static void lshift128(w128_t *out, w128_t const *in, int shift) { - QWORD th, tl, oh, ol; + uint64_t th, tl, oh, ol; - th = ((QWORD)in->u[2] << 32) | ((QWORD)in->u[3]); - tl = ((QWORD)in->u[0] << 32) | ((QWORD)in->u[1]); + th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]); + tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]); oh = th << (shift * 8); ol = tl << (shift * 8); @@ -135,10 +135,10 @@ inline static void lshift128(w128_t *out, w128_t const *in, int shift) { } #else inline static void lshift128(w128_t *out, w128_t const *in, int shift) { - QWORD th, tl, oh, ol; + uint64_t th, tl, oh, ol; - th = ((QWORD)in->u[3] << 32) | ((QWORD)in->u[2]); - tl = ((QWORD)in->u[1] << 32) | ((QWORD)in->u[0]); + th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]); + tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]); oh = th << (shift * 8); ol = tl << (shift * 8); @@ -379,12 +379,12 @@ unsigned int FRandom::GenRand32() * unless an initialization is again executed. * @return 64-bit pseudorandom number */ -QWORD FRandom::GenRand64() +uint64_t FRandom::GenRand64() { #if defined(BIG_ENDIAN64) && !defined(ONLY64) uint32_t r1, r2; #else - QWORD r; + uint64_t r; #endif assert(initialized); @@ -399,7 +399,7 @@ QWORD FRandom::GenRand64() r1 = sfmt.u[idx]; r2 = sfmt.u[idx + 1]; idx += 2; - return ((QWORD)r2 << 32) | r1; + return ((uint64_t)r2 << 32) | r1; #else r = sfmt.u64[idx / 2]; idx += 2; @@ -470,7 +470,7 @@ void FRandom::FillArray32(uint32_t *array, int size) * memory. Mac OSX doesn't have these functions, but \b malloc of OSX * returns the pointer to the aligned memory block. */ -void FRandom::FillArray64(QWORD *array, int size) +void FRandom::FillArray64(uint64_t *array, int size) { assert(initialized); assert(idx == SFMT::N32); diff --git a/src/sfmt/SFMT.h b/src/sfmt/SFMT.h index 72c9901867..ddaabf2461 100644 --- a/src/sfmt/SFMT.h +++ b/src/sfmt/SFMT.h @@ -68,7 +68,7 @@ union w128_t { vector unsigned int s; uint32_t u[4]; - QWORD u64[2]; + uint64_t u64[2]; }; #elif defined(HAVE_SSE2) @@ -78,7 +78,7 @@ union w128_t { union w128_t { __m128i si; uint32_t u[4]; - QWORD u64[2]; + uint64_t u64[2]; }; #else @@ -86,7 +86,7 @@ union w128_t { /** 128-bit data structure */ union w128_t { uint32_t u[4]; - QWORD u64[2]; + uint64_t u64[2]; }; #endif diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index db9f888877..a5972dceb6 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -39,7 +39,6 @@ #include #include extern HWND Window; -#define USE_WINDOWS_DWORD #else #define FALSE 0 #define TRUE 1 @@ -2159,7 +2158,7 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * void FMODSoundRenderer::MarkStartTime(FISoundChannel *chan) { #if FMOD_STUDIO - unsigned long long int dsp_time; + uint64_t dsp_time; ((FMOD::Channel *)chan->SysChannel)->getDSPClock(&dsp_time,NULL); chan->StartTime.Lo = dsp_time & 0xFFFFFFFF; chan->StartTime.Hi = dsp_time >> 32; @@ -2186,7 +2185,7 @@ bool FMODSoundRenderer::HandleChannelDelay(FMOD::Channel *chan, FISoundChannel * // it would be in now if it had never been evicted. QWORD_UNION nowtime; #if FMOD_STUDIO - unsigned long long int delay; + uint64_t delay; chan->getDelay(&delay,NULL,NULL); nowtime.Lo = delay & 0xFFFFFFFF; nowtime.Hi = delay >> 32; @@ -2203,11 +2202,11 @@ bool FMODSoundRenderer::HandleChannelDelay(FMOD::Channel *chan, FISoundChannel * { chan->setPosition(seekpos, FMOD_TIMEUNIT_PCM); } - reuse_chan->StartTime.AsOne = QWORD(nowtime.AsOne - seekpos * OutputRate / freq); + reuse_chan->StartTime.AsOne = uint64_t(nowtime.AsOne - seekpos * OutputRate / freq); } else if (reuse_chan->StartTime.AsOne != 0) { - QWORD difftime = nowtime.AsOne - reuse_chan->StartTime.AsOne; + uint64_t difftime = nowtime.AsOne - reuse_chan->StartTime.AsOne; if (difftime > 0) { // Clamp the position of looping sounds to be within the sound. @@ -2332,7 +2331,7 @@ FISoundChannel *FMODSoundRenderer::CommonChannelSetup(FMOD::Channel *chan, FISou { schan = S_GetChannel(chan); #if FMOD_STUDIO - unsigned long long int time; + uint64_t time; chan->getDelay(&time,NULL,NULL); schan->StartTime.Lo = time & 0xFFFFFFFF; schan->StartTime.Hi = time >> 32; @@ -2701,7 +2700,7 @@ void FMODSoundRenderer::Sync(bool sync) { Sys->lockDSP(); #if FMOD_STUDIO - unsigned long long int clock; + uint64_t clock; SfxGroup->getDSPClock(&clock,NULL); DSPClock.Lo = clock & 0xFFFFFFFF; DSPClock.Hi = clock >> 32; @@ -2726,7 +2725,7 @@ void FMODSoundRenderer::UpdateSounds() // Any sounds played between now and the next call to this function // will start exactly one tic from now. #if FMOD_STUDIO - unsigned long long int clock; + uint64_t clock; SfxGroup->getDSPClock(&clock,NULL); DSPClock.Lo = clock & 0xFFFFFFFF; DSPClock.Hi = clock >> 32; @@ -2743,7 +2742,7 @@ void FMODSoundRenderer::UpdateSounds() // //========================================================================== -std::pair FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend, bool monoize) +std::pair FMODSoundRenderer::LoadSoundRaw(uint8_t *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend, bool monoize) { FMOD_CREATESOUNDEXINFO exinfo; SoundHandle retval = { NULL }; @@ -2825,7 +2824,7 @@ std::pair FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int l // //========================================================================== -std::pair FMODSoundRenderer::LoadSound(BYTE *sfxdata, int length, bool monoize) +std::pair FMODSoundRenderer::LoadSound(uint8_t *sfxdata, int length, bool monoize) { FMOD_CREATESOUNDEXINFO exinfo; SoundHandle retval = { NULL }; @@ -3382,7 +3381,7 @@ short *FMODSoundRenderer::DecodeSample(int outlen, const void *coded, int sizeby sound->release(); if (result == FMOD_ERR_FILE_EOF) { - memset((BYTE *)outbuf + amt_read, 0, len - amt_read); + memset((uint8_t *)outbuf + amt_read, 0, len - amt_read); } else if (result != FMOD_OK || amt_read != len) { diff --git a/src/sound/fmodsound.h b/src/sound/fmodsound.h index 34fdeb7ad7..d80423ebda 100644 --- a/src/sound/fmodsound.h +++ b/src/sound/fmodsound.h @@ -15,8 +15,8 @@ public: void SetSfxVolume (float volume); void SetMusicVolume (float volume); - std::pair LoadSound(BYTE *sfxdata, int length, bool monoize); - std::pair LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1, bool monoize = false); + std::pair LoadSound(uint8_t *sfxdata, int length, bool monoize); + std::pair LoadSoundRaw(uint8_t *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1, bool monoize = false); void UnloadSound (SoundHandle sfx); unsigned int GetMSLength(SoundHandle sfx); unsigned int GetSampleLength(SoundHandle sfx); @@ -71,7 +71,7 @@ public: virtual MIDIDevice* CreateMIDIDevice() const override; private: - DWORD ActiveFMODVersion; + uint32_t ActiveFMODVersion; int SFXPaused; bool InitSuccess; bool DSPLocked; diff --git a/src/sound/i_midi_win32.h b/src/sound/i_midi_win32.h new file mode 100644 index 0000000000..1c7193081e --- /dev/null +++ b/src/sound/i_midi_win32.h @@ -0,0 +1,14 @@ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0400 +#undef _WIN32_WINNT +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif +#include +#include +#else +#define FALSE 0 +#define TRUE 1 +#endif diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 69a27a55c0..bf3ca81de9 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -32,11 +32,7 @@ ** */ -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#else +#ifndef _WIN32 #include #include #include @@ -44,10 +40,7 @@ #include #include #include -#include #include "mus2midi.h" -#define FALSE 0 -#define TRUE 1 extern void ChildSigHandler (int signum); #endif @@ -92,14 +85,14 @@ enum EMIDIType MIDI_MUS }; -extern int MUSHeaderSearch(const BYTE *head, int len); +extern int MUSHeaderSearch(const uint8_t *head, int len); EXTERN_CVAR (Int, snd_samplerate) EXTERN_CVAR (Int, snd_mididevice) static bool MusicDown = true; -static bool ungzip(BYTE *data, int size, TArray &newdata); +static bool ungzip(uint8_t *data, int size, TArray &newdata); MusInfo *currSong; int nomusic = 0; @@ -347,11 +340,11 @@ static MIDIStreamer *CreateMIDIStreamer(FileReader &reader, EMidiDevice devtype, // //========================================================================== -static EMIDIType IdentifyMIDIType(DWORD *id, int size) +static EMIDIType IdentifyMIDIType(uint32_t *id, int size) { // Check for MUS format // Tolerate sloppy wads by searching up to 32 bytes for the header - if (MUSHeaderSearch((BYTE*)id, size) >= 0) + if (MUSHeaderSearch((uint8_t*)id, size) >= 0) { return MIDI_MUS; } @@ -400,7 +393,7 @@ MusInfo *I_RegisterSong (FileReader *reader, MidiDeviceSetting *device) { MusInfo *info = NULL; const char *fmt; - DWORD id[32/4]; + uint32_t id[32/4]; if (nomusic) { @@ -420,7 +413,7 @@ MusInfo *I_RegisterSong (FileReader *reader, MidiDeviceSetting *device) if ((id[0] & MAKE_ID(255, 255, 255, 0)) == GZIP_ID) { int len = reader->GetLength(); - BYTE *gzipped = new BYTE[len]; + uint8_t *gzipped = new uint8_t[len]; if (reader->Read(gzipped, len) != len) { delete[] gzipped; @@ -481,7 +474,7 @@ retry_as_sndsys: else if ( (id[0] == MAKE_ID('R','A','W','A') && id[1] == MAKE_ID('D','A','T','A')) || // Rdos Raw OPL (id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL - (id[0] == MAKE_ID('A','D','L','I') && *((BYTE *)id + 4) == 'B')) // Martin Fernandez's modified IMF + (id[0] == MAKE_ID('A','D','L','I') && *((uint8_t *)id + 4) == 'B')) // Martin Fernandez's modified IMF { info = new OPLMUSSong (*reader, device != NULL? device->args.GetChars() : ""); } @@ -501,7 +494,7 @@ retry_as_sndsys: // Check for CDDA "format" if (id[0] == (('R')|(('I')<<8)|(('F')<<16)|(('F')<<24))) { - DWORD subid; + uint32_t subid; reader->Seek(8, SEEK_CUR); if (reader->Read (&subid, 4) != 4) @@ -590,11 +583,11 @@ MusInfo *I_RegisterURLSong (const char *url) // //========================================================================== -static bool ungzip(BYTE *data, int complen, TArray &newdata) +static bool ungzip(uint8_t *data, int complen, TArray &newdata) { - const BYTE *max = data + complen - 8; - const BYTE *compstart = data + 10; - BYTE flags = data[3]; + const uint8_t *max = data + complen - 8; + const uint8_t *compstart = data + 10; + uint8_t flags = data[3]; unsigned isize; z_stream stream; int err; @@ -628,7 +621,7 @@ static bool ungzip(BYTE *data, int complen, TArray &newdata) } // Decompress - isize = LittleLong(*(DWORD *)(data + complen - 4)); + isize = LittleLong(*(uint32_t *)(data + complen - 4)); newdata.Resize(isize); stream.next_in = (Bytef *)compstart; @@ -827,7 +820,7 @@ CCMD (writemidi) return; } - TArray midi; + TArray midi; FILE *f; bool success; diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index c6bf2c98ce..ba69741a58 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -1,24 +1,4 @@ -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define USE_WINDOWS_DWORD -#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0400 -#undef _WIN32_WINNT -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0400 -#endif -#ifndef USE_WINDOWS_DWORD -#define USE_WINDOWS_DWORD -#endif -#include -#include -#else -#define FALSE 0 -#define TRUE 1 -#endif -#ifdef __APPLE__ -#include -#endif // __APPLE__ + #include "tempfiles.h" #include "oplsynth/opl_mus_player.h" #include "c_cvars.h" @@ -39,67 +19,63 @@ EXTERN_CVAR (Float, timidity_mastervolume) // A device that provides a WinMM-like MIDI streaming interface ------------- -#ifndef _WIN32 -struct MIDIHDR +struct MidiHeader { - BYTE *lpData; - DWORD dwBufferLength; - DWORD dwBytesRecorded; - MIDIHDR *lpNext; + uint8_t *lpData; + uint32_t dwBufferLength; + uint32_t dwBytesRecorded; + MidiHeader *lpNext; }; +// These constants must match the corresponding values of the Windows headers +// to avoid readjustment in the native Windows device's playback functions +// and should not be changed. enum { - MOD_MIDIPORT = 1, - MOD_SYNTH, - MOD_SQSYNTH, - MOD_FMSYNTH, - MOD_MAPPER, - MOD_WAVETABLE, - MOD_SWSYNTH + MIDIDEV_MIDIPORT = 1, + MIDIDEV_SYNTH, + MIDIDEV_SQSYNTH, + MIDIDEV_FMSYNTH, + MIDIDEV_MAPPER, + MIDIDEV_WAVETABLE, + MIDIDEV_SWSYNTH }; -typedef BYTE *LPSTR; +enum : uint8_t +{ + MEVENT_TEMPO = 1, + MEVENT_NOP = 2, + MEVENT_LONGMSG = 128, +}; -#define MEVT_TEMPO ((BYTE)1) -#define MEVT_NOP ((BYTE)2) -#define MEVT_LONGMSG ((BYTE)128) - -#define MEVT_EVENTTYPE(x) ((BYTE)((x) >> 24)) -#define MEVT_EVENTPARM(x) ((x) & 0xffffff) - -#define MOM_DONE 969 -#else -// w32api does not define these -#ifndef MOD_WAVETABLE -#define MOD_WAVETABLE 6 -#define MOD_SWSYNTH 7 -#endif -#endif +#define MEVENT_EVENTTYPE(x) ((uint8_t)((x) >> 24)) +#define MEVENT_EVENTPARM(x) ((x) & 0xffffff) class MIDIStreamer; +typedef void(*MidiCallback)(void *); class MIDIDevice { public: MIDIDevice(); virtual ~MIDIDevice(); - virtual int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) = 0; + virtual int Open(MidiCallback, void *userdata) = 0; virtual void Close() = 0; virtual bool IsOpen() const = 0; virtual int GetTechnology() const = 0; virtual int SetTempo(int tempo) = 0; virtual int SetTimeDiv(int timediv) = 0; - virtual int StreamOut(MIDIHDR *data) = 0; - virtual int StreamOutSync(MIDIHDR *data) = 0; + virtual int StreamOut(MidiHeader *data) = 0; + virtual int StreamOutSync(MidiHeader *data) = 0; virtual int Resume() = 0; virtual void Stop() = 0; - virtual int PrepareHeader(MIDIHDR *data); - virtual int UnprepareHeader(MIDIHDR *data); + virtual int PrepareHeader(MidiHeader *data); + virtual int UnprepareHeader(MidiHeader *data); virtual bool FakeVolume(); virtual bool Pause(bool paused) = 0; - virtual bool NeedThreadedCallback(); + virtual void InitPlayback(); + virtual bool Update(); virtual void PrecacheInstruments(const uint16_t *instruments, int count); virtual void TimidityVolumeChanged(); virtual void FluidSettingInt(const char *setting, int value); @@ -110,81 +86,13 @@ public: virtual FString GetStats(); }; -// WinMM implementation of a MIDI output device ----------------------------- #ifdef _WIN32 -class WinMIDIDevice : public MIDIDevice -{ -public: - WinMIDIDevice(int dev_id); - ~WinMIDIDevice(); - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); - void Close(); - bool IsOpen() const; - int GetTechnology() const; - int SetTempo(int tempo); - int SetTimeDiv(int timediv); - int StreamOut(MIDIHDR *data); - int StreamOutSync(MIDIHDR *data); - int Resume(); - void Stop(); - int PrepareHeader(MIDIHDR *data); - int UnprepareHeader(MIDIHDR *data); - bool FakeVolume(); - bool NeedThreadedCallback(); - bool Pause(bool paused); - void PrecacheInstruments(const uint16_t *instruments, int count); - -protected: - static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD); - - HMIDISTRM MidiOut; - UINT DeviceID; - DWORD SavedVolume; - bool VolumeWorks; - - void (*Callback)(unsigned int, void *, DWORD, DWORD); - void *CallbackData; -}; +MIDIDevice *CreateWinMIDIDevice(int mididevice); +#elif defined __APPLE__ +MIDIDevice *CreateAudioToolboxMIDIDevice(); #endif - -// AudioToolbox implementation of a MIDI output device ---------------------- - -#ifdef __APPLE__ - -class AudioToolboxMIDIDevice : public MIDIDevice -{ -public: - virtual int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userData) override; - virtual void Close() override; - virtual bool IsOpen() const override; - virtual int GetTechnology() const override; - virtual int SetTempo(int tempo) override; - virtual int SetTimeDiv(int timediv) override; - virtual int StreamOut(MIDIHDR *data) override; - virtual int StreamOutSync(MIDIHDR *data) override; - virtual int Resume() override; - virtual void Stop() override; - virtual int PrepareHeader(MIDIHDR* data) override; - virtual bool FakeVolume() override { return true; } - virtual bool Pause(bool paused) override; - virtual bool Preprocess(MIDIStreamer *song, bool looping) override; - -private: - MusicPlayer m_player = nullptr; - MusicSequence m_sequence = nullptr; - AudioUnit m_audioUnit = nullptr; - CFRunLoopTimerRef m_timer = nullptr; - MusicTimeStamp m_length = 0; - - typedef void (*Callback)(unsigned int, void *, DWORD, DWORD); - Callback m_callback = nullptr; - void* m_userData = nullptr; - - static void TimerCallback(CFRunLoopTimerRef timer, void* info); -}; - -#endif // __APPLE__ +MIDIDevice *CreateTimidityPPMIDIDevice(const char *args); // Base class for pseudo-MIDI devices --------------------------------------- @@ -200,8 +108,8 @@ public: bool Pause(bool paused); int Resume(); void Stop(); - int StreamOut(MIDIHDR *data); - int StreamOutSync(MIDIHDR *data); + int StreamOut(MidiHeader *data); + int StreamOutSync(MidiHeader *data); int SetTempo(int tempo); int SetTimeDiv(int timediv); FString GetStats(); @@ -217,50 +125,10 @@ protected: class SndSysMIDIDevice : public PseudoMIDIDevice { public: - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + int Open(MidiCallback, void *userdata); bool Preprocess(MIDIStreamer *song, bool looping); }; -// MIDI file played with TiMidity++ and possibly streamed through the Sound System - -class TimidityPPMIDIDevice : public PseudoMIDIDevice -{ -public: - TimidityPPMIDIDevice(const char *args); - ~TimidityPPMIDIDevice(); - - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); - bool Preprocess(MIDIStreamer *song, bool looping); - bool IsOpen() const; - int Resume(); - - void Stop(); - bool IsOpen(); - void TimidityVolumeChanged(); - -protected: - bool LaunchTimidity(); - - FTempFileName DiskName; -#ifdef _WIN32 - HANDLE ReadWavePipe; - HANDLE WriteWavePipe; - HANDLE ChildProcess; - bool Validated; - bool ValidateTimidity(); -#else // _WIN32 - int WavePipe[2]; - pid_t ChildProcess; -#endif - FString CommandLine; - size_t LoopPos; - - static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); -#ifdef _WIN32 - static const char EventName[]; -#endif -}; - // Base class for software synthesizer MIDI output devices ------------------ class SoftSynthMIDIDevice : public MIDIDevice @@ -274,8 +142,8 @@ public: int GetTechnology() const; int SetTempo(int tempo); int SetTimeDiv(int timediv); - int StreamOut(MIDIHDR *data); - int StreamOutSync(MIDIHDR *data); + int StreamOut(MidiHeader *data); + int StreamOutSync(MidiHeader *data); int Resume(); void Stop(); bool Pause(bool paused); @@ -287,22 +155,22 @@ protected: double Division; double SamplesPerTick; double NextTickIn; - MIDIHDR *Events; + MidiHeader *Events; bool Started; - DWORD Position; + uint32_t Position; int SampleRate; - void (*Callback)(unsigned int, void *, DWORD, DWORD); + MidiCallback Callback; void *CallbackData; virtual void CalcTickRate(); int PlayTick(); - int OpenStream(int chunks, int flags, void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + int OpenStream(int chunks, int flags, MidiCallback, void *userdata); static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); virtual bool ServiceStream (void *buff, int numbytes); virtual void HandleEvent(int status, int parm1, int parm2) = 0; - virtual void HandleLongEvent(const BYTE *data, int len) = 0; + virtual void HandleLongEvent(const uint8_t *data, int len) = 0; virtual void ComputeOutput(float *buffer, int len) = 0; }; @@ -312,7 +180,7 @@ class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock { public: OPLMIDIDevice(const char *args); - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + int Open(MidiCallback, void *userdata); void Close(); int GetTechnology() const; FString GetStats(); @@ -321,7 +189,7 @@ protected: void CalcTickRate(); int PlayTick(); void HandleEvent(int status, int parm1, int parm2); - void HandleLongEvent(const BYTE *data, int len); + void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); bool ServiceStream(void *buff, int numbytes); }; @@ -347,7 +215,7 @@ public: TimidityMIDIDevice(const char *args); ~TimidityMIDIDevice(); - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + int Open(MidiCallback, void *userdata); void PrecacheInstruments(const uint16_t *instruments, int count); FString GetStats(); @@ -355,7 +223,7 @@ protected: Timidity::Renderer *Renderer; void HandleEvent(int status, int parm1, int parm2); - void HandleLongEvent(const BYTE *data, int len); + void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); }; @@ -381,7 +249,7 @@ public: WildMIDIDevice(const char *args); ~WildMIDIDevice(); - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + int Open(MidiCallback, void *userdata); void PrecacheInstruments(const uint16_t *instruments, int count); FString GetStats(); @@ -389,7 +257,7 @@ protected: WildMidi_Renderer *Renderer; void HandleEvent(int status, int parm1, int parm2); - void HandleLongEvent(const BYTE *data, int len); + void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); void WildMidiSetOption(int opt, int set); }; @@ -413,7 +281,7 @@ public: FluidSynthMIDIDevice(const char *args); ~FluidSynthMIDIDevice(); - int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + int Open(MidiCallback, void *userdata); FString GetStats(); void FluidSettingInt(const char *setting, int value); void FluidSettingNum(const char *setting, double value); @@ -421,7 +289,7 @@ public: protected: void HandleEvent(int status, int parm1, int parm2); - void HandleLongEvent(const BYTE *data, int len); + void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); int LoadPatchSets(const char *patches); @@ -489,23 +357,23 @@ public: void FluidSettingNum(const char *setting, double value); void FluidSettingStr(const char *setting, const char *value); void WildMidiSetOption(int opt, int set); - void CreateSMF(TArray &file, int looplimit=0); + void CreateSMF(TArray &file, int looplimit=0); + int ServiceEvent(); protected: MIDIStreamer(const char *dumpname, EMidiDevice type); - void OutputVolume (DWORD volume); - int FillBuffer(int buffer_num, int max_events, DWORD max_time); + void OutputVolume (uint32_t volume); + int FillBuffer(int buffer_num, int max_events, uint32_t max_time); int FillStopBuffer(int buffer_num); - DWORD *WriteStopNotes(DWORD *events); - int ServiceEvent(); + uint32_t *WriteStopNotes(uint32_t *events); int VolumeControllerChange(int channel, int volume); int ClampLoopCount(int loopcount); void SetTempo(int new_tempo); static EMidiDevice SelectMIDIDevice(EMidiDevice devtype); - MIDIDevice *CreateMIDIDevice(EMidiDevice devtype) const; + MIDIDevice *CreateMIDIDevice(EMidiDevice devtype); - static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2); + static void Callback(void *userdata); // Virtuals for subclasses to override virtual void StartPlayback(); @@ -515,7 +383,7 @@ protected: virtual bool CheckDone() = 0; virtual void Precache(); virtual bool SetMIDISubsong(int subsong); - virtual DWORD *MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) = 0; + virtual uint32_t *MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) = 0; enum { @@ -529,29 +397,20 @@ protected: SONG_ERROR }; -#ifdef _WIN32 - static DWORD WINAPI PlayerProc (LPVOID lpParameter); - DWORD PlayerLoop(); - - HANDLE PlayerThread; - HANDLE ExitEvent; - HANDLE BufferDoneEvent; -#endif - MIDIDevice *MIDI; - DWORD Events[2][MAX_EVENTS*3]; - MIDIHDR Buffer[2]; + uint32_t Events[2][MAX_EVENTS*3]; + MidiHeader Buffer[2]; int BufferNum; int EndQueued; bool VolumeChanged; bool Restarting; bool InitialPlayback; - DWORD NewVolume; + uint32_t NewVolume; int Division; int Tempo; int InitialTempo; - BYTE ChannelVolumes[16]; - DWORD Volume; + uint8_t ChannelVolumes[16]; + uint32_t Volume; EMidiDevice DeviceType; bool CallbackIsThreaded; int LoopLimit; @@ -577,11 +436,11 @@ protected: void DoRestart(); bool CheckDone(); void Precache(); - DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time); + uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time); MUSHeader *MusHeader; - BYTE *MusBuffer; - BYTE LastVelocity[16]; + uint8_t *MusBuffer; + uint8_t LastVelocity[16]; size_t MusP, MaxMusP; }; @@ -603,16 +462,16 @@ protected: void DoInitialSetup(); void DoRestart(); bool CheckDone(); - DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time); - void AdvanceTracks(DWORD time); + uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time); + void AdvanceTracks(uint32_t time); struct TrackInfo; void ProcessInitialMetaEvents (); - DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom); + uint32_t *SendCommand (uint32_t *event, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom); TrackInfo *FindNextDue (); - BYTE *MusHeader; + uint8_t *MusHeader; int SongLen; TrackInfo *Tracks; TrackInfo *TrackDue; @@ -625,15 +484,15 @@ protected: struct AutoNoteOff { - DWORD Delay; - BYTE Channel, Key; + uint32_t Delay; + uint8_t Channel, Key; }; // Sorry, std::priority_queue, but I want to be able to modify the contents of the heap. class NoteOffQueue : public TArray { public: - void AddNoteOff(DWORD delay, BYTE channel, BYTE key); - void AdvanceTime(DWORD time); + void AddNoteOff(uint32_t delay, uint8_t channel, uint8_t key); + void AdvanceTime(uint32_t time); bool Pop(AutoNoteOff &item); protected: @@ -663,25 +522,25 @@ protected: void DoInitialSetup(); void DoRestart(); bool CheckDone(); - DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time); - void AdvanceTracks(DWORD time); + uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time); + void AdvanceTracks(uint32_t time); struct TrackInfo; void ProcessInitialMetaEvents (); - DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom); + uint32_t *SendCommand (uint32_t *event, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom); TrackInfo *FindNextDue (); - static DWORD ReadVarLenHMI(TrackInfo *); - static DWORD ReadVarLenHMP(TrackInfo *); + static uint32_t ReadVarLenHMI(TrackInfo *); + static uint32_t ReadVarLenHMP(TrackInfo *); - BYTE *MusHeader; + uint8_t *MusHeader; int SongLen; int NumTracks; TrackInfo *Tracks; TrackInfo *TrackDue; TrackInfo *FakeTrack; - DWORD (*ReadVarLen)(TrackInfo *); + uint32_t (*ReadVarLen)(TrackInfo *); NoteOffQueue NoteOffs; }; @@ -702,20 +561,20 @@ protected: XMISong(const XMISong *original, const char *filename, EMidiDevice type); // file dump constructor - int FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const; - void FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const; + int FindXMIDforms(const uint8_t *chunk, int len, TrackInfo *songs) const; + void FoundXMID(const uint8_t *chunk, int len, TrackInfo *song) const; bool SetMIDISubsong(int subsong); void DoInitialSetup(); void DoRestart(); bool CheckDone(); - DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time); - void AdvanceSong(DWORD time); + uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time); + void AdvanceSong(uint32_t time); void ProcessInitialMetaEvents(); - DWORD *SendCommand (DWORD *event, EventSource track, DWORD delay, ptrdiff_t room, bool &sysex_noroom); + uint32_t *SendCommand (uint32_t *event, EventSource track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom); EventSource FindNextDue(); - BYTE *MusHeader; + uint8_t *MusHeader; int SongLen; // length of the entire file int NumSongs; TrackInfo *Songs; @@ -811,7 +670,7 @@ MusInfo *MOD_OpenSong(FileReader &reader); // Music played via Game Music Emu ------------------------------------------ -const char *GME_CheckFormat(uint32 header); +const char *GME_CheckFormat(uint32_t header); MusInfo *GME_OpenSong(FileReader &reader, const char *fmt); // -------------------------------------------------------------------------- diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index ecca194e68..42e5a8d7dc 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -32,19 +32,6 @@ ** */ -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#include "resource.h" -extern HWND Window; -extern HINSTANCE g_hInst; -#define USE_WINDOWS_DWORD -#else -#define FALSE 0 -#define TRUE 1 -#endif - #include #include #include @@ -52,7 +39,6 @@ extern HINSTANCE g_hInst; #include "doomtype.h" #include -#include "except.h" #include "fmodsound.h" #include "oalsound.h" @@ -125,6 +111,8 @@ CUSTOM_CVAR (Float, snd_sfxvolume, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOIN } } +class MIDIStreamer; + class NullSoundRenderer : public SoundRenderer { public: @@ -135,12 +123,12 @@ public: void SetMusicVolume (float volume) { } - std::pair LoadSound(BYTE *sfxdata, int length, bool monoize) + std::pair LoadSound(uint8_t *sfxdata, int length, bool monoize) { SoundHandle retval = { NULL }; return std::make_pair(retval, true); } - std::pair LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend, bool monoize) + std::pair LoadSoundRaw(uint8_t *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend, bool monoize) { SoundHandle retval = { NULL }; return std::make_pair(retval, true); @@ -461,9 +449,9 @@ FString SoundStream::GetStats() // //========================================================================== -std::pair SoundRenderer::LoadSoundVoc(BYTE *sfxdata, int length, bool monoize) +std::pair SoundRenderer::LoadSoundVoc(uint8_t *sfxdata, int length, bool monoize) { - BYTE * data = NULL; + uint8_t * data = NULL; int len, frequency, channels, bits, loopstart, loopend; len = frequency = channels = bits = 0; loopstart = loopend = -1; @@ -568,7 +556,7 @@ std::pair SoundRenderer::LoadSoundVoc(BYTE *sfxdata, int lengt // Second pass to write the data if (okay) { - data = new BYTE[len]; + data = new uint8_t[len]; i = 26; int j = 0; while (i < length) diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index db97c3555f..cd58c6f645 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -97,9 +97,9 @@ public: virtual void SetSfxVolume (float volume) = 0; virtual void SetMusicVolume (float volume) = 0; // Returns a pair containing a sound handle and a boolean indicating the sound can be used in 3D. - virtual std::pair LoadSound(BYTE *sfxdata, int length, bool monoize=false) = 0; - std::pair LoadSoundVoc(BYTE *sfxdata, int length, bool monoize=false); - virtual std::pair LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1, bool monoize = false) = 0; + virtual std::pair LoadSound(uint8_t *sfxdata, int length, bool monoize=false) = 0; + std::pair LoadSoundVoc(uint8_t *sfxdata, int length, bool monoize=false); + virtual std::pair LoadSoundRaw(uint8_t *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1, bool monoize = false) = 0; virtual void UnloadSound (SoundHandle sfx) = 0; // unloads a sound from memory virtual unsigned int GetMSLength(SoundHandle sfx) = 0; // Gets the length of a sound at its default frequency virtual unsigned int GetSampleLength(SoundHandle sfx) = 0; // Gets the length of a sound at its default frequency diff --git a/src/sound/music_audiotoolbox_mididevice.cpp b/src/sound/mididevices/music_audiotoolbox_mididevice.cpp similarity index 77% rename from src/sound/music_audiotoolbox_mididevice.cpp rename to src/sound/mididevices/music_audiotoolbox_mididevice.cpp index ba48055e59..859fdd31ca 100644 --- a/src/sound/music_audiotoolbox_mididevice.cpp +++ b/src/sound/mididevices/music_audiotoolbox_mididevice.cpp @@ -25,9 +25,45 @@ #ifdef __APPLE__ +#include #include "i_musicinterns.h" #include "templates.h" +// AudioToolbox implementation of a MIDI output device ---------------------- + +class AudioToolboxMIDIDevice : public MIDIDevice +{ +public: + virtual int Open(MidiCallback, void *userData) override; + virtual void Close() override; + virtual bool IsOpen() const override; + virtual int GetTechnology() const override; + virtual int SetTempo(int tempo) override; + virtual int SetTimeDiv(int timediv) override; + virtual int StreamOut(MidiHeader *data) override; + virtual int StreamOutSync(MidiHeader *data) override; + virtual int Resume() override; + virtual void Stop() override; + virtual int PrepareHeader(MidiHeader* data) override; + virtual bool FakeVolume() override { return true; } + virtual bool Pause(bool paused) override; + virtual bool Preprocess(MIDIStreamer *song, bool looping) override; + +private: + MusicPlayer m_player = nullptr; + MusicSequence m_sequence = nullptr; + AudioUnit m_audioUnit = nullptr; + CFRunLoopTimerRef m_timer = nullptr; + MusicTimeStamp m_length = 0; + + MidiCallback m_callback = nullptr; + void* m_userData = nullptr; + + static void TimerCallback(CFRunLoopTimerRef timer, void* info); +}; + + + #define AT_MIDI_CHECK_ERROR(CALL,...) \ { \ const OSStatus result = CALL; \ @@ -40,7 +76,7 @@ } \ } -int AudioToolboxMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userData) +int AudioToolboxMIDIDevice::Open(MidiCallback callback, void *userData) { AT_MIDI_CHECK_ERROR(NewMusicPlayer(&m_player), false); AT_MIDI_CHECK_ERROR(NewMusicSequence(&m_sequence), false); @@ -101,7 +137,7 @@ bool AudioToolboxMIDIDevice::IsOpen() const int AudioToolboxMIDIDevice::GetTechnology() const { - return MOD_SWSYNTH; + return MIDIDEV_SWSYNTH; } int AudioToolboxMIDIDevice::SetTempo(int tempo) @@ -114,12 +150,12 @@ int AudioToolboxMIDIDevice::SetTimeDiv(int timediv) return 0; } -int AudioToolboxMIDIDevice::StreamOut(MIDIHDR* data) +int AudioToolboxMIDIDevice::StreamOut(MidiHeader* data) { return 0; } -int AudioToolboxMIDIDevice::StreamOutSync(MIDIHDR* data) +int AudioToolboxMIDIDevice::StreamOutSync(MidiHeader* data) { return 0; } @@ -177,23 +213,23 @@ void AudioToolboxMIDIDevice::Stop() AT_MIDI_CHECK_ERROR(MusicPlayerStop(m_player)); } -int AudioToolboxMIDIDevice::PrepareHeader(MIDIHDR* data) +int AudioToolboxMIDIDevice::PrepareHeader(MidiHeader* data) { - MIDIHDR* events = data; - DWORD position = 0; + MidiHeader* events = data; + uint32_t position = 0; while (nullptr != events) { - DWORD* const event = reinterpret_cast(events->lpData + position); - const DWORD message = event[2]; + uint32_t* const event = reinterpret_cast(events->lpData + position); + const uint32_t message = event[2]; - if (0 == MEVT_EVENTTYPE(message)) + if (0 == MEVENT_EVENTTYPE(message)) { - static const DWORD VOLUME_CHANGE_EVENT = 7; + static const uint32_t VOLUME_CHANGE_EVENT = 7; - const DWORD status = message & 0xFF; - const DWORD param1 = (message >> 8) & 0x7F; - const DWORD param2 = (message >> 16) & 0x7F; + const uint32_t status = message & 0xFF; + const uint32_t param1 = (message >> 8) & 0x7F; + const uint32_t param2 = (message >> 16) & 0x7F; if (nullptr != m_audioUnit && MIDI_CTRLCHANGE == status && VOLUME_CHANGE_EVENT == param1) { @@ -204,7 +240,7 @@ int AudioToolboxMIDIDevice::PrepareHeader(MIDIHDR* data) // Advance to next event position += 12 + ( (message < 0x80000000) ? 0 - : ((MEVT_EVENTPARM(message) + 3) & ~3) ); + : ((MEVENT_EVENTPARM(message) + 3) & ~3) ); // Did we use up this buffer? if (position >= events->dwBytesRecorded) @@ -257,7 +293,7 @@ bool AudioToolboxMIDIDevice::Preprocess(MIDIStreamer* song, bool looping) { assert(nullptr != song); - TArray midi; + TArray midi; song->CreateSMF(midi, looping ? 0 : 1); CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, &midi[0], midi.Size(), kCFAllocatorNull); @@ -286,7 +322,7 @@ void AudioToolboxMIDIDevice::TimerCallback(CFRunLoopTimerRef timer, void* info) if (nullptr != self->m_callback) { - self->m_callback(MOM_DONE, self->m_userData, 0, 0); + self->m_callback(self->m_userData); } MusicTimeStamp currentTime = 0; @@ -300,4 +336,9 @@ void AudioToolboxMIDIDevice::TimerCallback(CFRunLoopTimerRef timer, void* info) #undef AT_MIDI_CHECK_ERROR +MIDIDevice *CreateAudioToolboxMIDIDevice() +{ + return new AudioToolboxMIDIDevice(); +} + #endif // __APPLE__ diff --git a/src/sound/music_fluidsynth_mididevice.cpp b/src/sound/mididevices/music_fluidsynth_mididevice.cpp similarity index 98% rename from src/sound/music_fluidsynth_mididevice.cpp rename to src/sound/mididevices/music_fluidsynth_mididevice.cpp index bbd6536bc8..711cf7480c 100644 --- a/src/sound/music_fluidsynth_mididevice.cpp +++ b/src/sound/mididevices/music_fluidsynth_mididevice.cpp @@ -37,6 +37,7 @@ // HEADER FILES ------------------------------------------------------------ #include "i_musicinterns.h" +#include "i_system.h" #include "templates.h" #include "doomdef.h" #include "m_swap.h" @@ -49,6 +50,10 @@ #ifdef DYN_FLUIDSYNTH #ifdef _WIN32 + +// do this without including windows.h for this one single prototype +extern "C" unsigned __stdcall GetSystemDirectoryA(char *lpBuffer, unsigned uSize); + #ifndef _M_X64 #define FLUIDSYNTHLIB1 "fluidsynth.dll" #define FLUIDSYNTHLIB2 "libfluidsynth.dll" @@ -317,7 +322,7 @@ FluidSynthMIDIDevice::FluidSynthMIDIDevice(const char *args) #ifdef _WIN32 // On Windows, look for the 4 megabyte patch set installed by Creative's drivers as a default. char sysdir[MAX_PATH+sizeof("\\CT4MGM.SF2")]; - UINT filepart; + uint32_t filepart; if (0 != (filepart = GetSystemDirectoryA(sysdir, MAX_PATH))) { strcat(sysdir, "\\CT4MGM.SF2"); @@ -372,7 +377,7 @@ FluidSynthMIDIDevice::~FluidSynthMIDIDevice() // //========================================================================== -int FluidSynthMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +int FluidSynthMIDIDevice::Open(MidiCallback callback, void *userdata) { if (FluidSynth == NULL) { @@ -438,7 +443,7 @@ void FluidSynthMIDIDevice::HandleEvent(int status, int parm1, int parm2) // //========================================================================== -void FluidSynthMIDIDevice::HandleLongEvent(const BYTE *data, int len) +void FluidSynthMIDIDevice::HandleLongEvent(const uint8_t *data, int len) { if (len > 1 && (data[0] == 0xF0 || data[0] == 0xF7)) { diff --git a/src/oplsynth/music_opl_mididevice.cpp b/src/sound/mididevices/music_opl_mididevice.cpp similarity index 98% rename from src/oplsynth/music_opl_mididevice.cpp rename to src/sound/mididevices/music_opl_mididevice.cpp index f6cfab30ed..e61a5c10d0 100644 --- a/src/oplsynth/music_opl_mididevice.cpp +++ b/src/sound/mididevices/music_opl_mididevice.cpp @@ -95,7 +95,7 @@ OPLMIDIDevice::OPLMIDIDevice(const char *args) // //========================================================================== -int OPLMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +int OPLMIDIDevice::Open(MidiCallback callback, void *userdata) { if (io == NULL || 0 == (NumChips = io->OPLinit(opl_numchips, FullPan, true))) { @@ -131,7 +131,7 @@ void OPLMIDIDevice::Close() int OPLMIDIDevice::GetTechnology() const { - return MOD_FMSYNTH; + return MIDIDEV_FMSYNTH; } //========================================================================== diff --git a/src/oplsynth/music_opldumper_mididevice.cpp b/src/sound/mididevices/music_opldumper_mididevice.cpp similarity index 99% rename from src/oplsynth/music_opldumper_mididevice.cpp rename to src/sound/mididevices/music_opldumper_mididevice.cpp index 9e9110285b..cf7000d4c6 100644 --- a/src/oplsynth/music_opldumper_mididevice.cpp +++ b/src/sound/mididevices/music_opldumper_mididevice.cpp @@ -200,11 +200,11 @@ public: if (File != NULL) { long where_am_i = ftell(File); - DWORD len[2]; + uint32_t len[2]; fseek(File, 12, SEEK_SET); len[0] = LittleLong(CurIntTime); - len[1] = LittleLong(DWORD(where_am_i - 24)); + len[1] = LittleLong(uint32_t(where_am_i - 24)); fwrite(len, 4, 2, File); fclose(File); } diff --git a/src/sound/music_pseudo_mididevice.cpp b/src/sound/mididevices/music_pseudo_mididevice.cpp similarity index 96% rename from src/sound/music_pseudo_mididevice.cpp rename to src/sound/mididevices/music_pseudo_mididevice.cpp index ecfed0da28..f85b3168d0 100644 --- a/src/sound/music_pseudo_mididevice.cpp +++ b/src/sound/mididevices/music_pseudo_mididevice.cpp @@ -117,7 +117,7 @@ bool PseudoMIDIDevice::IsOpen() const int PseudoMIDIDevice::GetTechnology() const { - return MOD_MIDIPORT; + return MIDIDEV_MIDIPORT; } //========================================================================== @@ -177,7 +177,7 @@ bool PseudoMIDIDevice::Pause(bool paused) // //========================================================================== -int PseudoMIDIDevice::StreamOutSync(MIDIHDR *header) +int PseudoMIDIDevice::StreamOutSync(MidiHeader *header) { assert(0); return 0; @@ -189,7 +189,7 @@ int PseudoMIDIDevice::StreamOutSync(MIDIHDR *header) // //========================================================================== -int PseudoMIDIDevice::StreamOut(MIDIHDR *header) +int PseudoMIDIDevice::StreamOut(MidiHeader *header) { assert(0); return 0; @@ -239,7 +239,7 @@ FString PseudoMIDIDevice::GetStats() // //========================================================================== -int SndSysMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +int SndSysMIDIDevice::Open(MidiCallback callback, void *userdata) { return 0; } diff --git a/src/sound/music_softsynth_mididevice.cpp b/src/sound/mididevices/music_softsynth_mididevice.cpp similarity index 93% rename from src/sound/music_softsynth_mididevice.cpp rename to src/sound/mididevices/music_softsynth_mididevice.cpp index 6bde3edf68..ae46e31d3f 100644 --- a/src/sound/music_softsynth_mididevice.cpp +++ b/src/sound/mididevices/music_softsynth_mididevice.cpp @@ -95,7 +95,7 @@ SoftSynthMIDIDevice::~SoftSynthMIDIDevice() // //========================================================================== -int SoftSynthMIDIDevice::OpenStream(int chunks, int flags, void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +int SoftSynthMIDIDevice::OpenStream(int chunks, int flags, MidiCallback callback, void *userdata) { int chunksize = (SampleRate / chunks) * 4; if (!(flags & SoundStream::Mono)) @@ -151,7 +151,7 @@ bool SoftSynthMIDIDevice::IsOpen() const int SoftSynthMIDIDevice::GetTechnology() const { - return MOD_SWSYNTH; + return MIDIDEV_SWSYNTH; } //========================================================================== @@ -238,7 +238,7 @@ void SoftSynthMIDIDevice::Stop() // //========================================================================== -int SoftSynthMIDIDevice::StreamOutSync(MIDIHDR *header) +int SoftSynthMIDIDevice::StreamOutSync(MidiHeader *header) { CritSec.Enter(); StreamOut(header); @@ -255,18 +255,18 @@ int SoftSynthMIDIDevice::StreamOutSync(MIDIHDR *header) // //========================================================================== -int SoftSynthMIDIDevice::StreamOut(MIDIHDR *header) +int SoftSynthMIDIDevice::StreamOut(MidiHeader *header) { header->lpNext = NULL; if (Events == NULL) { Events = header; - NextTickIn = SamplesPerTick * *(DWORD *)header->lpData; + NextTickIn = SamplesPerTick * *(uint32_t *)header->lpData; Position = 0; } else { - MIDIHDR **p; + MidiHeader **p; for (p = &Events; *p != NULL; p = &(*p)->lpNext) { } @@ -302,20 +302,20 @@ bool SoftSynthMIDIDevice::Pause(bool paused) int SoftSynthMIDIDevice::PlayTick() { - DWORD delay = 0; + uint32_t delay = 0; while (delay == 0 && Events != NULL) { - DWORD *event = (DWORD *)(Events->lpData + Position); - if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO) + uint32_t *event = (uint32_t *)(Events->lpData + Position); + if (MEVENT_EVENTTYPE(event[2]) == MEVENT_TEMPO) { - SetTempo(MEVT_EVENTPARM(event[2])); + SetTempo(MEVENT_EVENTPARM(event[2])); } - else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG) + else if (MEVENT_EVENTTYPE(event[2]) == MEVENT_LONGMSG) { - HandleLongEvent((BYTE *)&event[3], MEVT_EVENTPARM(event[2])); + HandleLongEvent((uint8_t *)&event[3], MEVENT_EVENTPARM(event[2])); } - else if (MEVT_EVENTTYPE(event[2]) == 0) + else if (MEVENT_EVENTTYPE(event[2]) == 0) { // Short MIDI event int status = event[2] & 0xff; int parm1 = (event[2] >> 8) & 0x7f; @@ -352,7 +352,7 @@ int SoftSynthMIDIDevice::PlayTick() } else { // Long message - Position += 12 + ((MEVT_EVENTPARM(event[2]) + 3) & ~3); + Position += 12 + ((MEVENT_EVENTPARM(event[2]) + 3) & ~3); } // Did we use up this buffer? @@ -363,7 +363,7 @@ int SoftSynthMIDIDevice::PlayTick() if (Callback != NULL) { - Callback(MOM_DONE, CallbackData, 0, 0); + Callback(CallbackData); } } @@ -373,7 +373,7 @@ int SoftSynthMIDIDevice::PlayTick() return int(Division); } - delay = *(DWORD *)(Events->lpData + Position); + delay = *(uint32_t *)(Events->lpData + Position); } return delay; } diff --git a/src/sound/music_timidity_mididevice.cpp b/src/sound/mididevices/music_timidity_mididevice.cpp similarity index 94% rename from src/sound/music_timidity_mididevice.cpp rename to src/sound/mididevices/music_timidity_mididevice.cpp index 401c34af79..cacddf5426 100644 --- a/src/sound/music_timidity_mididevice.cpp +++ b/src/sound/mididevices/music_timidity_mididevice.cpp @@ -46,24 +46,25 @@ // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- +// MIDI file played with TiMidity++ and possibly streamed through the Sound System struct FmtChunk { - DWORD ChunkID; - DWORD ChunkLen; + uint32_t ChunkID; + uint32_t ChunkLen; uint16_t FormatTag; uint16_t Channels; - DWORD SamplesPerSec; - DWORD AvgBytesPerSec; + uint32_t SamplesPerSec; + uint32_t AvgBytesPerSec; uint16_t BlockAlign; uint16_t BitsPerSample; uint16_t ExtensionSize; uint16_t ValidBitsPerSample; - DWORD ChannelMask; - DWORD SubFormatA; + uint32_t ChannelMask; + uint32_t SubFormatA; uint16_t SubFormatB; uint16_t SubFormatC; - BYTE SubFormatD[8]; + uint8_t SubFormatD[8]; }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -115,7 +116,7 @@ TimidityMIDIDevice::~TimidityMIDIDevice() // //========================================================================== -int TimidityMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +int TimidityMIDIDevice::Open(MidiCallback callback, void *userdata) { int ret = OpenStream(2, 0, callback, userdata); if (ret == 0) @@ -162,7 +163,7 @@ void TimidityMIDIDevice::HandleEvent(int status, int parm1, int parm2) // //========================================================================== -void TimidityMIDIDevice::HandleLongEvent(const BYTE *data, int len) +void TimidityMIDIDevice::HandleLongEvent(const uint8_t *data, int len) { Renderer->HandleLongMessage(data, len); } @@ -250,7 +251,7 @@ TimidityWaveWriterMIDIDevice::TimidityWaveWriterMIDIDevice(const char *filename, File = fopen(filename, "wb"); if (File != NULL) { // Write wave header - DWORD work[3]; + uint32_t work[3]; FmtChunk fmt; work[0] = MAKE_ID('R','I','F','F'); @@ -259,7 +260,7 @@ TimidityWaveWriterMIDIDevice::TimidityWaveWriterMIDIDevice(const char *filename, if (3 != fwrite(work, 4, 3, File)) goto fail; fmt.ChunkID = MAKE_ID('f','m','t',' '); - fmt.ChunkLen = LittleLong(DWORD(sizeof(fmt) - 8)); + fmt.ChunkLen = LittleLong(uint32_t(sizeof(fmt) - 8)); fmt.FormatTag = LittleShort(0xFFFE); // WAVE_FORMAT_EXTENSIBLE fmt.Channels = LittleShort(2); fmt.SamplesPerSec = LittleLong((int)Renderer->rate); @@ -305,15 +306,15 @@ TimidityWaveWriterMIDIDevice::~TimidityWaveWriterMIDIDevice() if (File != NULL) { long pos = ftell(File); - DWORD size; + uint32_t size; // data chunk size - size = LittleLong(DWORD(pos - 8)); + size = LittleLong(uint32_t(pos - 8)); if (0 == fseek(File, 4, SEEK_SET)) { if (1 == fwrite(&size, 4, 1, File)) { - size = LittleLong(DWORD(pos - 12 - sizeof(FmtChunk) - 8)); + size = LittleLong(uint32_t(pos - 12 - sizeof(FmtChunk) - 8)); if (0 == fseek(File, 4 + sizeof(FmtChunk) + 4, SEEK_CUR)) { if (1 == fwrite(&size, 4, 1, File)) diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/mididevices/music_timiditypp_mididevice.cpp similarity index 84% rename from src/sound/music_midi_timidity.cpp rename to src/sound/mididevices/music_timiditypp_mididevice.cpp index ee9d322a40..e2166c0a11 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/mididevices/music_timiditypp_mididevice.cpp @@ -1,3 +1,40 @@ +/* +** music_timiditypp_mididevice.cpp +** Provides access to timidity.exe +** +**--------------------------------------------------------------------------- +** Copyright 2001-2017 Randy Heit +** 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 "i_midi_win32.h" + + #include "i_musicinterns.h" #include "c_cvars.h" #include "cmdlib.h" @@ -20,6 +57,46 @@ void ChildSigHandler (int signum) } #endif +class TimidityPPMIDIDevice : public PseudoMIDIDevice +{ +public: + TimidityPPMIDIDevice(const char *args); + ~TimidityPPMIDIDevice(); + + int Open(MidiCallback, void *userdata); + bool Preprocess(MIDIStreamer *song, bool looping); + bool IsOpen() const; + int Resume(); + + void Stop(); + bool IsOpen(); + void TimidityVolumeChanged(); + +protected: + bool LaunchTimidity(); + + FTempFileName DiskName; +#ifdef _WIN32 + HANDLE ReadWavePipe; + HANDLE WriteWavePipe; + HANDLE ChildProcess; + bool Validated; + bool ValidateTimidity(); +#else // _WIN32 + int WavePipe[2]; + pid_t ChildProcess; +#endif + FString CommandLine; + size_t LoopPos; + + static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); +#ifdef _WIN32 + static const char EventName[]; +#endif +}; + + + #ifdef _WIN32 @@ -140,7 +217,7 @@ TimidityPPMIDIDevice::~TimidityPPMIDIDevice () bool TimidityPPMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) { - TArray midi; + TArray midi; bool success; FILE *f; @@ -178,12 +255,12 @@ bool TimidityPPMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) // //========================================================================== -int TimidityPPMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +int TimidityPPMIDIDevice::Open(MidiCallback callback, void *userdata) { int pipeSize; #ifdef _WIN32 - static SECURITY_ATTRIBUTES inheritable = { sizeof(inheritable), NULL, TRUE }; + static SECURITY_ATTRIBUTES inheritable = { sizeof(inheritable), NULL, true }; if (!Validated && !ValidateTimidity ()) { @@ -287,9 +364,9 @@ bool TimidityPPMIDIDevice::ValidateTimidity() DWORD fileLen; HANDLE diskFile; HANDLE mapping; - const BYTE *exeBase; - const BYTE *exeEnd; - const BYTE *exe; + const uint8_t *exeBase; + const uint8_t *exeEnd; + const uint8_t *exe; bool good; pathLen = SearchPath (NULL, timidity_exe, NULL, MAX_PATH, foundPath, &filePart); @@ -319,7 +396,7 @@ bool TimidityPPMIDIDevice::ValidateTimidity() CloseHandle (diskFile); return false; } - exeBase = (const BYTE *)MapViewOfFile (mapping, FILE_MAP_READ, 0, 0, 0); + exeBase = (const uint8_t *)MapViewOfFile (mapping, FILE_MAP_READ, 0, 0, 0); if (exeBase == NULL) { Printf(PRINT_BOLD, "Could not map %s\n", foundPath); @@ -343,7 +420,7 @@ bool TimidityPPMIDIDevice::ValidateTimidity() good = true; break; } - exe = (const BYTE *)tSpot + 1; + exe = (const uint8_t *)tSpot + 1; } } catch (...) @@ -392,7 +469,7 @@ bool TimidityPPMIDIDevice::LaunchTimidity () startup.lpTitle = TimidityTitle; startup.wShowWindow = SW_SHOWMINNOACTIVE; - if (CreateProcess(NULL, CommandLine.LockBuffer(), NULL, NULL, TRUE, + if (CreateProcess(NULL, CommandLine.LockBuffer(), NULL, NULL, true, DETACHED_PROCESS, NULL, NULL, &startup, &procInfo)) { ChildProcess = procInfo.hProcess; @@ -509,7 +586,7 @@ bool TimidityPPMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, didget = 0; for (;;) { - ReadFile(song->ReadWavePipe, (BYTE *)buff+didget, len-didget, &got, NULL); + ReadFile(song->ReadWavePipe, (uint8_t *)buff+didget, len-didget, &got, NULL); didget += got; if (didget >= (DWORD)len) break; @@ -518,7 +595,7 @@ bool TimidityPPMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, Sleep (10); if (!PeekNamedPipe(song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0) { - memset ((BYTE *)buff+didget, 0, len-didget); + memset ((uint8_t *)buff+didget, 0, len-didget); break; } } @@ -549,10 +626,10 @@ bool TimidityPPMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, } // fprintf(stderr,"something\n"); - got = read(song->WavePipe[0], (BYTE *)buff, len); + got = read(song->WavePipe[0], (uint8_t *)buff, len); if (got < len) { - memset((BYTE *)buff+got, 0, len-got); + memset((uint8_t *)buff+got, 0, len-got); } #endif return true; @@ -728,3 +805,9 @@ BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode) return bSuccess; } #endif + + +MIDIDevice *CreateTimidityPPMIDIDevice(const char *args) +{ + return new TimidityPPMIDIDevice(args); +} diff --git a/src/sound/music_wildmidi_mididevice.cpp b/src/sound/mididevices/music_wildmidi_mididevice.cpp similarity index 97% rename from src/sound/music_wildmidi_mididevice.cpp rename to src/sound/mididevices/music_wildmidi_mididevice.cpp index c664dc6235..c446105a8f 100644 --- a/src/sound/music_wildmidi_mididevice.cpp +++ b/src/sound/mididevices/music_wildmidi_mididevice.cpp @@ -142,7 +142,7 @@ WildMIDIDevice::~WildMIDIDevice() // //========================================================================== -int WildMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +int WildMIDIDevice::Open(MidiCallback callback, void *userdata) { if (Renderer == NULL) { @@ -193,7 +193,7 @@ void WildMIDIDevice::HandleEvent(int status, int parm1, int parm2) // //========================================================================== -void WildMIDIDevice::HandleLongEvent(const BYTE *data, int len) +void WildMIDIDevice::HandleLongEvent(const uint8_t *data, int len) { Renderer->LongEvent(data, len); } diff --git a/src/sound/music_win_mididevice.cpp b/src/sound/mididevices/music_win_mididevice.cpp similarity index 64% rename from src/sound/music_win_mididevice.cpp rename to src/sound/mididevices/music_win_mididevice.cpp index b025d70f06..5682d71acf 100644 --- a/src/sound/music_win_mididevice.cpp +++ b/src/sound/mididevices/music_win_mididevice.cpp @@ -34,6 +34,8 @@ #ifdef _WIN32 +#include "i_midi_win32.h" + // HEADER FILES ------------------------------------------------------------ #include "i_musicinterns.h" @@ -58,6 +60,52 @@ static bool IgnoreMIDIVolume(UINT id); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ +// WinMM implementation of a MIDI output device ----------------------------- + +class WinMIDIDevice : public MIDIDevice +{ +public: + WinMIDIDevice(int dev_id); + ~WinMIDIDevice(); + int Open(MidiCallback, void *userdata); + void Close(); + bool IsOpen() const; + int GetTechnology() const; + int SetTempo(int tempo); + int SetTimeDiv(int timediv); + int StreamOut(MidiHeader *data); + int StreamOutSync(MidiHeader *data); + int Resume(); + void Stop(); + int PrepareHeader(MidiHeader *data); + int UnprepareHeader(MidiHeader *data); + bool FakeVolume(); + bool Pause(bool paused); + void InitPlayback() override; + bool Update() override; + void PrecacheInstruments(const uint16_t *instruments, int count); + DWORD PlayerLoop(); + +//protected: + static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD); + + MIDIStreamer *Streamer; + HMIDISTRM MidiOut; + UINT DeviceID; + DWORD SavedVolume; + MIDIHDR WinMidiHeaders[2]; + int HeaderIndex; + bool VolumeWorks; + + MidiCallback Callback; + void *CallbackData; + + HANDLE BufferDoneEvent; + HANDLE ExitEvent; + HANDLE PlayerThread; + + +}; // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -75,6 +123,20 @@ WinMIDIDevice::WinMIDIDevice(int dev_id) { DeviceID = MAX(dev_id, 0); MidiOut = 0; + HeaderIndex = 0; + memset(WinMidiHeaders, 0, sizeof(WinMidiHeaders)); + + BufferDoneEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (BufferDoneEvent == nullptr) + { + Printf(PRINT_BOLD, "Could not create buffer done event for MIDI playback\n"); + } + ExitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (ExitEvent == nullptr) + { + Printf(PRINT_BOLD, "Could not create exit event for MIDI playback\n"); + } + PlayerThread = nullptr; } //========================================================================== @@ -86,6 +148,15 @@ WinMIDIDevice::WinMIDIDevice(int dev_id) WinMIDIDevice::~WinMIDIDevice() { Close(); + + if (ExitEvent != nullptr) + { + CloseHandle(ExitEvent); + } + if (BufferDoneEvent != nullptr) + { + CloseHandle(BufferDoneEvent); + } } //========================================================================== @@ -94,13 +165,13 @@ WinMIDIDevice::~WinMIDIDevice() // //========================================================================== -int WinMIDIDevice::Open(void (*callback)(UINT, void *, DWORD, DWORD), void *userdata) +int WinMIDIDevice::Open(MidiCallback callback, void *userdata) { MMRESULT err; Callback = callback; CallbackData = userdata; - if (MidiOut == NULL) + if (MidiOut == nullptr) { err = midiStreamOpen(&MidiOut, &DeviceID, 1, (DWORD_PTR)CallbackFunc, (DWORD_PTR)this, CALLBACK_FUNCTION); @@ -132,10 +203,10 @@ int WinMIDIDevice::Open(void (*callback)(UINT, void *, DWORD, DWORD), void *user void WinMIDIDevice::Close() { - if (MidiOut != NULL) + if (MidiOut != nullptr) { midiStreamClose(MidiOut); - MidiOut = NULL; + MidiOut = nullptr; } } @@ -147,7 +218,7 @@ void WinMIDIDevice::Close() bool WinMIDIDevice::IsOpen() const { - return MidiOut != NULL; + return MidiOut != nullptr; } //========================================================================== @@ -191,6 +262,19 @@ int WinMIDIDevice::SetTimeDiv(int timediv) return midiStreamProperty(MidiOut, (LPBYTE)&data, MIDIPROP_SET | MIDIPROP_TIMEDIV); } +//========================================================================== +// +// MIDIStreamer :: PlayerProc Static +// +// Entry point for the player thread. +// +//========================================================================== + +DWORD WINAPI PlayerProc(LPVOID lpParameter) +{ + return ((WinMIDIDevice *)lpParameter)->PlayerLoop(); +} + //========================================================================== // // WinMIDIDevice :: Resume @@ -199,7 +283,31 @@ int WinMIDIDevice::SetTimeDiv(int timediv) int WinMIDIDevice::Resume() { - return midiStreamRestart(MidiOut); + DWORD tid; + int ret = midiStreamRestart(MidiOut); + if (ret == 0) + { + PlayerThread = CreateThread(nullptr, 0, PlayerProc, this, 0, &tid); + if (PlayerThread == nullptr) + { + Printf("Creating MIDI thread failed\n"); + Stop(); + return MMSYSERR_NOTSUPPORTED; + } + } + return ret; +} + +//========================================================================== +// +// WinMIDIDevice :: InitPlayback +// +//========================================================================== + +void WinMIDIDevice::InitPlayback() +{ + ResetEvent(ExitEvent); + ResetEvent(BufferDoneEvent); } //========================================================================== @@ -210,6 +318,14 @@ int WinMIDIDevice::Resume() void WinMIDIDevice::Stop() { + if (PlayerThread != nullptr) + { + SetEvent(ExitEvent); + WaitForSingleObject(PlayerThread, INFINITE); + CloseHandle(PlayerThread); + PlayerThread = nullptr; + } + midiStreamStop(MidiOut); midiOutReset((HMIDIOUT)MidiOut); if (VolumeWorks) @@ -218,6 +334,40 @@ void WinMIDIDevice::Stop() } } +//========================================================================== +// +// MIDIStreamer :: PlayerLoop +// +// Services MIDI playback events. +// +//========================================================================== + +DWORD WinMIDIDevice::PlayerLoop() +{ + HANDLE events[2] = { BufferDoneEvent, ExitEvent }; + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + + for (;;) + { + switch (WaitForMultipleObjects(2, events, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + if (Callback != nullptr) Callback(CallbackData); + break; + + case WAIT_OBJECT_0 + 1: + return 0; + + default: + // Should not happen. + return MMSYSERR_ERROR; + } + } + return 0; +} + + //========================================================================== // // WinMIDIDevice :: PrecacheInstruments @@ -247,7 +397,7 @@ void WinMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count) { return; } - BYTE bank[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + uint8_t bank[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int i, chan; for (i = 0, chan = 0; i < count; ++i) @@ -323,9 +473,11 @@ bool WinMIDIDevice::Pause(bool paused) // //========================================================================== -int WinMIDIDevice::StreamOut(MIDIHDR *header) +int WinMIDIDevice::StreamOut(MidiHeader *header) { - return midiStreamOut(MidiOut, header, sizeof(MIDIHDR)); + auto syshdr = (MIDIHDR*)header->lpNext; + assert(syshdr == &WinMidiHeaders[0] || syshdr == &WinMidiHeaders[1]); + return midiStreamOut(MidiOut, syshdr, sizeof(MIDIHDR)); } //========================================================================== @@ -334,9 +486,9 @@ int WinMIDIDevice::StreamOut(MIDIHDR *header) // //========================================================================== -int WinMIDIDevice::StreamOutSync(MIDIHDR *header) +int WinMIDIDevice::StreamOutSync(MidiHeader *header) { - return midiStreamOut(MidiOut, header, sizeof(MIDIHDR)); + return StreamOut(header); } //========================================================================== @@ -345,9 +497,19 @@ int WinMIDIDevice::StreamOutSync(MIDIHDR *header) // //========================================================================== -int WinMIDIDevice::PrepareHeader(MIDIHDR *header) +int WinMIDIDevice::PrepareHeader(MidiHeader *header) { - return midiOutPrepareHeader((HMIDIOUT)MidiOut, header, sizeof(MIDIHDR)); + // This code depends on the driving implementation only having two buffers that get passed alternatingly. + // If there were more buffers this would require more intelligent handling. + assert(header->lpNext == nullptr); + MIDIHDR *syshdr = &WinMidiHeaders[HeaderIndex ^= 1]; + memset(syshdr, 0, sizeof(MIDIHDR)); + syshdr->lpData = (LPSTR)header->lpData; + syshdr->dwBufferLength = header->dwBufferLength; + syshdr->dwBytesRecorded = header->dwBytesRecorded; + // this device does not use the lpNext pointer to link MIDI events so use it to point to the system data structure. + header->lpNext = (MidiHeader*)syshdr; + return midiOutPrepareHeader((HMIDIOUT)MidiOut, syshdr, sizeof(MIDIHDR)); } //========================================================================== @@ -356,9 +518,19 @@ int WinMIDIDevice::PrepareHeader(MIDIHDR *header) // //========================================================================== -int WinMIDIDevice::UnprepareHeader(MIDIHDR *header) +int WinMIDIDevice::UnprepareHeader(MidiHeader *header) { - return midiOutUnprepareHeader((HMIDIOUT)MidiOut, header, sizeof(MIDIHDR)); + auto syshdr = (MIDIHDR*)header->lpNext; + if (syshdr != nullptr) + { + assert(syshdr == &WinMidiHeaders[0] || syshdr == &WinMidiHeaders[1]); + header->lpNext = nullptr; + return midiOutUnprepareHeader((HMIDIOUT)MidiOut, syshdr, sizeof(MIDIHDR)); + } + else + { + return MMSYSERR_NOERROR; + } } //========================================================================== @@ -377,15 +549,71 @@ bool WinMIDIDevice::FakeVolume() //========================================================================== // -// WinMIDIDevice :: NeedThreadedCallback -// -// When using the MM system, the callback can't yet touch the buffer, so -// the real processing needs to happen in a different thread. +// WinMIDIDevice :: Update // //========================================================================== -bool WinMIDIDevice::NeedThreadedCallback() +bool WinMIDIDevice::Update() { + // If the PlayerThread is signalled, then it's dead. + if (PlayerThread != nullptr && + WaitForSingleObject(PlayerThread, 0) == WAIT_OBJECT_0) + { + static const char *const MMErrorCodes[] = + { + "No error", + "Unspecified error", + "Device ID out of range", + "Driver failed enable", + "Device already allocated", + "Device handle is invalid", + "No device driver present", + "Memory allocation error", + "Function isn't supported", + "Error value out of range", + "Invalid flag passed", + "Invalid parameter passed", + "Handle being used simultaneously on another thread", + "Specified alias not found", + "Bad registry database", + "Registry key not found", + "Registry read error", + "Registry write error", + "Registry delete error", + "Registry value not found", + "Driver does not call DriverCallback", + "More data to be returned", + }; + static const char *const MidiErrorCodes[] = + { + "MIDI header not prepared", + "MIDI still playing something", + "MIDI no configured instruments", + "MIDI hardware is still busy", + "MIDI port no longer connected", + "MIDI invalid MIF", + "MIDI operation unsupported with open mode", + "MIDI through device 'eating' a message", + }; + DWORD code = 0xABADCAFE; + GetExitCodeThread(PlayerThread, &code); + CloseHandle(PlayerThread); + PlayerThread = nullptr; + Printf("MIDI playback failure: "); + if (code < countof(MMErrorCodes)) + { + Printf("%s\n", MMErrorCodes[code]); + } + else if (code >= MIDIERR_BASE && code < MIDIERR_BASE + countof(MidiErrorCodes)) + { + Printf("%s\n", MidiErrorCodes[code - MIDIERR_BASE]); + } + else + { + Printf("%08x\n", code); + } + return false; + } return true; } @@ -398,9 +626,9 @@ bool WinMIDIDevice::NeedThreadedCallback() void CALLBACK WinMIDIDevice::CallbackFunc(HMIDIOUT hOut, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2) { WinMIDIDevice *self = (WinMIDIDevice *)dwInstance; - if (self->Callback != NULL) + if (uMsg == MOM_DONE) { - self->Callback(uMsg, self->CallbackData, dwParam1, dwParam2); + SetEvent(self->BufferDoneEvent); } } @@ -436,9 +664,9 @@ static bool IgnoreMIDIVolume(UINT id) if (MMSYSERR_NOERROR == midiOutGetDevCaps(id, &caps, sizeof(caps))) { - // The Microsoft GS Wavetable Synth advertises itself as MOD_SWSYNTH with a VOLUME control. + // The Microsoft GS Wavetable Synth advertises itself as MIDIDEV_SWSYNTH with a VOLUME control. // If the one we're using doesn't match that, we don't need to bother checking the name. - if (caps.wTechnology == MOD_SWSYNTH && (caps.dwSupport & MIDICAPS_VOLUME)) + if (caps.wTechnology == MIDIDEV_SWSYNTH && (caps.dwSupport & MIDICAPS_VOLUME)) { if (strncmp(caps.szPname, "Microsoft GS", 12) == 0) { @@ -447,9 +675,9 @@ static bool IgnoreMIDIVolume(UINT id) // Now try to create an IMMDeviceEnumerator interface. If it succeeds, // we know we're using the new audio stack introduced with Vista and // should ignore this MIDI device's volume control. - if (SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, + if (SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&enumerator)) - && enumerator != NULL) + && enumerator != nullptr) { enumerator->Release(); return true; @@ -461,4 +689,16 @@ static bool IgnoreMIDIVolume(UINT id) return false; } +MIDIDevice *CreateWinMIDIDevice(int mididevice) +{ + auto d = new WinMIDIDevice(mididevice); + if (d->BufferDoneEvent == nullptr || d->ExitEvent == nullptr) + { + delete d; + return nullptr; + } + return d; +} #endif + + diff --git a/src/sound/mpg123_decoder.cpp b/src/sound/mpg123_decoder.cpp index 0a2ba94f1b..605970bc9b 100644 --- a/src/sound/mpg123_decoder.cpp +++ b/src/sound/mpg123_decoder.cpp @@ -1,7 +1,6 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include -#define USE_WINDOWS_DWORD #endif #include "mpg123_decoder.h" diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 3ad8fafc20..4e3708eb48 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -1,3 +1,6 @@ +#include "i_midi_win32.h" + + #include "i_musicinterns.h" #include "c_dispatch.h" #include "i_music.h" @@ -7,7 +10,7 @@ #include "v_text.h" #include "menu/menu.h" -static DWORD nummididevices; +static uint32_t nummididevices; static bool nummididevicesset; #ifdef HAVE_FLUIDSYNTH @@ -65,7 +68,7 @@ static void MIDIDeviceChanged(int newdev) } #ifdef _WIN32 -UINT mididevice; +unsigned mididevice; CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { @@ -114,7 +117,7 @@ void I_BuildMIDIMenuList (FOptionValues *opt) { AddDefaultMidiDevices(opt); - for (DWORD id = 0; id < nummididevices; ++id) + for (uint32_t id = 0; id < nummididevices; ++id) { MIDIOUTCAPS caps; MMRESULT res; @@ -130,7 +133,7 @@ void I_BuildMIDIMenuList (FOptionValues *opt) } } -static void PrintMidiDevice (int id, const char *name, uint16_t tech, DWORD support) +static void PrintMidiDevice (int id, const char *name, uint16_t tech, uint32_t support) { if (id == snd_mididevice) { @@ -139,13 +142,13 @@ static void PrintMidiDevice (int id, const char *name, uint16_t tech, DWORD supp Printf ("% 2d. %s : ", id, name); switch (tech) { - case MOD_MIDIPORT: Printf ("MIDIPORT"); break; - case MOD_SYNTH: Printf ("SYNTH"); break; - case MOD_SQSYNTH: Printf ("SQSYNTH"); break; - case MOD_FMSYNTH: Printf ("FMSYNTH"); break; - case MOD_MAPPER: Printf ("MAPPER"); break; - case MOD_WAVETABLE: Printf ("WAVETABLE"); break; - case MOD_SWSYNTH: Printf ("SWSYNTH"); break; + case MIDIDEV_MIDIPORT: Printf ("MIDIPORT"); break; + case MIDIDEV_SYNTH: Printf ("SYNTH"); break; + case MIDIDEV_SQSYNTH: Printf ("SQSYNTH"); break; + case MIDIDEV_FMSYNTH: Printf ("FMSYNTH"); break; + case MIDIDEV_MAPPER: Printf ("MAPPER"); break; + case MIDIDEV_WAVETABLE: Printf ("WAVETABLE"); break; + case MIDIDEV_SWSYNTH: Printf ("SWSYNTH"); break; } if (support & MIDICAPS_CACHE) { @@ -172,13 +175,13 @@ CCMD (snd_listmididevices) MIDIOUTCAPS caps; MMRESULT res; - PrintMidiDevice (-6, "WildMidi", MOD_SWSYNTH, 0); + PrintMidiDevice (-6, "WildMidi", MIDIDEV_SWSYNTH, 0); #ifdef HAVE_FLUIDSYNTH - PrintMidiDevice (-5, "FluidSynth", MOD_SWSYNTH, 0); + PrintMidiDevice (-5, "FluidSynth", MIDIDEV_SWSYNTH, 0); #endif - PrintMidiDevice (-4, "Gravis Ultrasound Emulation", MOD_SWSYNTH, 0); - PrintMidiDevice (-3, "Emulated OPL FM Synth", MOD_FMSYNTH, 0); - PrintMidiDevice (-2, "TiMidity++", MOD_SWSYNTH, 0); + PrintMidiDevice (-4, "Gravis Ultrasound Emulation", MIDIDEV_SWSYNTH, 0); + PrintMidiDevice (-3, "Emulated OPL FM Synth", MIDIDEV_FMSYNTH, 0); + PrintMidiDevice (-2, "TiMidity++", MIDIDEV_SWSYNTH, 0); PrintMidiDevice (-1, "Sound System", 0, 0); if (nummididevices != 0) { diff --git a/src/sound/music_stream.cpp b/src/sound/music_stream.cpp deleted file mode 100644 index e0a99f06f4..0000000000 --- a/src/sound/music_stream.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "i_musicinterns.h" - -void StreamSong::Play (bool looping, int subsong) -{ - m_Status = STATE_Stopped; - m_Looping = looping; - - if (m_Stream->Play (m_Looping, 1)) - { - if (subsong != 0) - { - m_Stream->SetOrder (subsong); - } - m_Status = STATE_Playing; - } -} - -void StreamSong::Pause () -{ - if (m_Status == STATE_Playing && m_Stream != NULL) - { - if (m_Stream->SetPaused (true)) - m_Status = STATE_Paused; - } -} - -void StreamSong::Resume () -{ - if (m_Status == STATE_Paused && m_Stream != NULL) - { - if (m_Stream->SetPaused (false)) - m_Status = STATE_Playing; - } -} - -void StreamSong::Stop () -{ - if (m_Status != STATE_Stopped && m_Stream) - { - m_Stream->Stop (); - } - m_Status = STATE_Stopped; -} - -StreamSong::~StreamSong () -{ - Stop (); - if (m_Stream != NULL) - { - delete m_Stream; - m_Stream = NULL; - } -} - -StreamSong::StreamSong (FileReader *reader) -{ - m_Stream = GSnd->OpenStream (reader, SoundStream::Loop); -} - -StreamSong::StreamSong (const char *url) -{ - m_Stream = GSnd->OpenStream (url, SoundStream::Loop); -} - -bool StreamSong::IsPlaying () -{ - if (m_Status != STATE_Stopped) - { - if (m_Stream->IsEnded()) - { - Stop(); - return false; - } - return true; - } - return false; -} - -// -// StreamSong :: SetPosition -// -// Sets the position in ms. - -bool StreamSong::SetPosition(unsigned int pos) -{ - if (m_Stream != NULL) - { - return m_Stream->SetPosition(pos); - } - else - { - return false; - } -} - -bool StreamSong::SetSubsong(int subsong) -{ - if (m_Stream != NULL) - { - return m_Stream->SetOrder(subsong); - } - else - { - return false; - } -} - -FString StreamSong::GetStats() -{ - if (m_Stream != NULL) - { - return m_Stream->GetStats(); - } - return "No song loaded\n"; -} diff --git a/src/sound/music_cd.cpp b/src/sound/musicformats/music_cd.cpp similarity index 100% rename from src/sound/music_cd.cpp rename to src/sound/musicformats/music_cd.cpp diff --git a/src/sound/music_dumb.cpp b/src/sound/musicformats/music_dumb.cpp similarity index 91% rename from src/sound/music_dumb.cpp rename to src/sound/musicformats/music_dumb.cpp index 3c48dac7be..d688d1c2da 100644 --- a/src/sound/music_dumb.cpp +++ b/src/sound/musicformats/music_dumb.cpp @@ -3,20 +3,34 @@ ** Alternative module player, using a modified DUMB for decoding. ** Based on the Foobar2000 component foo_dumb, version 0.9.8.4. ** -** I tried doing this as an FMOD codec, but the built-in codecs -** take precedence over user-created ones, so this never saw any -** of the files. +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** 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. +**--------------------------------------------------------------------------- ** -** Note that if the replayer isn't fast enough to provide data -** for the stream in a timely manner, you'll hear random data at -** those points. I mention this because the debug version of DUMB -** is *MUCH, MUCH, MUCH* slower than the release version. If your -** processor only has a single core, you probably shouldn't even -** use the debug version of DUMB. Unfortunately, when I tried -** linking the debug build of ZDoom against a release build of DUMB, -** there were some files that would not load, even though they -** loaded fine when the DUMB build type matched the ZDoom build type. -** (Applies to Visual C++, GCC should be fine, I think.) */ // HEADER FILES ------------------------------------------------------------ @@ -78,7 +92,7 @@ protected: typedef struct tagITFILEHEADER { - DWORD id; // 0x4D504D49 + uint32_t id; // 0x4D504D49 char songname[26]; uint16_t reserved1; // 0x1004 uint16_t ordnum; @@ -96,8 +110,8 @@ typedef struct tagITFILEHEADER uint8_t sep; uint8_t zero; uint16_t msglength; - DWORD msgoffset; - DWORD reserved2; + uint32_t msgoffset; + uint32_t reserved2; uint8_t chnpan[64]; uint8_t chnvol[64]; } FORCE_PACKED ITFILEHEADER, *PITFILEHEADER; @@ -145,9 +159,9 @@ CUSTOM_CVAR(Float, mod_dumb_mastervolume, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // //========================================================================== -static inline QWORD time_to_samples(double p_time,int p_sample_rate) +static inline uint64_t time_to_samples(double p_time,int p_sample_rate) { - return (QWORD)floor((double)p_sample_rate * p_time + 0.5); + return (uint64_t)floor((double)p_sample_rate * p_time + 0.5); } //========================================================================== @@ -274,16 +288,16 @@ static bool ReadIT(const uint8_t * ptr, unsigned size, input_mod *info, bool met unsigned msgoffset = LittleLong(pifh->msgoffset); unsigned msgend = msgoffset + LittleShort(pifh->msglength); - DWORD * offset; + uint32_t * offset; // FString name; if (meta) { - offset = (DWORD *)(ptr + 0xC0 + LittleShort(pifh->ordnum) + LittleShort(pifh->insnum) * 4); + offset = (uint32_t *)(ptr + 0xC0 + LittleShort(pifh->ordnum) + LittleShort(pifh->insnum) * 4); for (n = 0, l = LittleShort(pifh->smpnum); n < l; n++, offset++) { - DWORD offset_n = LittleLong( *offset ); + uint32_t offset_n = LittleLong( *offset ); if ( offset_n >= msgoffset && offset_n < msgend ) msgend = offset_n; if ((!offset_n) || (offset_n + 0x14 + 26 + 2 >= size)) continue; // XXX @@ -304,11 +318,11 @@ static bool ReadIT(const uint8_t * ptr, unsigned size, input_mod *info, bool met #endif } - offset = (DWORD *)(ptr + 0xC0 + LittleShort(pifh->ordnum)); + offset = (uint32_t *)(ptr + 0xC0 + LittleShort(pifh->ordnum)); for (n = 0, l = LittleShort(pifh->insnum); n < l; n++, offset++) { - DWORD offset_n = LittleLong( *offset ); + uint32_t offset_n = LittleLong( *offset ); if ( offset_n >= msgoffset && offset_n < msgend ) msgend = offset_n; if ((!offset_n) || (offset_n + 0x20 + 26 >= size)) continue; #if 0 @@ -340,9 +354,9 @@ static bool ReadIT(const uint8_t * ptr, unsigned size, input_mod *info, bool met } } - if ((pos + 8 < size) && (*(DWORD *)(ptr + pos) == MAKE_ID('P','N','A','M'))) + if ((pos + 8 < size) && (*(uint32_t *)(ptr + pos) == MAKE_ID('P','N','A','M'))) { - unsigned len = LittleLong(*(DWORD *)(ptr + pos + 4)); + unsigned len = LittleLong(*(uint32_t *)(ptr + pos + 4)); pos += 8; if ((pos + len <= size) && (len <= 240 * 32) && (len >= 32)) { @@ -366,9 +380,9 @@ static bool ReadIT(const uint8_t * ptr, unsigned size, input_mod *info, bool met } } - if ((pos + 8 < size) && (*(DWORD *)(ptr + pos) == MAKE_ID('C','N','A','M'))) + if ((pos + 8 < size) && (*(uint32_t *)(ptr + pos) == MAKE_ID('C','N','A','M'))) { - unsigned len = LittleLong(*(DWORD *)(ptr + pos + 4)); + unsigned len = LittleLong(*(uint32_t *)(ptr + pos + 4)); pos += 8; if ((pos + len <= size) && (len <= 64 * 20) && (len >= 20)) { @@ -393,14 +407,14 @@ static bool ReadIT(const uint8_t * ptr, unsigned size, input_mod *info, bool met } } - offset = (DWORD *)(ptr + 0xC0 + LittleShort(pifh->ordnum) + LittleShort(pifh->insnum) * 4 + LittleShort(pifh->smpnum) * 4); + offset = (uint32_t *)(ptr + 0xC0 + LittleShort(pifh->ordnum) + LittleShort(pifh->insnum) * 4 + LittleShort(pifh->smpnum) * 4); uint8_t chnmask[64]; for (n = 0, l = LittleShort(pifh->patnum); n < l; n++) { memset(chnmask, 0, sizeof(chnmask)); - DWORD offset_n = LittleLong( offset[n] ); + uint32_t offset_n = LittleLong( offset[n] ); if ((!offset_n) || (offset_n + 4 >= size)) continue; unsigned len = LittleShort(*(uint16_t *)(ptr + offset_n)); unsigned rows = LittleShort(*(uint16_t *)(ptr + offset_n + 2)); @@ -759,7 +773,7 @@ MusInfo *MOD_OpenSong(FileReader &reader) union { uint8_t start[64]; - DWORD dstart[64/4]; + uint32_t dstart[64/4]; }; dumbfile_mem_status filestate; DUMBFILE *f = NULL; @@ -845,7 +859,7 @@ MusInfo *MOD_OpenSong(FileReader &reader) /*start_order = 0;*/ } } - else if (size >= 4 && dstart[0] == (DWORD)MAKE_ID('P','S','M',254)) + else if (size >= 4 && dstart[0] == (uint32_t)MAKE_ID('P','S','M',254)) { if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) { @@ -908,7 +922,7 @@ MusInfo *MOD_OpenSong(FileReader &reader) // safe. We'll restrict MOD loading to 31-instrument modules with known // signatures and let the sound system worry about 15-instrument ones. // (Assuming it even supports them) - duh = dumb_read_mod_quick(f, TRUE); + duh = dumb_read_mod_quick(f, true); } if (f != NULL) diff --git a/src/sound/music_gme.cpp b/src/sound/musicformats/music_gme.cpp similarity index 99% rename from src/sound/music_gme.cpp rename to src/sound/musicformats/music_gme.cpp index d00728ec69..88aeabcaba 100644 --- a/src/sound/music_gme.cpp +++ b/src/sound/musicformats/music_gme.cpp @@ -102,7 +102,7 @@ CUSTOM_CVAR(Float, gme_stereodepth, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // //========================================================================== -const char *GME_CheckFormat(uint32 id) +const char *GME_CheckFormat(uint32_t id) { return gme_identify_header(&id); } @@ -117,7 +117,7 @@ MusInfo *GME_OpenSong(FileReader &reader, const char *fmt) { gme_type_t type; gme_err_t err; - BYTE *song; + uint8_t *song; Music_Emu *emu; int sample_rate; @@ -135,7 +135,7 @@ MusInfo *GME_OpenSong(FileReader &reader, const char *fmt) int fpos = reader.Tell(); int len = reader.GetLength(); - song = new BYTE[len]; + song = new uint8_t[len]; if (reader.Read(song, len) != len) { delete[] song; diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/musicformats/music_hmi_midiout.cpp similarity index 93% rename from src/sound/music_hmi_midiout.cpp rename to src/sound/musicformats/music_hmi_midiout.cpp index 5197cd3082..f5322d2c18 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/musicformats/music_hmi_midiout.cpp @@ -89,18 +89,18 @@ struct HMISong::TrackInfo { - const BYTE *TrackBegin; + const uint8_t *TrackBegin; size_t TrackP; size_t MaxTrackP; - DWORD Delay; - DWORD PlayedTime; + uint32_t Delay; + uint32_t PlayedTime; uint16_t Designation[NUM_HMI_DESIGNATIONS]; bool Enabled; bool Finished; - BYTE RunningStatus; + uint8_t RunningStatus; - DWORD ReadVarLenHMI(); - DWORD ReadVarLenHMP(); + uint32_t ReadVarLenHMI(); + uint32_t ReadVarLenHMP(); }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -131,18 +131,12 @@ extern char MIDI_CommonLengths[15]; HMISong::HMISong (FileReader &reader, EMidiDevice type, const char *args) : MIDIStreamer(type, args), MusHeader(0), Tracks(0) { -#ifdef _WIN32 - if (ExitEvent == NULL) - { - return; - } -#endif int len = reader.GetLength(); if (len < 0x100) { // Way too small to be HMI. return; } - MusHeader = new BYTE[len]; + MusHeader = new uint8_t[len]; SongLen = len; NumTracks = 0; if (reader.Read(MusHeader, len) != len) @@ -153,8 +147,8 @@ HMISong::HMISong (FileReader &reader, EMidiDevice type, const char *args) { SetupForHMI(len); } - else if (((DWORD *)MusHeader)[0] == MAKE_ID('H','M','I','M') && - ((DWORD *)MusHeader)[1] == MAKE_ID('I','D','I','P')) + else if (((uint32_t *)MusHeader)[0] == MAKE_ID('H','M','I','M') && + ((uint32_t *)MusHeader)[1] == MAKE_ID('I','D','I','P')) { SetupForHMP(len); } @@ -382,11 +376,11 @@ void HMISong::SetupForHMP(int len) void HMISong::CheckCaps(int tech) { // What's the equivalent HMI device for our technology? - if (tech == MOD_FMSYNTH) + if (tech == MIDIDEV_FMSYNTH) { tech = HMI_DEV_OPL3; } - else if (tech == MOD_MIDIPORT) + else if (tech == MIDIDEV_MIDIPORT) { tech = HMI_DEV_MPU401; } @@ -499,12 +493,12 @@ bool HMISong::CheckDone() // //========================================================================== -DWORD *HMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) +uint32_t *HMISong::MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) { - DWORD *start_events; - DWORD tot_time = 0; - DWORD time = 0; - DWORD delay; + uint32_t *start_events; + uint32_t tot_time = 0; + uint32_t time = 0; + uint32_t delay; start_events = events; while (TrackDue && events < max_event_p && tot_time <= max_time) @@ -524,7 +518,7 @@ DWORD *HMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) do { bool sysex_noroom = false; - DWORD *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom); + uint32_t *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom); if (sysex_noroom) { return events; @@ -552,7 +546,7 @@ DWORD *HMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) // //========================================================================== -void HMISong::AdvanceTracks(DWORD time) +void HMISong::AdvanceTracks(uint32_t time) { for (int i = 0; i <= NumTracks; ++i) { @@ -573,10 +567,10 @@ void HMISong::AdvanceTracks(DWORD time) // //========================================================================== -DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom) +uint32_t *HMISong::SendCommand (uint32_t *events, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom) { - DWORD len; - BYTE event, data1 = 0, data2 = 0; + uint32_t len; + uint8_t event, data1 = 0, data2 = 0; // If the next event comes from the fake track, pop an entry off the note-off queue. if (track == FakeTrack) @@ -601,7 +595,7 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptrdi // Otherwise, we do it at the end of the function. events[0] = delay; events[1] = 0; - events[2] = MEVT_NOP << 24; + events[2] = MEVENT_NOP << 24; if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND && event != 0xFe) { @@ -669,15 +663,15 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptrdi } else { - BYTE *msg = (BYTE *)&events[3]; + uint8_t *msg = (uint8_t *)&events[3]; if (event == MIDI_SYSEX) { // Need to add the SysEx marker to the message. - events[2] = (MEVT_LONGMSG << 24) | (len + 1); + events[2] = (MEVENT_LONGMSG << 24) | (len + 1); *msg++ = MIDI_SYSEX; } else { - events[2] = (MEVT_LONGMSG << 24) | len; + events[2] = (MEVENT_LONGMSG << 24) | len; } memcpy(msg, &track->TrackBegin[track->TrackP], len); msg += len; @@ -712,7 +706,7 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptrdi (track->TrackBegin[track->TrackP+2]); events[0] = delay; events[1] = 0; - events[2] = (MEVT_TEMPO << 24) | Tempo; + events[2] = (MEVENT_TEMPO << 24) | Tempo; break; } track->TrackP += len; @@ -756,11 +750,11 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptrdi track->Delay = ReadVarLen(track); } // Advance events pointer unless this is a non-delaying NOP. - if (events[0] != 0 || MEVT_EVENTTYPE(events[2]) != MEVT_NOP) + if (events[0] != 0 || MEVENT_EVENTTYPE(events[2]) != MEVENT_NOP) { - if (MEVT_EVENTTYPE(events[2]) == MEVT_LONGMSG) + if (MEVENT_EVENTTYPE(events[2]) == MEVENT_LONGMSG) { - events += 3 + ((MEVT_EVENTPARM(events[2]) + 3) >> 2); + events += 3 + ((MEVENT_EVENTPARM(events[2]) + 3) >> 2); } else { @@ -782,8 +776,8 @@ void HMISong::ProcessInitialMetaEvents () { TrackInfo *track; int i; - BYTE event; - DWORD len; + uint8_t event; + uint32_t len; for (i = 0; i < NumTracks; ++i) { @@ -828,7 +822,7 @@ void HMISong::ProcessInitialMetaEvents () // //========================================================================== -DWORD HMISong::ReadVarLenHMI(TrackInfo *track) +uint32_t HMISong::ReadVarLenHMI(TrackInfo *track) { return track->ReadVarLenHMI(); } @@ -839,7 +833,7 @@ DWORD HMISong::ReadVarLenHMI(TrackInfo *track) // //========================================================================== -DWORD HMISong::ReadVarLenHMP(TrackInfo *track) +uint32_t HMISong::ReadVarLenHMP(TrackInfo *track) { return track->ReadVarLenHMP(); } @@ -852,9 +846,9 @@ DWORD HMISong::ReadVarLenHMP(TrackInfo *track) // //========================================================================== -DWORD HMISong::TrackInfo::ReadVarLenHMI() +uint32_t HMISong::TrackInfo::ReadVarLenHMI() { - DWORD time = 0, t = 0x80; + uint32_t time = 0, t = 0x80; while ((t & 0x80) && TrackP < MaxTrackP) { @@ -874,10 +868,10 @@ DWORD HMISong::TrackInfo::ReadVarLenHMI() // //========================================================================== -DWORD HMISong::TrackInfo::ReadVarLenHMP() +uint32_t HMISong::TrackInfo::ReadVarLenHMP() { - DWORD time = 0; - BYTE t = 0; + uint32_t time = 0; + uint8_t t = 0; int off = 0; while (!(t & 0x80) && TrackP < MaxTrackP) @@ -895,7 +889,7 @@ DWORD HMISong::TrackInfo::ReadVarLenHMP() // //========================================================================== -void NoteOffQueue::AddNoteOff(DWORD delay, BYTE channel, BYTE key) +void NoteOffQueue::AddNoteOff(uint32_t delay, uint8_t channel, uint8_t key) { unsigned int i = Reserve(1); while (i > 0 && (*this)[Parent(i)].Delay > delay) @@ -931,7 +925,7 @@ bool NoteOffQueue::Pop(AutoNoteOff &item) // //========================================================================== -void NoteOffQueue::AdvanceTime(DWORD time) +void NoteOffQueue::AdvanceTime(uint32_t time) { // Because the time is decreasing by the same amount for every entry, // the heap property is maintained. @@ -985,7 +979,7 @@ void NoteOffQueue::Heapify() HMISong::TrackInfo *HMISong::FindNextDue () { TrackInfo *track; - DWORD best; + uint32_t best; int i; // Give precedence to whichever track last had events taken from it. @@ -1052,7 +1046,7 @@ HMISong::HMISong(const HMISong *original, const char *filename, EMidiDevice type : MIDIStreamer(filename, type) { SongLen = original->SongLen; - MusHeader = new BYTE[original->SongLen]; + MusHeader = new uint8_t[original->SongLen]; memcpy(MusHeader, original->MusHeader, original->SongLen); NumTracks = original->NumTracks; Division = original->Division; diff --git a/src/sound/music_midistream.cpp b/src/sound/musicformats/music_midistream.cpp similarity index 80% rename from src/sound/music_midistream.cpp rename to src/sound/musicformats/music_midistream.cpp index 0f7501f791..1a47f3c2b8 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/musicformats/music_midistream.cpp @@ -34,6 +34,7 @@ // HEADER FILES ------------------------------------------------------------ + #include "i_musicinterns.h" #include "templates.h" #include "doomdef.h" @@ -53,7 +54,7 @@ // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- -static void WriteVarLen (TArray &file, DWORD value); +static void WriteVarLen (TArray &file, uint32_t value); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- @@ -61,14 +62,14 @@ EXTERN_CVAR(Float, snd_musicvolume) EXTERN_CVAR(Int, snd_mididevice) #ifdef _WIN32 -extern UINT mididevice; +extern unsigned mididevice; #endif extern char MIDI_EventLengths[7]; // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static const BYTE StaticMIDIhead[] = +static const uint8_t StaticMIDIhead[] = { 'M','T','h','d', 0, 0, 0, 6, 0, 0, // format 0: only one track @@ -91,24 +92,9 @@ static const BYTE StaticMIDIhead[] = MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args) : -#ifdef _WIN32 - PlayerThread(0), ExitEvent(0), BufferDoneEvent(0), -#endif MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), Args(args) { -#ifdef _WIN32 - BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (BufferDoneEvent == NULL) - { - Printf(PRINT_BOLD, "Could not create buffer done event for MIDI playback\n"); - } - ExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (ExitEvent == NULL) - { - Printf(PRINT_BOLD, "Could not create exit event for MIDI playback\n"); - return; - } -#endif + memset(Buffer, 0, sizeof(Buffer)); } //========================================================================== @@ -119,15 +105,9 @@ MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args) MIDIStreamer::MIDIStreamer(const char *dumpname, EMidiDevice type) : -#ifdef _WIN32 - PlayerThread(0), ExitEvent(0), BufferDoneEvent(0), -#endif MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), DumpFilename(dumpname) { -#ifdef _WIN32 - BufferDoneEvent = NULL; - ExitEvent = NULL; -#endif + memset(Buffer, 0, sizeof(Buffer)); } //========================================================================== @@ -139,16 +119,6 @@ MIDIStreamer::MIDIStreamer(const char *dumpname, EMidiDevice type) MIDIStreamer::~MIDIStreamer() { Stop(); -#ifdef _WIN32 - if (ExitEvent != NULL) - { - CloseHandle(ExitEvent); - } - if (BufferDoneEvent != NULL) - { - CloseHandle(BufferDoneEvent); - } -#endif if (MIDI != NULL) { delete MIDI; @@ -176,11 +146,7 @@ bool MIDIStreamer::IsMIDI() const bool MIDIStreamer::IsValid() const { -#ifdef _WIN32 - return ExitEvent != NULL && Division != 0; -#else return Division != 0; -#endif } //========================================================================== @@ -256,13 +222,13 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device) // //========================================================================== -MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const +MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) { switch (devtype) { case MDEV_MMAPI: #ifdef _WIN32 - return new WinMIDIDevice(mididevice); + return CreateWinMIDIDevice(mididevice); #endif assert(0); // Intentional fall-through for non-Windows systems. @@ -287,11 +253,11 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const { // The creation of an OPL MIDI device can abort with an error if no GENMIDI lump can be found. Printf("Unable to create OPL MIDI device: %s\nFalling back to Sound System playback", err.GetMessage()); - return new SndSysMIDIDevice; + return GSnd->CreateMIDIDevice(); } case MDEV_TIMIDITY: - return new TimidityPPMIDIDevice(Args); + return CreateTimidityPPMIDIDevice(Args); case MDEV_WILDMIDI: return new WildMIDIDevice(Args); @@ -309,7 +275,6 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const void MIDIStreamer::Play(bool looping, int subsong) { - DWORD tid; EMidiDevice devtype; m_Status = STATE_Stopped; @@ -337,10 +302,6 @@ void MIDIStreamer::Play(bool looping, int subsong) MIDI = CreateMIDIDevice(devtype); } -#ifndef _WIN32 - assert(MIDI == NULL || MIDI->NeedThreadedCallback() == false); -#endif - if (MIDI == NULL || 0 != MIDI->Open(Callback, this)) { Printf(PRINT_BOLD, "Could not open MIDI out device\n"); @@ -371,25 +332,7 @@ void MIDIStreamer::Play(bool looping, int subsong) } else { -#ifdef _WIN32 - if (MIDI->NeedThreadedCallback()) - { - PlayerThread = CreateThread(NULL, 0, PlayerProc, this, 0, &tid); - if (PlayerThread == NULL) - { - Printf ("Creating MIDI thread failed\n"); - Stop(); - } - else - { - m_Status = STATE_Playing; - } - } - else -#endif - { - m_Status = STATE_Playing; - } + m_Status = STATE_Playing; } } @@ -416,10 +359,7 @@ void MIDIStreamer::StartPlayback() MusicVolumeChanged(); // set volume to current music's properties OutputVolume(Volume); -#ifdef _WIN32 - ResetEvent(ExitEvent); - ResetEvent(BufferDoneEvent); -#endif + MIDI->InitPlayback(); // Fill the initial buffers for the song. BufferNum = 0; @@ -505,15 +445,7 @@ void MIDIStreamer::Resume() void MIDIStreamer::Stop() { EndQueued = 4; -#ifdef _WIN32 - if (PlayerThread != NULL) - { - SetEvent(ExitEvent); - WaitForSingleObject(PlayerThread, INFINITE); - CloseHandle(PlayerThread); - PlayerThread = NULL; - } -#endif + if (MIDI != NULL && MIDI->IsOpen()) { MIDI->Stop(); @@ -562,7 +494,7 @@ void MIDIStreamer::MusicVolumeChanged() if (MIDI != NULL && MIDI->FakeVolume()) { float realvolume = clamp(snd_musicvolume * relative_volume, 0.f, 1.f); - Volume = clamp((DWORD)(realvolume * 65535.f), 0, 65535); + Volume = clamp((uint32_t)(realvolume * 65535.f), 0, 65535); } else { @@ -654,7 +586,7 @@ void MIDIStreamer::WildMidiSetOption(int opt, int set) // //========================================================================== -void MIDIStreamer::OutputVolume (DWORD volume) +void MIDIStreamer::OutputVolume (uint32_t volume) { if (MIDI != NULL && MIDI->FakeVolume()) { @@ -685,13 +617,9 @@ int MIDIStreamer::VolumeControllerChange(int channel, int volume) // // MIDIStreamer :: Callback Static // -// Signals the BufferDoneEvent to prepare the next buffer. The buffer is not -// prepared in the callback directly, because it's generally still in use by -// the MIDI streamer when this callback is executed. -// //========================================================================== -void MIDIStreamer::Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2) +void MIDIStreamer::Callback(void *userdata) { MIDIStreamer *self = (MIDIStreamer *)userdata; @@ -699,19 +627,7 @@ void MIDIStreamer::Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, D { return; } - if (uMsg == MOM_DONE) - { -#ifdef _WIN32 - if (self->PlayerThread != NULL) - { - SetEvent(self->BufferDoneEvent); - } - else -#endif - { - self->ServiceEvent(); - } - } + self->ServiceEvent(); } //========================================================================== @@ -725,122 +641,9 @@ void MIDIStreamer::Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, D void MIDIStreamer::Update() { -#ifdef _WIN32 - // If the PlayerThread is signalled, then it's dead. - if (PlayerThread != NULL && - WaitForSingleObject(PlayerThread, 0) == WAIT_OBJECT_0) - { - static const char *const MMErrorCodes[] = - { - "No error", - "Unspecified error", - "Device ID out of range", - "Driver failed enable", - "Device already allocated", - "Device handle is invalid", - "No device driver present", - "Memory allocation error", - "Function isn't supported", - "Error value out of range", - "Invalid flag passed", - "Invalid parameter passed", - "Handle being used simultaneously on another thread", - "Specified alias not found", - "Bad registry database", - "Registry key not found", - "Registry read error", - "Registry write error", - "Registry delete error", - "Registry value not found", - "Driver does not call DriverCallback", - "More data to be returned", - }; - static const char *const MidiErrorCodes[] = - { - "MIDI header not prepared", - "MIDI still playing something", - "MIDI no configured instruments", - "MIDI hardware is still busy", - "MIDI port no longer connected", - "MIDI invalid MIF", - "MIDI operation unsupported with open mode", - "MIDI through device 'eating' a message", - }; - DWORD code = 0xABADCAFE; - GetExitCodeThread(PlayerThread, &code); - CloseHandle(PlayerThread); - PlayerThread = NULL; - Printf ("MIDI playback failure: "); - if (code < countof(MMErrorCodes)) - { - Printf("%s\n", MMErrorCodes[code]); - } - else if (code >= MIDIERR_BASE && code < MIDIERR_BASE + countof(MidiErrorCodes)) - { - Printf("%s\n", MidiErrorCodes[code - MIDIERR_BASE]); - } - else - { - Printf("%08x\n", code); - } - Stop(); - } -#endif + if (!MIDI->Update()) Stop(); } -//========================================================================== -// -// MIDIStreamer :: PlayerProc Static -// -// Entry point for the player thread. -// -//========================================================================== - -#ifdef _WIN32 -DWORD WINAPI MIDIStreamer::PlayerProc (LPVOID lpParameter) -{ - return ((MIDIStreamer *)lpParameter)->PlayerLoop(); -} -#endif - -//========================================================================== -// -// MIDIStreamer :: PlayerLoop -// -// Services MIDI playback events. -// -//========================================================================== - -#ifdef _WIN32 -DWORD MIDIStreamer::PlayerLoop() -{ - HANDLE events[2] = { BufferDoneEvent, ExitEvent }; - int res; - - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - - for (;;) - { - switch (WaitForMultipleObjects(2, events, FALSE, INFINITE)) - { - case WAIT_OBJECT_0: - if (0 != (res = ServiceEvent())) - { - return res; - } - break; - - case WAIT_OBJECT_0 + 1: - return 0; - - default: - // Should not happen. - return MMSYSERR_ERROR; - } - } -} -#endif - //========================================================================== // // MIDIStreamer :: ServiceEvent @@ -879,8 +682,8 @@ fill: switch (res & 3) { case SONG_MORE: - if ((MIDI->NeedThreadedCallback() && 0 != (res = MIDI->StreamOutSync(&Buffer[BufferNum]))) || - (!MIDI->NeedThreadedCallback() && 0 != (res = MIDI->StreamOut(&Buffer[BufferNum])))) + res = MIDI->StreamOut(&Buffer[BufferNum]); + if (res != 0) { return res; } @@ -921,7 +724,7 @@ fill: // //========================================================================== -int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time) +int MIDIStreamer::FillBuffer(int buffer_num, int max_events, uint32_t max_time) { if (!Restarting && CheckDone()) { @@ -929,9 +732,9 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time) } int i; - DWORD *events = Events[buffer_num], *max_event_p; - DWORD tot_time = 0; - DWORD time = 0; + uint32_t *events = Events[buffer_num], *max_event_p; + uint32_t tot_time = 0; + uint32_t time = 0; // The final event is for a NOP to hold the delay from the last event. max_event_p = events + (max_events - 1) * 3; @@ -942,7 +745,7 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time) // Send the full master volume SysEx message. events[0] = 0; // dwDeltaTime events[1] = 0; // dwStreamID - events[2] = (MEVT_LONGMSG << 24) | 8; // dwEvent + events[2] = (MEVENT_LONGMSG << 24) | 8; // dwEvent events[3] = MAKE_ID(0xf0,0x7f,0x7f,0x04); // dwParms[0] events[4] = MAKE_ID(0x01,0x7f,0x7f,0xf7); // dwParms[1] events += 5; @@ -955,7 +758,7 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time) VolumeChanged = false; for (i = 0; i < 16; ++i) { - BYTE courseVol = (BYTE)(((ChannelVolumes[i]+1) * NewVolume) >> 16); + uint8_t courseVol = (uint8_t)(((ChannelVolumes[i]+1) * NewVolume) >> 16); events[0] = 0; // dwDeltaTime events[1] = 0; // dwStreamID events[2] = MIDI_CTRLCHANGE | i | (7<<8) | (courseVol<<16); @@ -968,9 +771,9 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time) { // Be more responsive when unpausing by only playing each buffer // for a third of the maximum time. - events[0] = MAX(1, (max_time / 3) * Division / Tempo); + events[0] = MAX(1, (max_time / 3) * Division / Tempo); events[1] = 0; - events[2] = MEVT_NOP << 24; + events[2] = MEVENT_NOP << 24; events += 3; } else @@ -981,7 +784,7 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time) // Reset the tempo to the inital value. events[0] = 0; // dwDeltaTime events[1] = 0; // dwStreamID - events[2] = (MEVT_TEMPO << 24) | InitialTempo; // dwEvent + events[2] = (MEVENT_TEMPO << 24) | InitialTempo; // dwEvent events += 3; // Stop all notes in case any were left hanging. events = WriteStopNotes(events); @@ -989,9 +792,9 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time) } events = MakeEvents(events, max_event_p, max_time); } - memset(&Buffer[buffer_num], 0, sizeof(MIDIHDR)); - Buffer[buffer_num].lpData = (LPSTR)Events[buffer_num]; - Buffer[buffer_num].dwBufferLength = DWORD((LPSTR)events - Buffer[buffer_num].lpData); + memset(&Buffer[buffer_num], 0, sizeof(MidiHeader)); + Buffer[buffer_num].lpData = (uint8_t *)Events[buffer_num]; + Buffer[buffer_num].dwBufferLength = uint32_t((uint8_t *)events - Buffer[buffer_num].lpData); Buffer[buffer_num].dwBytesRecorded = Buffer[buffer_num].dwBufferLength; if (0 != (i = MIDI->PrepareHeader(&Buffer[buffer_num]))) { @@ -1010,7 +813,7 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time) int MIDIStreamer::FillStopBuffer(int buffer_num) { - DWORD *events = Events[buffer_num]; + uint32_t *events = Events[buffer_num]; int i; events = WriteStopNotes(events); @@ -1018,12 +821,12 @@ int MIDIStreamer::FillStopBuffer(int buffer_num) // wait some tics, just so that this buffer takes some time events[0] = 500; events[1] = 0; - events[2] = MEVT_NOP << 24; + events[2] = MEVENT_NOP << 24; events += 3; - memset(&Buffer[buffer_num], 0, sizeof(MIDIHDR)); - Buffer[buffer_num].lpData = (LPSTR)Events[buffer_num]; - Buffer[buffer_num].dwBufferLength = DWORD((LPSTR)events - Buffer[buffer_num].lpData); + memset(&Buffer[buffer_num], 0, sizeof(MidiHeader)); + Buffer[buffer_num].lpData = (uint8_t*)Events[buffer_num]; + Buffer[buffer_num].dwBufferLength = uint32_t((uint8_t*)events - Buffer[buffer_num].lpData); Buffer[buffer_num].dwBytesRecorded = Buffer[buffer_num].dwBufferLength; if (0 != (i = MIDI->PrepareHeader(&Buffer[buffer_num]))) { @@ -1041,7 +844,7 @@ int MIDIStreamer::FillStopBuffer(int buffer_num) // //========================================================================== -DWORD *MIDIStreamer::WriteStopNotes(DWORD *events) +uint32_t *MIDIStreamer::WriteStopNotes(uint32_t *events) { for (int i = 0; i < 16; ++i) { @@ -1069,8 +872,8 @@ DWORD *MIDIStreamer::WriteStopNotes(DWORD *events) void MIDIStreamer::Precache() { - BYTE found_instruments[256] = { 0, }; - BYTE found_banks[256] = { 0, }; + uint8_t found_instruments[256] = { 0, }; + uint8_t found_banks[256] = { 0, }; bool multiple_banks = false; LoopLimit = 1; @@ -1081,10 +884,10 @@ void MIDIStreamer::Precache() // Simulate playback to pick out used instruments. while (!CheckDone()) { - DWORD *event_end = MakeEvents(Events[0], &Events[0][MAX_EVENTS*3], 1000000*600); - for (DWORD *event = Events[0]; event < event_end; ) + uint32_t *event_end = MakeEvents(Events[0], &Events[0][MAX_EVENTS*3], 1000000*600); + for (uint32_t *event = Events[0]; event < event_end; ) { - if (MEVT_EVENTTYPE(event[2]) == 0) + if (MEVENT_EVENTTYPE(event[2]) == 0) { int command = (event[2] & 0x70); int channel = (event[2] & 0x0f); @@ -1124,7 +927,7 @@ void MIDIStreamer::Precache() } else { // long message - event += 3 + ((MEVT_EVENTPARM(event[2]) + 3) >> 2); + event += 3 + ((MEVENT_EVENTPARM(event[2]) + 3) >> 2); } } } @@ -1167,13 +970,13 @@ void MIDIStreamer::Precache() // //========================================================================== -void MIDIStreamer::CreateSMF(TArray &file, int looplimit) +void MIDIStreamer::CreateSMF(TArray &file, int looplimit) { - DWORD delay = 0; - BYTE running_status = 255; + uint32_t delay = 0; + uint8_t running_status = 255; // Always create songs aimed at GM devices. - CheckCaps(MOD_MIDIPORT); + CheckCaps(MIDIDEV_MIDIPORT); LoopLimit = looplimit <= 0 ? EXPORT_LOOP_LIMIT : looplimit; DoRestart(); Tempo = InitialTempo; @@ -1188,29 +991,29 @@ void MIDIStreamer::CreateSMF(TArray &file, int looplimit) while (!CheckDone()) { - DWORD *event_end = MakeEvents(Events[0], &Events[0][MAX_EVENTS*3], 1000000*600); - for (DWORD *event = Events[0]; event < event_end; ) + uint32_t *event_end = MakeEvents(Events[0], &Events[0][MAX_EVENTS*3], 1000000*600); + for (uint32_t *event = Events[0]; event < event_end; ) { delay += event[0]; - if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO) + if (MEVENT_EVENTTYPE(event[2]) == MEVENT_TEMPO) { WriteVarLen(file, delay); delay = 0; - DWORD tempo = MEVT_EVENTPARM(event[2]); + uint32_t tempo = MEVENT_EVENTPARM(event[2]); file.Push(MIDI_META); file.Push(MIDI_META_TEMPO); file.Push(3); - file.Push(BYTE(tempo >> 16)); - file.Push(BYTE(tempo >> 8)); - file.Push(BYTE(tempo)); + file.Push(uint8_t(tempo >> 16)); + file.Push(uint8_t(tempo >> 8)); + file.Push(uint8_t(tempo)); running_status = 255; } - else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG) + else if (MEVENT_EVENTTYPE(event[2]) == MEVENT_LONGMSG) { WriteVarLen(file, delay); delay = 0; - DWORD len = MEVT_EVENTPARM(event[2]); - BYTE *bytes = (BYTE *)&event[3]; + uint32_t len = MEVENT_EVENTPARM(event[2]); + uint8_t *bytes = (uint8_t *)&event[3]; if (bytes[0] == MIDI_SYSEX) { len--; @@ -1226,20 +1029,20 @@ void MIDIStreamer::CreateSMF(TArray &file, int looplimit) } running_status = 255; } - else if (MEVT_EVENTTYPE(event[2]) == 0) + else if (MEVENT_EVENTTYPE(event[2]) == 0) { WriteVarLen(file, delay); delay = 0; - BYTE status = BYTE(event[2]); + uint8_t status = uint8_t(event[2]); if (status != running_status) { running_status = status; file.Push(status); } - file.Push(BYTE((event[2] >> 8) & 0x7F)); + file.Push(uint8_t((event[2] >> 8) & 0x7F)); if (MIDI_EventLengths[(status >> 4) & 7] == 2) { - file.Push(BYTE((event[2] >> 16) & 0x7F)); + file.Push(uint8_t((event[2] >> 16) & 0x7F)); } } // Advance to next event @@ -1249,7 +1052,7 @@ void MIDIStreamer::CreateSMF(TArray &file, int looplimit) } else { // long message - event += 3 + ((MEVT_EVENTPARM(event[2]) + 3) >> 2); + event += 3 + ((MEVENT_EVENTPARM(event[2]) + 3) >> 2); } } } @@ -1261,11 +1064,11 @@ void MIDIStreamer::CreateSMF(TArray &file, int looplimit) file.Push(0); // Fill in track length - DWORD len = file.Size() - 22; - file[18] = BYTE(len >> 24); - file[19] = BYTE(len >> 16); - file[20] = BYTE(len >> 8); - file[21] = BYTE(len & 255); + uint32_t len = file.Size() - 22; + file[18] = uint8_t(len >> 24); + file[19] = uint8_t(len >> 16); + file[20] = uint8_t(len >> 8); + file[21] = uint8_t(len & 255); LoopLimit = 0; } @@ -1276,9 +1079,9 @@ void MIDIStreamer::CreateSMF(TArray &file, int looplimit) // //========================================================================== -static void WriteVarLen (TArray &file, DWORD value) +static void WriteVarLen (TArray &file, uint32_t value) { - DWORD buffer = value & 0x7F; + uint32_t buffer = value & 0x7F; while ( (value >>= 7) ) { @@ -1288,7 +1091,7 @@ static void WriteVarLen (TArray &file, DWORD value) for (;;) { - file.Push(BYTE(buffer)); + file.Push(uint8_t(buffer)); if (buffer & 0x80) { buffer >>= 8; @@ -1305,7 +1108,7 @@ static void WriteVarLen (TArray &file, DWORD value) // MIDIStreamer :: SetTempo // // Sets the tempo from a track's initial meta events. Later tempo changes -// create MEVT_TEMPO events instead. +// create MEVENT_TEMPO events instead. // //========================================================================== @@ -1456,7 +1259,7 @@ bool MIDIDevice::Preprocess(MIDIStreamer *song, bool looping) // //========================================================================== -int MIDIDevice::PrepareHeader(MIDIHDR *header) +int MIDIDevice::PrepareHeader(MidiHeader *header) { return 0; } @@ -1469,7 +1272,7 @@ int MIDIDevice::PrepareHeader(MIDIHDR *header) // //========================================================================== -int MIDIDevice::UnprepareHeader(MIDIHDR *header) +int MIDIDevice::UnprepareHeader(MidiHeader *header) { return 0; } @@ -1490,16 +1293,23 @@ bool MIDIDevice::FakeVolume() //========================================================================== // -// MIDIDevice :: NeedThreadedCallabck // -// Most implementations can service the callback directly rather than using -// a separate thread. // //========================================================================== -bool MIDIDevice::NeedThreadedCallback() +void MIDIDevice::InitPlayback() { - return false; +} + +//========================================================================== +// +// +// +//========================================================================== + +bool MIDIDevice::Update() +{ + return true; } //========================================================================== diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/musicformats/music_mus_midiout.cpp similarity index 91% rename from src/sound/music_mus_midiout.cpp rename to src/sound/musicformats/music_mus_midiout.cpp index 420c1dfcdc..955c293a78 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/musicformats/music_mus_midiout.cpp @@ -53,7 +53,7 @@ // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- -int MUSHeaderSearch(const BYTE *head, int len); +int MUSHeaderSearch(const uint8_t *head, int len); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- @@ -61,7 +61,7 @@ int MUSHeaderSearch(const BYTE *head, int len); // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static const BYTE CtrlTranslate[15] = +static const uint8_t CtrlTranslate[15] = { 0, // program change 0, // bank select @@ -96,14 +96,7 @@ static const BYTE CtrlTranslate[15] = MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type, const char *args) : MIDIStreamer(type, args), MusHeader(0), MusBuffer(0) { -#ifdef _WIN32 - if (ExitEvent == NULL) - { - return; - } -#endif - - BYTE front[32]; + uint8_t front[32]; int start; if (reader.Read(front, sizeof(front)) != sizeof(front)) @@ -127,9 +120,9 @@ MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type, const char *args) { // It's too short. return; } - MusHeader = (MUSHeader *)new BYTE[len]; + MusHeader = (MUSHeader *)new uint8_t[len]; memcpy(MusHeader, front + start, sizeof(front) - start); - if (reader.Read((BYTE *)MusHeader + sizeof(front) - start, len - (sizeof(front) - start)) != (len - (32 - start))) + if (reader.Read((uint8_t *)MusHeader + sizeof(front) - start, len - (sizeof(front) - start)) != (len - (32 - start))) { return; } @@ -140,7 +133,7 @@ MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type, const char *args) return; } - MusBuffer = (BYTE *)MusHeader + LittleShort(MusHeader->SongStart); + MusBuffer = (uint8_t *)MusHeader + LittleShort(MusHeader->SongStart); MaxMusP = MIN(LittleShort(MusHeader->SongLen), len - LittleShort(MusHeader->SongStart)); Division = 140; InitialTempo = 1000000; @@ -156,7 +149,7 @@ MUSSong2::~MUSSong2 () { if (MusHeader != NULL) { - delete[] (BYTE *)MusHeader; + delete[] (uint8_t *)MusHeader; } } @@ -212,12 +205,12 @@ bool MUSSong2::CheckDone() void MUSSong2::Precache() { TArray work(LittleShort(MusHeader->NumInstruments)); - const BYTE *used = (BYTE *)MusHeader + sizeof(MUSHeader) / sizeof(BYTE); + const uint8_t *used = (uint8_t *)MusHeader + sizeof(MUSHeader) / sizeof(uint8_t); int i, k; for (i = k = 0; i < LittleShort(MusHeader->NumInstruments); ++i) { - BYTE instr = used[k++]; + uint8_t instr = used[k++]; uint16_t val; if (instr < 128) { @@ -260,19 +253,19 @@ void MUSSong2::Precache() // //========================================================================== -DWORD *MUSSong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) +uint32_t *MUSSong2::MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) { - DWORD tot_time = 0; - DWORD time = 0; + uint32_t tot_time = 0; + uint32_t time = 0; max_time = max_time * Division / Tempo; while (events < max_event_p && tot_time <= max_time) { - BYTE mid1, mid2; - BYTE channel; - BYTE t = 0, status; - BYTE event = MusBuffer[MusP++]; + uint8_t mid1, mid2; + uint8_t channel; + uint8_t t = 0, status; + uint8_t event = MusBuffer[MusP++]; if ((event & 0x70) != MUS_SCOREEND) { @@ -370,7 +363,7 @@ end: { events[0] = time; // dwDeltaTime events[1] = 0; // dwStreamID - events[2] = MEVT_NOP << 24; // dwEvent + events[2] = MEVENT_NOP << 24; // dwEvent events += 3; } return events; @@ -409,9 +402,9 @@ MUSSong2::MUSSong2(const MUSSong2 *original, const char *filename, EMidiDevice t { int songstart = LittleShort(original->MusHeader->SongStart); MaxMusP = original->MaxMusP; - MusHeader = (MUSHeader *)new BYTE[songstart + MaxMusP]; + MusHeader = (MUSHeader *)new uint8_t[songstart + MaxMusP]; memcpy(MusHeader, original->MusHeader, songstart + MaxMusP); - MusBuffer = (BYTE *)MusHeader + songstart; + MusBuffer = (uint8_t *)MusHeader + songstart; Division = 140; InitialTempo = 1000000; } @@ -425,7 +418,7 @@ MUSSong2::MUSSong2(const MUSSong2 *original, const char *filename, EMidiDevice t // //========================================================================== -int MUSHeaderSearch(const BYTE *head, int len) +int MUSHeaderSearch(const uint8_t *head, int len) { len -= 4; for (int i = 0; i <= len; ++i) diff --git a/src/sound/music_mus_opl.cpp b/src/sound/musicformats/music_opl.cpp similarity index 57% rename from src/sound/music_mus_opl.cpp rename to src/sound/musicformats/music_opl.cpp index 9150b70e5a..85f4b815b9 100644 --- a/src/sound/music_mus_opl.cpp +++ b/src/sound/musicformats/music_opl.cpp @@ -1,3 +1,36 @@ +/* +** music_opl.cpp +** Plays raw OPL formats +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** 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 "i_musicinterns.h" #include "oplsynth/muslib.h" #include "oplsynth/opl.h" diff --git a/src/sound/music_smf_midiout.cpp b/src/sound/musicformats/music_smf_midiout.cpp similarity index 92% rename from src/sound/music_smf_midiout.cpp rename to src/sound/musicformats/music_smf_midiout.cpp index 623cef53ed..32de991b32 100644 --- a/src/sound/music_smf_midiout.cpp +++ b/src/sound/musicformats/music_smf_midiout.cpp @@ -57,24 +57,24 @@ struct MIDISong2::TrackInfo { - const BYTE *TrackBegin; + const uint8_t *TrackBegin; size_t TrackP; size_t MaxTrackP; - DWORD Delay; - DWORD PlayedTime; + uint32_t Delay; + uint32_t PlayedTime; bool Finished; - BYTE RunningStatus; + uint8_t RunningStatus; bool Designated; bool EProgramChange; bool EVolume; uint16_t Designation; size_t LoopBegin; - DWORD LoopDelay; + uint32_t LoopDelay; int LoopCount; bool LoopFinished; - DWORD ReadVarLen (); + uint32_t ReadVarLen (); }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -108,14 +108,8 @@ MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type, const char *args) int p; int i; -#ifdef _WIN32 - if (ExitEvent == NULL) - { - return; - } -#endif SongLen = reader.GetLength(); - MusHeader = new BYTE[SongLen]; + MusHeader = new uint8_t[SongLen]; if (reader.Read(MusHeader, SongLen) != SongLen) return; @@ -149,13 +143,13 @@ MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type, const char *args) // Gather information about each track for (i = 0, p = 14; i < NumTracks && p < SongLen + 8; ++i) { - DWORD chunkLen = + uint32_t chunkLen = (MusHeader[p+4]<<24) | (MusHeader[p+5]<<16) | (MusHeader[p+6]<<8) | (MusHeader[p+7]); - if (chunkLen + p + 8 > (DWORD)SongLen) + if (chunkLen + p + 8 > (uint32_t)SongLen) { // Track too long, so truncate it chunkLen = SongLen - p - 8; } @@ -213,11 +207,11 @@ MIDISong2::~MIDISong2 () void MIDISong2::CheckCaps(int tech) { DesignationMask = 0xFF0F; - if (tech == MOD_FMSYNTH) + if (tech == MIDIDEV_FMSYNTH) { DesignationMask = 0x00F0; } - else if (tech == MOD_MIDIPORT) + else if (tech == MIDIDEV_MIDIPORT) { DesignationMask = 0x0001; } @@ -298,12 +292,12 @@ bool MIDISong2::CheckDone() // //========================================================================== -DWORD *MIDISong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) +uint32_t *MIDISong2::MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) { - DWORD *start_events; - DWORD tot_time = 0; - DWORD time = 0; - DWORD delay; + uint32_t *start_events; + uint32_t tot_time = 0; + uint32_t time = 0; + uint32_t delay; start_events = events; while (TrackDue && events < max_event_p && tot_time <= max_time) @@ -323,7 +317,7 @@ DWORD *MIDISong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) do { bool sysex_noroom = false; - DWORD *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom); + uint32_t *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom); if (sysex_noroom) { return events; @@ -351,7 +345,7 @@ DWORD *MIDISong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) // //========================================================================== -void MIDISong2::AdvanceTracks(DWORD time) +void MIDISong2::AdvanceTracks(uint32_t time) { for (int i = 0; i < NumTracks; ++i) { @@ -371,10 +365,10 @@ void MIDISong2::AdvanceTracks(DWORD time) // //========================================================================== -DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom) +uint32_t *MIDISong2::SendCommand (uint32_t *events, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom) { - DWORD len; - BYTE event, data1 = 0, data2 = 0; + uint32_t len; + uint8_t event, data1 = 0, data2 = 0; int i; sysex_noroom = false; @@ -387,7 +381,7 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptr // The actual event type will be filled in below. events[0] = delay; events[1] = 0; - events[2] = MEVT_NOP << 24; + events[2] = MEVENT_NOP << 24; if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND) { @@ -456,7 +450,7 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptr case 110: // EMIDI Track Designation - InitBeat only // Instruments 4, 5, 6, and 7 are all FM synth. // The rest are all wavetable. - if (track->PlayedTime < (DWORD)Division) + if (track->PlayedTime < (uint32_t)Division) { if (data2 == 127) { @@ -473,7 +467,7 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptr break; case 111: // EMIDI Track Exclusion - InitBeat only - if (track->PlayedTime < (DWORD)Division) + if (track->PlayedTime < (uint32_t)Division) { if (track->Designated && data2 <= 9) { @@ -485,7 +479,7 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptr case 112: // EMIDI Program Change // Ignored unless it also appears in the InitBeat - if (track->PlayedTime < (DWORD)Division || track->EProgramChange) + if (track->PlayedTime < (uint32_t)Division || track->EProgramChange) { track->EProgramChange = true; event = 0xC0 | (event & 0x0F); @@ -496,7 +490,7 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptr case 113: // EMIDI Volume // Ignored unless it also appears in the InitBeat - if (track->PlayedTime < (DWORD)Division || track->EVolume) + if (track->PlayedTime < (uint32_t)Division || track->EVolume) { track->EVolume = true; data1 = 7; @@ -610,15 +604,15 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptr } else { - BYTE *msg = (BYTE *)&events[3]; + uint8_t *msg = (uint8_t *)&events[3]; if (event == MIDI_SYSEX) { // Need to add the SysEx marker to the message. - events[2] = (MEVT_LONGMSG << 24) | (len + 1); + events[2] = (MEVENT_LONGMSG << 24) | (len + 1); *msg++ = MIDI_SYSEX; } else { - events[2] = (MEVT_LONGMSG << 24) | len; + events[2] = (MEVENT_LONGMSG << 24) | len; } memcpy(msg, &track->TrackBegin[track->TrackP], len); msg += len; @@ -653,7 +647,7 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptr (track->TrackBegin[track->TrackP+2]); events[0] = delay; events[1] = 0; - events[2] = (MEVT_TEMPO << 24) | Tempo; + events[2] = (MEVENT_TEMPO << 24) | Tempo; break; } track->TrackP += len; @@ -673,11 +667,11 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptr track->Delay = track->ReadVarLen(); } // Advance events pointer unless this is a non-delaying NOP. - if (events[0] != 0 || MEVT_EVENTTYPE(events[2]) != MEVT_NOP) + if (events[0] != 0 || MEVENT_EVENTTYPE(events[2]) != MEVENT_NOP) { - if (MEVT_EVENTTYPE(events[2]) == MEVT_LONGMSG) + if (MEVENT_EVENTTYPE(events[2]) == MEVENT_LONGMSG) { - events += 3 + ((MEVT_EVENTPARM(events[2]) + 3) >> 2); + events += 3 + ((MEVENT_EVENTPARM(events[2]) + 3) >> 2); } else { @@ -699,8 +693,8 @@ void MIDISong2::ProcessInitialMetaEvents () { TrackInfo *track; int i; - BYTE event; - DWORD len; + uint8_t event; + uint32_t len; for (i = 0; i < NumTracks; ++i) { @@ -747,9 +741,9 @@ void MIDISong2::ProcessInitialMetaEvents () // //========================================================================== -DWORD MIDISong2::TrackInfo::ReadVarLen () +uint32_t MIDISong2::TrackInfo::ReadVarLen () { - DWORD time = 0, t = 0x80; + uint32_t time = 0, t = 0x80; while ((t & 0x80) && TrackP < MaxTrackP) { @@ -771,7 +765,7 @@ DWORD MIDISong2::TrackInfo::ReadVarLen () MIDISong2::TrackInfo *MIDISong2::FindNextDue () { TrackInfo *track; - DWORD best; + uint32_t best; int i; // Give precedence to whichever track last had events taken from it. @@ -845,7 +839,7 @@ MIDISong2::MIDISong2(const MIDISong2 *original, const char *filename, EMidiDevic : MIDIStreamer(filename, type) { SongLen = original->SongLen; - MusHeader = new BYTE[original->SongLen]; + MusHeader = new uint8_t[original->SongLen]; memcpy(MusHeader, original->MusHeader, original->SongLen); Format = original->Format; NumTracks = original->NumTracks; diff --git a/src/sound/musicformats/music_stream.cpp b/src/sound/musicformats/music_stream.cpp new file mode 100644 index 0000000000..2e57bd31be --- /dev/null +++ b/src/sound/musicformats/music_stream.cpp @@ -0,0 +1,148 @@ +/* +** music_stream.cpp +** Wrapper around the sound system's streaming feature for music. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** 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 "i_musicinterns.h" + +void StreamSong::Play (bool looping, int subsong) +{ + m_Status = STATE_Stopped; + m_Looping = looping; + + if (m_Stream->Play (m_Looping, 1)) + { + if (subsong != 0) + { + m_Stream->SetOrder (subsong); + } + m_Status = STATE_Playing; + } +} + +void StreamSong::Pause () +{ + if (m_Status == STATE_Playing && m_Stream != NULL) + { + if (m_Stream->SetPaused (true)) + m_Status = STATE_Paused; + } +} + +void StreamSong::Resume () +{ + if (m_Status == STATE_Paused && m_Stream != NULL) + { + if (m_Stream->SetPaused (false)) + m_Status = STATE_Playing; + } +} + +void StreamSong::Stop () +{ + if (m_Status != STATE_Stopped && m_Stream) + { + m_Stream->Stop (); + } + m_Status = STATE_Stopped; +} + +StreamSong::~StreamSong () +{ + Stop (); + if (m_Stream != NULL) + { + delete m_Stream; + m_Stream = NULL; + } +} + +StreamSong::StreamSong (FileReader *reader) +{ + m_Stream = GSnd->OpenStream (reader, SoundStream::Loop); +} + +StreamSong::StreamSong (const char *url) +{ + m_Stream = GSnd->OpenStream (url, SoundStream::Loop); +} + +bool StreamSong::IsPlaying () +{ + if (m_Status != STATE_Stopped) + { + if (m_Stream->IsEnded()) + { + Stop(); + return false; + } + return true; + } + return false; +} + +// +// StreamSong :: SetPosition +// +// Sets the position in ms. + +bool StreamSong::SetPosition(unsigned int pos) +{ + if (m_Stream != NULL) + { + return m_Stream->SetPosition(pos); + } + else + { + return false; + } +} + +bool StreamSong::SetSubsong(int subsong) +{ + if (m_Stream != NULL) + { + return m_Stream->SetOrder(subsong); + } + else + { + return false; + } +} + +FString StreamSong::GetStats() +{ + if (m_Stream != NULL) + { + return m_Stream->GetStats(); + } + return "No song loaded\n"; +} diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/musicformats/music_xmi_midiout.cpp similarity index 92% rename from src/sound/music_xmi_midiout.cpp rename to src/sound/musicformats/music_xmi_midiout.cpp index c9a2e9c467..fd595cb09a 100644 --- a/src/sound/music_xmi_midiout.cpp +++ b/src/sound/musicformats/music_xmi_midiout.cpp @@ -65,22 +65,22 @@ struct LoopInfo struct XMISong::TrackInfo { - const BYTE *EventChunk; + const uint8_t *EventChunk; size_t EventLen; size_t EventP; - const BYTE *TimbreChunk; + const uint8_t *TimbreChunk; size_t TimbreLen; - DWORD Delay; - DWORD PlayedTime; + uint32_t Delay; + uint32_t PlayedTime; bool Finished; LoopInfo ForLoops[MAX_FOR_DEPTH]; int ForDepth; - DWORD ReadVarLen(); - DWORD ReadDelay(); + uint32_t ReadVarLen(); + uint32_t ReadDelay(); }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -111,14 +111,8 @@ extern char MIDI_CommonLengths[15]; XMISong::XMISong (FileReader &reader, EMidiDevice type, const char *args) : MIDIStreamer(type, args), MusHeader(0), Songs(0) { -#ifdef _WIN32 - if (ExitEvent == NULL) - { - return; - } -#endif SongLen = reader.GetLength(); - MusHeader = new BYTE[SongLen]; + MusHeader = new uint8_t[SongLen]; if (reader.Read(MusHeader, SongLen) != SongLen) return; @@ -172,7 +166,7 @@ XMISong::~XMISong () // //========================================================================== -int XMISong::FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const +int XMISong::FindXMIDforms(const uint8_t *chunk, int len, TrackInfo *songs) const { int count = 0; @@ -214,7 +208,7 @@ int XMISong::FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const // //========================================================================== -void XMISong::FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const +void XMISong::FoundXMID(const uint8_t *chunk, int len, TrackInfo *song) const { for (int p = 0; p <= len - 8; ) { @@ -313,12 +307,12 @@ bool XMISong::CheckDone() // //========================================================================== -DWORD *XMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) +uint32_t *XMISong::MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) { - DWORD *start_events; - DWORD tot_time = 0; - DWORD time = 0; - DWORD delay; + uint32_t *start_events; + uint32_t tot_time = 0; + uint32_t time = 0; + uint32_t delay; start_events = events; while (EventDue != EVENT_None && events < max_event_p && tot_time <= max_time) @@ -338,7 +332,7 @@ DWORD *XMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) do { bool sysex_noroom = false; - DWORD *new_events = SendCommand(events, EventDue, time, max_event_p - events, sysex_noroom); + uint32_t *new_events = SendCommand(events, EventDue, time, max_event_p - events, sysex_noroom); if (sysex_noroom) { return events; @@ -366,7 +360,7 @@ DWORD *XMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) // //========================================================================== -void XMISong::AdvanceSong(DWORD time) +void XMISong::AdvanceSong(uint32_t time) { if (time != 0) { @@ -387,10 +381,10 @@ void XMISong::AdvanceSong(DWORD time) // //========================================================================== -DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay, ptrdiff_t room, bool &sysex_noroom) +uint32_t *XMISong::SendCommand (uint32_t *events, EventSource due, uint32_t delay, ptrdiff_t room, bool &sysex_noroom) { - DWORD len; - BYTE event, data1 = 0, data2 = 0; + uint32_t len; + uint8_t event, data1 = 0, data2 = 0; if (due == EVENT_Fake) { @@ -416,7 +410,7 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay, ptrdif // Otherwise, we do it at the end of the function. events[0] = delay; events[1] = 0; - events[2] = MEVT_NOP << 24; + events[2] = MEVENT_NOP << 24; if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND) { @@ -540,15 +534,15 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay, ptrdif } else { - BYTE *msg = (BYTE *)&events[3]; + uint8_t *msg = (uint8_t *)&events[3]; if (event == MIDI_SYSEX) { // Need to add the SysEx marker to the message. - events[2] = (MEVT_LONGMSG << 24) | (len + 1); + events[2] = (MEVENT_LONGMSG << 24) | (len + 1); *msg++ = MIDI_SYSEX; } else { - events[2] = (MEVT_LONGMSG << 24) | len; + events[2] = (MEVENT_LONGMSG << 24) | len; } memcpy(msg, &track->EventChunk[track->EventP++], len); msg += len; @@ -591,11 +585,11 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay, ptrdif track->Delay = track->ReadDelay(); } // Advance events pointer unless this is a non-delaying NOP. - if (events[0] != 0 || MEVT_EVENTTYPE(events[2]) != MEVT_NOP) + if (events[0] != 0 || MEVENT_EVENTTYPE(events[2]) != MEVENT_NOP) { - if (MEVT_EVENTTYPE(events[2]) == MEVT_LONGMSG) + if (MEVENT_EVENTTYPE(events[2]) == MEVENT_LONGMSG) { - events += 3 + ((MEVT_EVENTPARM(events[2]) + 3) >> 2); + events += 3 + ((MEVENT_EVENTPARM(events[2]) + 3) >> 2); } else { @@ -616,8 +610,8 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay, ptrdif void XMISong::ProcessInitialMetaEvents () { TrackInfo *track = CurrSong; - BYTE event; - DWORD len; + uint8_t event; + uint32_t len; while (!track->Finished && track->EventP < track->EventLen - 3 && @@ -646,9 +640,9 @@ void XMISong::ProcessInitialMetaEvents () // //========================================================================== -DWORD XMISong::TrackInfo::ReadVarLen() +uint32_t XMISong::TrackInfo::ReadVarLen() { - DWORD time = 0, t = 0x80; + uint32_t time = 0, t = 0x80; while ((t & 0x80) && EventP < EventLen) { @@ -667,9 +661,9 @@ DWORD XMISong::TrackInfo::ReadVarLen() // //========================================================================== -DWORD XMISong::TrackInfo::ReadDelay() +uint32_t XMISong::TrackInfo::ReadDelay() { - DWORD time = 0, t; + uint32_t time = 0, t; while (EventP < EventLen && !((t = EventChunk[EventP]) & 0x80)) { @@ -697,8 +691,8 @@ XMISong::EventSource XMISong::FindNextDue() } // Which is due sooner? The current song or the note-offs? - DWORD real_delay = CurrSong->Finished ? 0xFFFFFFFF : CurrSong->Delay; - DWORD fake_delay = NoteOffs.Size() == 0 ? 0xFFFFFFFF : NoteOffs[0].Delay; + uint32_t real_delay = CurrSong->Finished ? 0xFFFFFFFF : CurrSong->Delay; + uint32_t fake_delay = NoteOffs.Size() == 0 ? 0xFFFFFFFF : NoteOffs[0].Delay; return (fake_delay <= real_delay) ? EVENT_Fake : EVENT_Real; } @@ -736,7 +730,7 @@ XMISong::XMISong(const XMISong *original, const char *filename, EMidiDevice type : MIDIStreamer(filename, type) { SongLen = original->SongLen; - MusHeader = new BYTE[original->SongLen]; + MusHeader = new uint8_t[original->SongLen]; memcpy(MusHeader, original->MusHeader, original->SongLen); NumSongs = original->NumSongs; Tempo = InitialTempo = original->InitialTempo; diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 6bd2dc0910..90639624a3 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -35,7 +35,6 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include -#define USE_WINDOWS_DWORD #else #include #endif @@ -133,7 +132,7 @@ EXTERN_CVAR (Bool, snd_pitched) #define MAKE_PTRID(x) ((void*)(uintptr_t)(x)) -#define GET_PTRID(x) ((uint32)(uintptr_t)(x)) +#define GET_PTRID(x) ((uint32_t)(uintptr_t)(x)) static ALenum checkALError(const char *fn, unsigned int ln) @@ -1053,7 +1052,7 @@ void OpenALSoundRenderer::SetSfxVolume(float volume) void OpenALSoundRenderer::SetMusicVolume(float volume) { MusicVolume = volume; - for(uint32 i = 0;i < Streams.Size();++i) + for(uint32_t i = 0;i < Streams.Size();++i) Streams[i]->UpdateVolume(); } @@ -1099,7 +1098,7 @@ float OpenALSoundRenderer::GetOutputRate() } -std::pair OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend, bool monoize) +std::pair OpenALSoundRenderer::LoadSoundRaw(uint8_t *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend, bool monoize) { SoundHandle retval = { NULL }; @@ -1195,7 +1194,7 @@ std::pair OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int return std::make_pair(retval, channels==1); } -std::pair OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length, bool monoize) +std::pair OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int length, bool monoize) { SoundHandle retval = { NULL }; MemoryReader reader((const char*)sfxdata, length); @@ -1245,13 +1244,13 @@ std::pair OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int le } else if(type == SampleType_UInt8) { - BYTE *sfxdata = (BYTE*)&data[0]; + uint8_t *sfxdata = (uint8_t*)&data[0]; for(size_t i = 0;i < frames;i++) { int sum = 0; for(size_t c = 0;c < chancount;c++) sum += sfxdata[i*chancount + c] - 128; - sfxdata[i] = BYTE((sum / chancount) + 128); + sfxdata[i] = uint8_t((sum / chancount) + 128); } } data.Resize(unsigned(data.Size()/chancount)); @@ -1654,7 +1653,7 @@ void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) alSourcei(source, AL_BUFFER, 0); getALError(); - uint32 i; + uint32_t i; if((i=PausableSfx.Find(source)) < PausableSfx.Size()) PausableSfx.Delete(i); if((i=ReverbSfx.Find(source)) < ReverbSfx.Size()) @@ -1747,10 +1746,10 @@ void OpenALSoundRenderer::Sync(bool sync) TArray toplay = SfxGroup; if(SFXPaused) { - uint32 i = 0; + uint32_t i = 0; while(i < toplay.Size()) { - uint32 p = PausableSfx.Find(toplay[i]); + uint32_t p = PausableSfx.Find(toplay[i]); if(p < PausableSfx.Size()) toplay.Delete(i); else @@ -1879,14 +1878,14 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); // Apply the updated filters on the sources - for(uint32 i = 0;i < ReverbSfx.Size();++i) + for(uint32_t i = 0;i < ReverbSfx.Size();++i) { alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); } } - for(uint32 i = 0;i < ReverbSfx.Size();++i) + for(uint32_t i = 0;i < ReverbSfx.Size();++i) alSourcef(ReverbSfx[i], AL_PITCH, PITCH_MULT); getALError(); } @@ -1903,14 +1902,14 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 1.f); alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - for(uint32 i = 0;i < ReverbSfx.Size();++i) + for(uint32_t i = 0;i < ReverbSfx.Size();++i) { alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); } } - for(uint32 i = 0;i < ReverbSfx.Size();++i) + for(uint32_t i = 0;i < ReverbSfx.Size();++i) alSourcef(ReverbSfx[i], AL_PITCH, 1.f); getALError(); } @@ -2009,9 +2008,9 @@ FString OpenALSoundRenderer::GatherStats() alcGetIntegerv(Device, ALC_REFRESH, 1, &updates); getALCError(Device); - uint32 total = Sources.Size(); - uint32 used = SfxGroup.Size()+Streams.Size(); - uint32 unused = FreeSfx.Size(); + uint32_t total = Sources.Size(); + uint32_t used = SfxGroup.Size()+Streams.Size(); + uint32_t unused = FreeSfx.Size(); FString out; out.Format("%u sources (" TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" active, " TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" free), Update interval: " TEXTCOLOR_YELLOW"%d" TEXTCOLOR_NORMAL"ms", @@ -2055,10 +2054,10 @@ void OpenALSoundRenderer::PrintDriversList() MIDIDevice* OpenALSoundRenderer::CreateMIDIDevice() const { #ifdef _WIN32 - extern UINT mididevice; - return new WinMIDIDevice(mididevice); + extern unsigned mididevice; + return CreateWinMIDIDevice(mididevice); #elif defined __APPLE__ - return new AudioToolboxMIDIDevice; + return CreateAudioToolboxMIDIDevice(); #else return new OPLMIDIDevice(nullptr); #endif @@ -2067,7 +2066,7 @@ MIDIDevice* OpenALSoundRenderer::CreateMIDIDevice() const void OpenALSoundRenderer::PurgeStoppedSources() { // Release channels that are stopped - for(uint32 i = 0;i < SfxGroup.Size();++i) + for(uint32_t i = 0;i < SfxGroup.Size();++i) { ALuint src = SfxGroup[i]; ALint state = AL_INITIAL; diff --git a/src/sound/oalsound.h b/src/sound/oalsound.h index 3e110bde04..0c54f5ab3c 100644 --- a/src/sound/oalsound.h +++ b/src/sound/oalsound.h @@ -83,8 +83,8 @@ public: virtual void SetSfxVolume(float volume); virtual void SetMusicVolume(float volume); - virtual std::pair LoadSound(BYTE *sfxdata, int length, bool monoize); - virtual std::pair LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1, bool monoize = false); + virtual std::pair LoadSound(uint8_t *sfxdata, int length, bool monoize); + virtual std::pair LoadSoundRaw(uint8_t *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1, bool monoize = false); virtual void UnloadSound(SoundHandle sfx); virtual unsigned int GetMSLength(SoundHandle sfx); virtual unsigned int GetSampleLength(SoundHandle sfx); diff --git a/src/oplsynth/OPL3.cpp b/src/sound/oplsynth/OPL3.cpp similarity index 99% rename from src/oplsynth/OPL3.cpp rename to src/sound/oplsynth/OPL3.cpp index a71f12f2b0..a6db55a0f4 100644 --- a/src/oplsynth/OPL3.cpp +++ b/src/sound/oplsynth/OPL3.cpp @@ -523,7 +523,7 @@ namespace EnvelopeGeneratorData class OPL3 : public OPLEmul { public: - BYTE registers[0x200]; + uint8_t registers[0x200]; Operator *operators[2][0x20]; Channel2op *channels2op[2][9]; diff --git a/src/oplsynth/dosbox/opl.cpp b/src/sound/oplsynth/dosbox/opl.cpp similarity index 99% rename from src/oplsynth/dosbox/opl.cpp rename to src/sound/oplsynth/dosbox/opl.cpp index 8f2fdd8592..25b4462109 100644 --- a/src/oplsynth/dosbox/opl.cpp +++ b/src/sound/oplsynth/dosbox/opl.cpp @@ -34,12 +34,12 @@ static FRandom pr_opl; typedef uintptr_t Bitu; typedef intptr_t Bits; -typedef DWORD Bit32u; +typedef uint32_t Bit32u; typedef int32_t Bit32s; typedef uint16_t Bit16u; typedef int16_t Bit16s; typedef uint8_t Bit8u; -typedef SBYTE Bit8s; +typedef int8_t Bit8s; #define OPLTYPE_IS_OPL3 #undef PI diff --git a/src/oplsynth/dosbox/opl.h b/src/sound/oplsynth/dosbox/opl.h similarity index 100% rename from src/oplsynth/dosbox/opl.h rename to src/sound/oplsynth/dosbox/opl.h diff --git a/src/oplsynth/fmopl.cpp b/src/sound/oplsynth/fmopl.cpp similarity index 100% rename from src/oplsynth/fmopl.cpp rename to src/sound/oplsynth/fmopl.cpp diff --git a/src/oplsynth/mlopl.cpp b/src/sound/oplsynth/mlopl.cpp similarity index 100% rename from src/oplsynth/mlopl.cpp rename to src/sound/oplsynth/mlopl.cpp diff --git a/src/oplsynth/mlopl_io.cpp b/src/sound/oplsynth/mlopl_io.cpp similarity index 100% rename from src/oplsynth/mlopl_io.cpp rename to src/sound/oplsynth/mlopl_io.cpp diff --git a/src/oplsynth/muslib.h b/src/sound/oplsynth/muslib.h similarity index 100% rename from src/oplsynth/muslib.h rename to src/sound/oplsynth/muslib.h diff --git a/src/oplsynth/nukedopl3.cpp b/src/sound/oplsynth/nukedopl3.cpp similarity index 100% rename from src/oplsynth/nukedopl3.cpp rename to src/sound/oplsynth/nukedopl3.cpp diff --git a/src/oplsynth/nukedopl3.h b/src/sound/oplsynth/nukedopl3.h similarity index 99% rename from src/oplsynth/nukedopl3.h rename to src/sound/oplsynth/nukedopl3.h index b5abf3b38b..17ebbe4d2c 100644 --- a/src/oplsynth/nukedopl3.h +++ b/src/sound/oplsynth/nukedopl3.h @@ -34,12 +34,12 @@ typedef uintptr_t Bitu; typedef intptr_t Bits; -typedef DWORD Bit32u; +typedef uint32_t Bit32u; typedef int32_t Bit32s; typedef uint16_t Bit16u; typedef int16_t Bit16s; typedef uint8_t Bit8u; -typedef SBYTE Bit8s; +typedef int8_t Bit8s; // Channel types diff --git a/src/oplsynth/opl.h b/src/sound/oplsynth/opl.h similarity index 100% rename from src/oplsynth/opl.h rename to src/sound/oplsynth/opl.h diff --git a/src/oplsynth/opl_mus_player.cpp b/src/sound/oplsynth/opl_mus_player.cpp similarity index 93% rename from src/oplsynth/opl_mus_player.cpp rename to src/sound/oplsynth/opl_mus_player.cpp index 5c8d2d2a5b..55dd132cef 100644 --- a/src/oplsynth/opl_mus_player.cpp +++ b/src/sound/oplsynth/opl_mus_player.cpp @@ -75,8 +75,8 @@ fail: delete[] scoredata; } // Check for RDosPlay raw OPL format - if (((DWORD *)scoredata)[0] == MAKE_ID('R','A','W','A') && - ((DWORD *)scoredata)[1] == MAKE_ID('D','A','T','A')) + if (((uint32_t *)scoredata)[0] == MAKE_ID('R','A','W','A') && + ((uint32_t *)scoredata)[1] == MAKE_ID('D','A','T','A')) { RawPlayer = RDosPlay; if (*(uint16_t *)(scoredata + 8) == 0) @@ -86,16 +86,16 @@ fail: delete[] scoredata; SamplesPerTick = LittleShort(*(uint16_t *)(scoredata + 8)) / ADLIB_CLOCK_MUL; } // Check for DosBox OPL dump - else if (((DWORD *)scoredata)[0] == MAKE_ID('D','B','R','A') && - ((DWORD *)scoredata)[1] == MAKE_ID('W','O','P','L')) + else if (((uint32_t *)scoredata)[0] == MAKE_ID('D','B','R','A') && + ((uint32_t *)scoredata)[1] == MAKE_ID('W','O','P','L')) { if (LittleShort(((uint16_t *)scoredata)[5]) == 1) { RawPlayer = DosBox1; SamplesPerTick = OPL_SAMPLE_RATE / 1000; - ScoreLen = MIN(ScoreLen - 24, LittleLong(((DWORD *)scoredata)[4])) + 24; + ScoreLen = MIN(ScoreLen - 24, LittleLong(((uint32_t *)scoredata)[4])) + 24; } - else if (((DWORD *)scoredata)[2] == MAKE_ID(2,0,0,0)) + else if (((uint32_t *)scoredata)[2] == MAKE_ID(2,0,0,0)) { bool okay = true; if (scoredata[21] != 0) @@ -113,7 +113,7 @@ fail: delete[] scoredata; RawPlayer = DosBox2; SamplesPerTick = OPL_SAMPLE_RATE / 1000; int headersize = 0x1A + scoredata[0x19]; - ScoreLen = MIN(ScoreLen - headersize, LittleLong(((DWORD *)scoredata)[3]) * 2) + headersize; + ScoreLen = MIN(ScoreLen - headersize, LittleLong(((uint32_t *)scoredata)[3]) * 2) + headersize; } else { @@ -122,7 +122,7 @@ fail: delete[] scoredata; } } // Check for modified IMF format (includes a header) - else if (((DWORD *)scoredata)[0] == MAKE_ID('A','D','L','I') && + else if (((uint32_t *)scoredata)[0] == MAKE_ID('A','D','L','I') && scoredata[4] == 'B' && scoredata[5] == 1) { int songlen; @@ -143,7 +143,7 @@ fail: delete[] scoredata; scoredata = NULL; return; } - songlen = LittleLong(*(DWORD *)score); + songlen = LittleLong(*(uint32_t *)score); if (songlen != 0 && (songlen +=4) < ScoreLen - (score - scoredata)) { ScoreLen = songlen + int(score - scoredata); @@ -207,7 +207,7 @@ void OPLmusicFile::Restart () while (*score++ != '\0') {} } score++; // Skip unknown byte - if (*(DWORD *)score != 0) + if (*(uint32_t *)score != 0) { score += 4; // Skip song length } @@ -487,7 +487,7 @@ int OPLmusicFile::PlayTick () delay = 0; while (delay == 0 && score + 4 - scoredata <= ScoreLen) { - if (*(DWORD *)score == 0xFFFFFFFF) + if (*(uint32_t *)score == 0xFFFFFFFF) { // This is a special value that means to end the song. return 0; } diff --git a/src/oplsynth/opl_mus_player.h b/src/sound/oplsynth/opl_mus_player.h similarity index 100% rename from src/oplsynth/opl_mus_player.h rename to src/sound/oplsynth/opl_mus_player.h diff --git a/src/sound/sndfile_decoder.cpp b/src/sound/sndfile_decoder.cpp index 02f24b9da2..5a957eb271 100644 --- a/src/sound/sndfile_decoder.cpp +++ b/src/sound/sndfile_decoder.cpp @@ -1,7 +1,6 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include -#define USE_WINDOWS_DWORD #endif #include "sndfile_decoder.h" diff --git a/src/timidity/CHANGES b/src/sound/timidity/CHANGES similarity index 100% rename from src/timidity/CHANGES rename to src/sound/timidity/CHANGES diff --git a/src/timidity/COPYING b/src/sound/timidity/COPYING similarity index 100% rename from src/timidity/COPYING rename to src/sound/timidity/COPYING diff --git a/src/timidity/FAQ b/src/sound/timidity/FAQ similarity index 100% rename from src/timidity/FAQ rename to src/sound/timidity/FAQ diff --git a/src/timidity/README b/src/sound/timidity/README similarity index 100% rename from src/timidity/README rename to src/sound/timidity/README diff --git a/src/timidity/common.cpp b/src/sound/timidity/common.cpp similarity index 100% rename from src/timidity/common.cpp rename to src/sound/timidity/common.cpp diff --git a/src/timidity/dls1.h b/src/sound/timidity/dls1.h similarity index 99% rename from src/timidity/dls1.h rename to src/sound/timidity/dls1.h index abc2075a51..86eccffc92 100644 --- a/src/timidity/dls1.h +++ b/src/sound/timidity/dls1.h @@ -133,7 +133,7 @@ typedef struct _DLSID { ULONG ulData1; USHORT usData2; USHORT usData3; - BYTE abData4[8]; + uint8_t abData4[8]; } DLSID, FAR *LPDLSID; typedef struct _DLSVERSION { diff --git a/src/timidity/dls2.h b/src/sound/timidity/dls2.h similarity index 100% rename from src/timidity/dls2.h rename to src/sound/timidity/dls2.h diff --git a/src/timidity/gf1patch.h b/src/sound/timidity/gf1patch.h similarity index 66% rename from src/timidity/gf1patch.h rename to src/sound/timidity/gf1patch.h index b030bb6e09..9b1800799a 100644 --- a/src/timidity/gf1patch.h +++ b/src/sound/timidity/gf1patch.h @@ -26,13 +26,13 @@ struct GF1PatchHeader char Header[HEADER_SIZE]; char GravisID[ID_SIZE]; /* Id = "ID#000002" */ char Description[DESC_SIZE]; - BYTE Instruments; - BYTE Voices; - BYTE Channels; + uint8_t Instruments; + uint8_t Voices; + uint8_t Channels; uint16_t WaveForms; uint16_t MasterVolume; - DWORD DataSize; - BYTE Reserved[PATCH_HEADER_RESERVED_SIZE]; + uint32_t DataSize; + uint8_t Reserved[PATCH_HEADER_RESERVED_SIZE]; } GCC_PACKED; struct GF1InstrumentData @@ -40,23 +40,23 @@ struct GF1InstrumentData uint16_t Instrument; char InstrumentName[INST_NAME_SIZE]; int InstrumentSize; - BYTE Layers; - BYTE Reserved[RESERVED_SIZE]; + uint8_t Layers; + uint8_t Reserved[RESERVED_SIZE]; } GCC_PACKED; struct GF1LayerData { - BYTE LayerDuplicate; - BYTE Layer; + uint8_t LayerDuplicate; + uint8_t Layer; int LayerSize; - BYTE Samples; - BYTE Reserved[LAYER_RESERVED_SIZE]; + uint8_t Samples; + uint8_t Reserved[LAYER_RESERVED_SIZE]; } GCC_PACKED; struct GF1PatchData { char WaveName[7]; - BYTE Fractions; + uint8_t Fractions; int WaveSize; int StartLoop; int EndLoop; @@ -65,19 +65,19 @@ struct GF1PatchData int HighFrequency; int RootFrequency; int16_t Tune; - BYTE Balance; - BYTE EnvelopeRate[ENVELOPES]; - BYTE EnvelopeOffset[ENVELOPES]; - BYTE TremoloSweep; - BYTE TremoloRate; - BYTE TremoloDepth; - BYTE VibratoSweep; - BYTE VibratoRate; - BYTE VibratoDepth; - BYTE Modes; + uint8_t Balance; + uint8_t EnvelopeRate[ENVELOPES]; + uint8_t EnvelopeOffset[ENVELOPES]; + uint8_t TremoloSweep; + uint8_t TremoloRate; + uint8_t TremoloDepth; + uint8_t VibratoSweep; + uint8_t VibratoRate; + uint8_t VibratoDepth; + uint8_t Modes; int16_t ScaleFrequency; uint16_t ScaleFactor; /* From 0 to 2048 or 0 to 2 */ - BYTE Reserved[PATCH_DATA_RESERVED_SIZE]; + uint8_t Reserved[PATCH_DATA_RESERVED_SIZE]; } GCC_PACKED; #ifdef _MSC_VER #pragma pack(pop) diff --git a/src/timidity/instrum.cpp b/src/sound/timidity/instrum.cpp similarity index 97% rename from src/timidity/instrum.cpp rename to src/sound/timidity/instrum.cpp index e4d5918791..0b88f39bc0 100644 --- a/src/timidity/instrum.cpp +++ b/src/sound/timidity/instrum.cpp @@ -81,7 +81,7 @@ ToneBank::~ToneBank() } } -int convert_tremolo_sweep(Renderer *song, BYTE sweep) +int convert_tremolo_sweep(Renderer *song, uint8_t sweep) { if (sweep == 0) return 0; @@ -90,7 +90,7 @@ int convert_tremolo_sweep(Renderer *song, BYTE sweep) int(((song->control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (song->rate * sweep)); } -int convert_vibrato_sweep(Renderer *song, BYTE sweep, int vib_control_ratio) +int convert_vibrato_sweep(Renderer *song, uint8_t sweep, int vib_control_ratio) { if (sweep == 0) return 0; @@ -104,13 +104,13 @@ int convert_vibrato_sweep(Renderer *song, BYTE sweep, int vib_control_ratio) */ } -int convert_tremolo_rate(Renderer *song, BYTE rate) +int convert_tremolo_rate(Renderer *song, uint8_t rate) { return int(((song->control_ratio * rate) << RATE_SHIFT) / (TREMOLO_RATE_TUNING * song->rate)); } -int convert_vibrato_rate(Renderer *song, BYTE rate) +int convert_vibrato_rate(Renderer *song, uint8_t rate) { /* Return a suitable vibrato_control_ratio value */ return @@ -403,7 +403,7 @@ fail: { sp->envelope.gf1.rate[j] = patch_data.EnvelopeRate[j]; /* [RH] GF1NEW clamps the offsets to the range [5,251], so we do too. */ - sp->envelope.gf1.offset[j] = clamp(patch_data.EnvelopeOffset[j], 5, 251); + sp->envelope.gf1.offset[j] = clamp(patch_data.EnvelopeOffset[j], 5, 251); } /* Then read the sample data */ @@ -474,7 +474,7 @@ void convert_sample_data(Sample *sp, const void *data) { case 0: { /* 8-bit, signed */ - SBYTE *cp = (SBYTE *)data; + int8_t *cp = (int8_t *)data; newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t)); for (int i = 0; i < sp->data_length; ++i) { @@ -492,7 +492,7 @@ void convert_sample_data(Sample *sp, const void *data) case PATCH_UNSIGNED: { /* 8-bit, unsigned */ - BYTE *cp = (BYTE *)data; + uint8_t *cp = (uint8_t *)data; newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t)); for (int i = 0; i < sp->data_length; ++i) { diff --git a/src/timidity/instrum.obj b/src/sound/timidity/instrum.obj similarity index 100% rename from src/timidity/instrum.obj rename to src/sound/timidity/instrum.obj diff --git a/src/timidity/instrum_dls.cpp b/src/sound/timidity/instrum_dls.cpp similarity index 98% rename from src/timidity/instrum_dls.cpp rename to src/sound/timidity/instrum_dls.cpp index 06ce83df23..4f553ff5fd 100644 --- a/src/timidity/instrum_dls.cpp +++ b/src/sound/timidity/instrum_dls.cpp @@ -59,7 +59,7 @@ struct RIFF_Chunk uint32_t magic; uint32_t length; uint32_t subtype; - BYTE *data; + uint8_t *data; RIFF_Chunk *child; RIFF_Chunk *next; }; @@ -85,9 +85,9 @@ static int ChunkHasSubChunks(uint32_t magic) return (magic == RIFF || magic == LIST); } -static void LoadSubChunks(RIFF_Chunk *chunk, BYTE *data, uint32_t left) +static void LoadSubChunks(RIFF_Chunk *chunk, uint8_t *data, uint32_t left) { - BYTE *subchunkData; + uint8_t *subchunkData; uint32_t subchunkDataLen; while ( left > 8 ) { @@ -133,7 +133,7 @@ static void LoadSubChunks(RIFF_Chunk *chunk, BYTE *data, uint32_t left) RIFF_Chunk *LoadRIFF(FILE *src) { RIFF_Chunk *chunk; - BYTE *subchunkData; + uint8_t *subchunkData; uint32_t subchunkDataLen; /* Allocate the chunk structure */ @@ -148,7 +148,7 @@ RIFF_Chunk *LoadRIFF(FILE *src) delete chunk; return NULL; } - chunk->data = (BYTE *)malloc(chunk->length); + chunk->data = (uint8_t *)malloc(chunk->length); if ( chunk->data == NULL ) { __Sound_SetError(ERR_OUT_OF_MEMORY); delete chunk; @@ -255,6 +255,8 @@ typedef int16_t SHORT; typedef uint16_t USHORT; typedef int32_t LONG; typedef uint32_t ULONG; +typedef uint32_t DWORD; + #define mmioFOURCC MAKE_ID #define DEFINE_GUID(A, B, C, E, F, G, H, I, J, K, L, M) @@ -274,7 +276,7 @@ struct WaveFMT struct DLS_Wave { WaveFMT *format; - BYTE *data; + uint8_t *data; uint32_t length; WSMPL *wsmp; WLOOP *wsmp_loop; @@ -451,7 +453,7 @@ static void Parse_wsmp(DLS_Data *data, RIFF_Chunk *chunk, WSMPL **wsmp_ptr, WLOO wsmp->lAttenuation = LittleLong(wsmp->lAttenuation); wsmp->fulOptions = LittleLong(wsmp->fulOptions); wsmp->cSampleLoops = LittleLong(wsmp->cSampleLoops); - loop = (WLOOP *)((BYTE *)chunk->data + wsmp->cbSize); + loop = (WLOOP *)((uint8_t *)chunk->data + wsmp->cbSize); *wsmp_ptr = wsmp; *wsmp_loop_ptr = loop; for ( i = 0; i < wsmp->cSampleLoops; ++i ) { @@ -470,7 +472,7 @@ static void Parse_art(DLS_Data *data, RIFF_Chunk *chunk, CONNECTIONLIST **art_pt CONNECTION *artList; art->cbSize = LittleLong(art->cbSize); art->cConnections = LittleLong(art->cConnections); - artList = (CONNECTION *)((BYTE *)chunk->data + art->cbSize); + artList = (CONNECTION *)((uint8_t *)chunk->data + art->cbSize); *art_ptr = art; *artList_ptr = artList; for ( i = 0; i < art->cConnections; ++i ) { @@ -591,7 +593,7 @@ static void Parse_ptbl(DLS_Data *data, RIFF_Chunk *chunk) ptbl->cbSize = LittleLong(ptbl->cbSize); ptbl->cCues = LittleLong(ptbl->cCues); data->ptbl = ptbl; - data->ptblList = (POOLCUE *)((BYTE *)chunk->data + ptbl->cbSize); + data->ptblList = (POOLCUE *)((uint8_t *)chunk->data + ptbl->cbSize); for ( i = 0; i < ptbl->cCues; ++i ) { data->ptblList[i].ulOffset = LittleLong(data->ptblList[i].ulOffset); } @@ -1123,12 +1125,12 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, sample->type = INST_DLS; sample->self_nonexclusive = !!(rgn->header->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE); - sample->key_group = (SBYTE)rgn->header->usKeyGroup; + sample->key_group = (int8_t)rgn->header->usKeyGroup; sample->low_freq = note_to_freq(rgn->header->RangeKey.usLow); sample->high_freq = note_to_freq(rgn->header->RangeKey.usHigh); sample->root_freq = note_to_freq(rgn->wsmp->usUnityNote + rgn->wsmp->sFineTune * .01f); - sample->low_vel = (BYTE)rgn->header->RangeVelocity.usLow; - sample->high_vel = (BYTE)rgn->header->RangeVelocity.usHigh; + sample->low_vel = (uint8_t)rgn->header->RangeVelocity.usLow; + sample->high_vel = (uint8_t)rgn->header->RangeVelocity.usHigh; sample->modes = wave->format->wBitsPerSample == 8 ? PATCH_UNSIGNED : PATCH_16; sample->sample_rate = wave->format->dwSamplesPerSec; diff --git a/src/timidity/instrum_font.cpp b/src/sound/timidity/instrum_font.cpp similarity index 100% rename from src/timidity/instrum_font.cpp rename to src/sound/timidity/instrum_font.cpp diff --git a/src/timidity/instrum_sf2.cpp b/src/sound/timidity/instrum_sf2.cpp similarity index 93% rename from src/timidity/instrum_sf2.cpp rename to src/sound/timidity/instrum_sf2.cpp index fe839ce45e..a2d747b927 100644 --- a/src/timidity/instrum_sf2.cpp +++ b/src/sound/timidity/instrum_sf2.cpp @@ -12,7 +12,7 @@ using namespace Timidity; -#define cindex(identifier) (BYTE)(((size_t)&((SFGenComposite *)1)->identifier - 1) / 2) +#define cindex(identifier) (uint8_t)(((size_t)&((SFGenComposite *)1)->identifier - 1) / 2) class CIOErr {}; class CBadForm {}; @@ -20,8 +20,8 @@ class CBadVer {}; struct ListHandler { - DWORD ID; - void (*Parser)(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); + uint32_t ID; + void (*Parser)(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); }; enum @@ -36,8 +36,8 @@ struct GenDef { short Min; short Max; - BYTE StructIndex; - BYTE Flags; + uint8_t StructIndex; + uint8_t Flags; }; static const GenDef GenDefs[] = @@ -155,15 +155,15 @@ static const SFGenComposite DefaultGenerators = -1 // overridingRootKey }; -static void ParseIfil(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); -static void ParseSmpl(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); -static void ParseSm24(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); -static void ParsePhdr(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); -static void ParseBag(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); -static void ParseMod(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); -static void ParseGen(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); -static void ParseInst(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); -static void ParseShdr(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen); +static void ParseIfil(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); +static void ParseSmpl(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); +static void ParseSm24(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); +static void ParsePhdr(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); +static void ParseBag(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); +static void ParseMod(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); +static void ParseGen(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); +static void ParseInst(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); +static void ParseShdr(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen); ListHandler INFOHandlers[] = { @@ -218,9 +218,9 @@ static int32_t calc_rate(Renderer *song, int diff, double sec) } -static inline DWORD read_id(FileReader *f) +static inline uint32_t read_id(FileReader *f) { - DWORD id; + uint32_t id; if (f->Read(&id, 4) != 4) { throw CIOErr(); @@ -230,7 +230,7 @@ static inline DWORD read_id(FileReader *f) static inline int read_byte(FileReader *f) { - BYTE x; + uint8_t x; if (f->Read(&x, 1) != 1) { throw CIOErr(); @@ -240,7 +240,7 @@ static inline int read_byte(FileReader *f) static inline int read_char(FileReader *f) { - SBYTE x; + int8_t x; if (f->Read(&x, 1) != 1) { throw CIOErr(); @@ -268,9 +268,9 @@ static inline int read_sword(FileReader *f) return LittleShort(x); } -static inline DWORD read_dword(FileReader *f) +static inline uint32_t read_dword(FileReader *f) { - DWORD x; + uint32_t x; if (f->Read(&x, 4) != 4) { throw CIOErr(); @@ -287,7 +287,7 @@ static inline void read_name(FileReader *f, char name[21]) name[20] = 0; } -static inline void skip_chunk(FileReader *f, DWORD len) +static inline void skip_chunk(FileReader *f, uint32_t len) { // RIFF, like IFF, adds an extra pad byte to the end of // odd-sized chunks so that new chunks are always on even @@ -298,7 +298,7 @@ static inline void skip_chunk(FileReader *f, DWORD len) } } -static void check_list(FileReader *f, DWORD id, DWORD filelen, DWORD &chunklen) +static void check_list(FileReader *f, uint32_t id, uint32_t filelen, uint32_t &chunklen) { if (read_id(f) != ID_LIST) { @@ -315,7 +315,7 @@ static void check_list(FileReader *f, DWORD id, DWORD filelen, DWORD &chunklen) } } -static void ParseIfil(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) +static void ParseIfil(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen) { uint16_t major, minor; @@ -334,11 +334,11 @@ static void ParseIfil(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) sf2->MinorVersion = minor; } -static void ParseLIST(SFFile *sf2, FileReader *f, DWORD chunklen, ListHandler *handlers) +static void ParseLIST(SFFile *sf2, FileReader *f, uint32_t chunklen, ListHandler *handlers) { ListHandler *handler; - DWORD id; - DWORD len; + uint32_t id; + uint32_t len; chunklen -= 4; while (chunklen > 0) @@ -368,7 +368,7 @@ static void ParseLIST(SFFile *sf2, FileReader *f, DWORD chunklen, ListHandler *h } } -static void ParseINFO(SFFile *sf2, FileReader *f, DWORD chunklen) +static void ParseINFO(SFFile *sf2, FileReader *f, uint32_t chunklen) { sf2->MinorVersion = -1; @@ -380,7 +380,7 @@ static void ParseINFO(SFFile *sf2, FileReader *f, DWORD chunklen) } } -static void ParseSdta(SFFile *sf2, FileReader *f, DWORD chunklen) +static void ParseSdta(SFFile *sf2, FileReader *f, uint32_t chunklen) { ParseLIST(sf2, f, chunklen, SdtaHandlers); if (sf2->SampleDataOffset == 0) @@ -397,7 +397,7 @@ static void ParseSdta(SFFile *sf2, FileReader *f, DWORD chunklen) } } -static void ParseSmpl(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) +static void ParseSmpl(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen) { // Only use the first smpl chunk. (Or should we reject files with more than one?) if (sf2->SampleDataOffset == 0) @@ -412,7 +412,7 @@ static void ParseSmpl(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) skip_chunk(f, chunklen); } -static void ParseSm24(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) +static void ParseSm24(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen) { // The sm24 chunk is ignored if the file version is < 2.04 if (sf2->MinorVersion >= 4) @@ -427,12 +427,12 @@ static void ParseSm24(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) skip_chunk(f, chunklen); } -static void ParsePdta(SFFile *sf2, FileReader *f, DWORD chunklen) +static void ParsePdta(SFFile *sf2, FileReader *f, uint32_t chunklen) { ParseLIST(sf2, f, chunklen, PdtaHandlers); } -static void ParsePhdr(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) +static void ParsePhdr(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen) { SFPreset *preset; @@ -470,7 +470,7 @@ static void ParsePhdr(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) } } -static void ParseBag(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) +static void ParseBag(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen) { SFBag *bags, *bag; uint16_t prev_mod = 0; @@ -532,7 +532,7 @@ static void ParseBag(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) } } -static void ParseMod(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) +static void ParseMod(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen) { // Section 7.4, page 23: // It [the PMOD sub-chunk] is always a multiple of ten bytes in length, @@ -545,7 +545,7 @@ static void ParseMod(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) skip_chunk(f, chunklen); } -static void ParseGen(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) +static void ParseGen(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen) { SFGenList *gens, *gen; int numgens; @@ -597,7 +597,7 @@ static void ParseGen(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) } } -static void ParseInst(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) +static void ParseInst(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen) { int i; SFInst *inst; @@ -632,7 +632,7 @@ static void ParseInst(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) } } -static void ParseShdr(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) +static void ParseShdr(SFFile *sf2, FileReader *f, uint32_t chunkid, uint32_t chunklen) { int i; SFSample *sample; @@ -694,8 +694,8 @@ static void ParseShdr(SFFile *sf2, FileReader *f, DWORD chunkid, DWORD chunklen) SFFile *ReadSF2(const char *filename, FileReader *f) { SFFile *sf2 = NULL; - DWORD filelen; - DWORD chunklen; + uint32_t filelen; + uint32_t chunklen; try { @@ -1140,7 +1140,7 @@ void SFFile::TranslatePercussionPresetZone(SFPreset *preset, SFBag *pzone) } SetInstrumentGenerators(&perc.Generators, InstrBags[i].GenIndex, InstrBags[i + 1].GenIndex); AddPresetGenerators(&perc.Generators, pzone->GenIndex, (pzone + 1)->GenIndex, preset); - perc.Generators.drumset = (BYTE)preset->Program; + perc.Generators.drumset = (uint8_t)preset->Program; perc.Generators.key = key; perc.Generators.velRange.Lo = MAX(pzone->VelRange.Lo, InstrBags[i].VelRange.Lo); perc.Generators.velRange.Hi = MIN(pzone->VelRange.Hi, InstrBags[i].VelRange.Hi); @@ -1487,7 +1487,7 @@ void SFFile::ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Rend // Set tuning (in cents) sp->tune = gen->coarseTune * 100 + gen->fineTune; - sp->velocity = (SBYTE)gen->velocity; + sp->velocity = (int8_t)gen->velocity; sp->initial_attenuation = gen->initialAttenuation; } @@ -1502,7 +1502,7 @@ void SFFile::ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Rend void SFFile::LoadSample(SFSample *sample) { FileReader *fp = pathExpander.openFileReader(Filename, NULL); - DWORD i; + uint32_t i; if (fp == NULL) { @@ -1522,7 +1522,7 @@ void SFFile::LoadSample(SFSample *sample) fp->Seek(SampleDataLSBOffset + sample->Start, SEEK_SET); for (i = 0; i < sample->End - sample->Start; ++i) { - BYTE samp; + uint8_t samp; *fp >> samp; sample->InMemoryData[i] = ((((int32_t(sample->InMemoryData[i] * 32768) << 8) | samp) << 8) >> 8) / 8388608.f; } diff --git a/src/timidity/mix.cpp b/src/sound/timidity/mix.cpp similarity index 99% rename from src/timidity/mix.cpp rename to src/sound/timidity/mix.cpp index 037226c2c2..e09a15ba0e 100644 --- a/src/timidity/mix.cpp +++ b/src/sound/timidity/mix.cpp @@ -32,7 +32,7 @@ namespace Timidity { -static int convert_envelope_rate(Renderer *song, BYTE rate) +static int convert_envelope_rate(Renderer *song, uint8_t rate) { int r; diff --git a/src/timidity/playmidi.cpp b/src/sound/timidity/playmidi.cpp similarity index 99% rename from src/timidity/playmidi.cpp rename to src/sound/timidity/playmidi.cpp index f1426206ec..f8df539fa0 100644 --- a/src/timidity/playmidi.cpp +++ b/src/sound/timidity/playmidi.cpp @@ -116,7 +116,7 @@ void Renderer::recompute_freq(int v) voice[v].sample_increment = (int)(a); } -static const BYTE vol_table[] = { +static const uint8_t vol_table[] = { 000 /* 000 */, 129 /* 001 */, 145 /* 002 */, 155 /* 003 */, 161 /* 004 */, 166 /* 005 */, 171 /* 006 */, 174 /* 007 */, 177 /* 008 */, 180 /* 009 */, 182 /* 010 */, 185 /* 011 */, @@ -872,7 +872,7 @@ void Renderer::DataEntryFineNRPN(int chan, int nrpn, int val) { } -void Renderer::HandleLongMessage(const BYTE *data, int len) +void Renderer::HandleLongMessage(const uint8_t *data, int len) { // SysEx handling goes here. } diff --git a/src/timidity/resample.cpp b/src/sound/timidity/resample.cpp similarity index 100% rename from src/timidity/resample.cpp rename to src/sound/timidity/resample.cpp diff --git a/src/timidity/sf2.h b/src/sound/timidity/sf2.h similarity index 93% rename from src/timidity/sf2.h rename to src/sound/timidity/sf2.h index 45dec4ae02..26d413ab56 100644 --- a/src/timidity/sf2.h +++ b/src/sound/timidity/sf2.h @@ -2,15 +2,15 @@ typedef uint16_t SFGenerator; struct SFRange { - BYTE Lo; - BYTE Hi; + uint8_t Lo; + uint8_t Hi; }; struct SFPreset { char Name[21]; - BYTE LoadOrder:7; - BYTE bHasGlobalZone:1; + uint8_t LoadOrder:7; + uint8_t bHasGlobalZone:1; uint16_t Program; uint16_t Bank; uint16_t BagIndex; @@ -29,21 +29,21 @@ struct SFBag struct SFInst { char Name[21]; - BYTE Pad:7; - BYTE bHasGlobalZone:1; + uint8_t Pad:7; + uint8_t bHasGlobalZone:1; uint16_t BagIndex; }; struct SFSample { float *InMemoryData; - DWORD Start; - DWORD End; - DWORD StartLoop; - DWORD EndLoop; - DWORD SampleRate; - BYTE OriginalPitch; - SBYTE PitchCorrection; + uint32_t Start; + uint32_t End; + uint32_t StartLoop; + uint32_t EndLoop; + uint32_t SampleRate; + uint8_t OriginalPitch; + int8_t PitchCorrection; uint16_t SampleLink; uint16_t SampleType; char Name[21]; @@ -199,8 +199,8 @@ struct SFGenComposite SFRange keyRange; // For normal use struct // For intermediate percussion use { - BYTE drumset; - BYTE key; + uint8_t drumset; + uint8_t key; }; }; SFRange velRange; @@ -263,7 +263,7 @@ struct SFPerc { SFPreset *Preset; SFGenComposite Generators; - BYTE LoadOrder; + uint8_t LoadOrder; }; // Container for all parameters from a SoundFont file @@ -302,10 +302,10 @@ struct SFFile : public Timidity::FontFile SFSample *Samples; TArray Percussion; int MinorVersion; - DWORD SampleDataOffset; - DWORD SampleDataLSBOffset; - DWORD SizeSampleData; - DWORD SizeSampleDataLSB; + uint32_t SampleDataOffset; + uint32_t SampleDataLSBOffset; + uint32_t SizeSampleData; + uint32_t SizeSampleDataLSB; int NumPresets; int NumPresetBags; int NumPresetGenerators; diff --git a/src/timidity/timidity.cpp b/src/sound/timidity/timidity.cpp similarity index 99% rename from src/timidity/timidity.cpp rename to src/sound/timidity/timidity.cpp index 4ca2b872e3..86fe9d11d8 100644 --- a/src/timidity/timidity.cpp +++ b/src/sound/timidity/timidity.cpp @@ -579,7 +579,7 @@ int LoadDMXGUS() char readbuffer[1024]; long size = data.GetLength(); long read = 0; - BYTE remap[256]; + uint8_t remap[256]; FString patches[256]; memset(remap, 255, sizeof(remap)); diff --git a/src/timidity/timidity.h b/src/sound/timidity/timidity.h similarity index 97% rename from src/timidity/timidity.h rename to src/sound/timidity/timidity.h index 28f4908afe..1c73b3dc68 100644 --- a/src/timidity/timidity.h +++ b/src/sound/timidity/timidity.h @@ -220,7 +220,7 @@ struct Sample { struct { - BYTE rate[6], offset[6]; + uint8_t rate[6], offset[6]; } gf1; struct { @@ -236,7 +236,7 @@ struct Sample int32_t tremolo_sweep_increment, tremolo_phase_increment, vibrato_sweep_increment, vibrato_control_ratio; - BYTE + uint8_t tremolo_depth, vibrato_depth, low_vel, high_vel, type; @@ -255,7 +255,7 @@ struct Sample // SF2 stuff int16_t tune; - SBYTE velocity; + int8_t velocity; float initial_attenuation; }; @@ -312,7 +312,7 @@ struct ToneBankElement FString name; int note, pan, fontbank, fontpreset, fontnote; - SBYTE strip_loop, strip_envelope, strip_tail; + int8_t strip_loop, strip_envelope, strip_tail; }; /* A hack to delay instrument loading until after reading the entire MIDI file. */ @@ -428,9 +428,9 @@ struct Channel bank, program, sustain, pitchbend, mono, /* one note only on this channel */ pitchsens; - BYTE + uint8_t volume, expression; - SBYTE + int8_t panning; uint16_t rpn, nrpn; @@ -445,8 +445,8 @@ struct Channel struct MinEnvelope { - BYTE stage; - BYTE bUpdating; + uint8_t stage; + uint8_t bUpdating; }; struct GF1Envelope : public MinEnvelope @@ -490,7 +490,7 @@ struct Envelope SF2Envelope sf2; }; - BYTE Type; + uint8_t Type; void Init(struct Renderer *song, struct Voice *v); bool Update(struct Voice *v) @@ -515,7 +515,7 @@ struct Envelope struct Voice { - BYTE + uint8_t status, channel, note, velocity; Sample *sample; float @@ -633,7 +633,7 @@ struct Renderer ~Renderer(); void HandleEvent(int status, int parm1, int parm2); - void HandleLongMessage(const BYTE *data, int len); + void HandleLongMessage(const uint8_t *data, int len); void HandleController(int chan, int ctrl, int val); void ComputeOutput(float *buffer, int num_samples); void MarkInstrument(int bank, int percussion, int instr); @@ -641,10 +641,10 @@ struct Renderer int load_missing_instruments(); int set_default_instrument(const char *name); - int convert_tremolo_sweep(BYTE sweep); - int convert_vibrato_sweep(BYTE sweep, int vib_control_ratio); - int convert_tremolo_rate(BYTE rate); - int convert_vibrato_rate(BYTE rate); + int convert_tremolo_sweep(uint8_t sweep); + int convert_vibrato_sweep(uint8_t sweep, int vib_control_ratio); + int convert_tremolo_rate(uint8_t rate); + int convert_vibrato_rate(uint8_t rate); void recompute_freq(int voice); void recompute_amp(Voice *v); diff --git a/src/wildmidi/common.h b/src/sound/wildmidi/common.h similarity index 100% rename from src/wildmidi/common.h rename to src/sound/wildmidi/common.h diff --git a/src/wildmidi/file_io.cpp b/src/sound/wildmidi/file_io.cpp similarity index 99% rename from src/wildmidi/file_io.cpp rename to src/sound/wildmidi/file_io.cpp index be34b46223..7dffff3874 100644 --- a/src/wildmidi/file_io.cpp +++ b/src/sound/wildmidi/file_io.cpp @@ -35,7 +35,7 @@ #include -#include "../files.h" +#include "files.h" #include "wm_error.h" #include "file_io.h" #include "pathexpander.h" diff --git a/src/wildmidi/file_io.h b/src/sound/wildmidi/file_io.h similarity index 100% rename from src/wildmidi/file_io.h rename to src/sound/wildmidi/file_io.h diff --git a/src/wildmidi/gus_pat.cpp b/src/sound/wildmidi/gus_pat.cpp similarity index 100% rename from src/wildmidi/gus_pat.cpp rename to src/sound/wildmidi/gus_pat.cpp diff --git a/src/wildmidi/gus_pat.h b/src/sound/wildmidi/gus_pat.h similarity index 100% rename from src/wildmidi/gus_pat.h rename to src/sound/wildmidi/gus_pat.h diff --git a/src/wildmidi/reverb.cpp b/src/sound/wildmidi/reverb.cpp similarity index 100% rename from src/wildmidi/reverb.cpp rename to src/sound/wildmidi/reverb.cpp diff --git a/src/wildmidi/reverb.h b/src/sound/wildmidi/reverb.h similarity index 100% rename from src/wildmidi/reverb.h rename to src/sound/wildmidi/reverb.h diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/sound/wildmidi/wildmidi_lib.cpp similarity index 100% rename from src/wildmidi/wildmidi_lib.cpp rename to src/sound/wildmidi/wildmidi_lib.cpp diff --git a/src/wildmidi/wildmidi_lib.h b/src/sound/wildmidi/wildmidi_lib.h similarity index 100% rename from src/wildmidi/wildmidi_lib.h rename to src/sound/wildmidi/wildmidi_lib.h diff --git a/src/wildmidi/wm_error.cpp b/src/sound/wildmidi/wm_error.cpp similarity index 100% rename from src/wildmidi/wm_error.cpp rename to src/sound/wildmidi/wm_error.cpp diff --git a/src/wildmidi/wm_error.h b/src/sound/wildmidi/wm_error.h similarity index 100% rename from src/wildmidi/wm_error.h rename to src/sound/wildmidi/wm_error.h diff --git a/src/statnums.h b/src/statnums.h index dd868bbe13..682366142a 100644 --- a/src/statnums.h +++ b/src/statnums.h @@ -59,7 +59,8 @@ enum STAT_LIGHTTRANSFER, // A sector light transfer. These must be ticked after the light effects!!! STAT_EARTHQUAKE, // Earthquake actors STAT_MAPMARKER, // Map marker actors - + STAT_DLIGHT, + STAT_DEFAULT = 100, // Thinkers go here unless specified otherwise. STAT_SECTOREFFECT, // All sector effects that cause floor and ceiling movement STAT_ACTORMOVER, // actor movers diff --git a/src/stats.h b/src/stats.h index c82410db10..df61dafb0f 100644 --- a/src/stats.h +++ b/src/stats.h @@ -124,10 +124,10 @@ inline unsigned __int64 rdtsc() return 0; } #else -inline unsigned long long rdtsc() +inline uint64_t rdtsc() { #ifdef __amd64__ - unsigned long long tsc; + uint64_t tsc; asm volatile ("rdtsc; shlq $32, %%rdx; orq %%rdx, %%rax" : "=a" (tsc) :: "%rdx"); return tsc; #elif defined __ppc__ @@ -142,7 +142,7 @@ inline unsigned long long rdtsc() #else // i386 if (CPU.bRDTSC) { - unsigned long long tsc; + uint64_t tsc; asm volatile ("\trdtsc\n" : "=A" (tsc)); return tsc; } @@ -167,13 +167,13 @@ public: void Clock() { - long long time = rdtsc(); + int64_t time = rdtsc(); Counter -= time; } void Unclock() { - long long time = rdtsc(); + int64_t time = rdtsc(); Counter += time; } @@ -187,13 +187,13 @@ public: return Counter * PerfToMillisec; } - long long GetRawCounter() + int64_t GetRawCounter() { return Counter; } private: - long long Counter; + int64_t Counter; }; #endif diff --git a/src/swrenderer/drawers/r_draw.cpp b/src/swrenderer/drawers/r_draw.cpp new file mode 100644 index 0000000000..a4467266e3 --- /dev/null +++ b/src/swrenderer/drawers/r_draw.cpp @@ -0,0 +1,164 @@ +/* +** r_draw.cpp +** +**--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2016 Magnus Norddahl +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include + +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_draw.h" +#include "r_draw_rgba.h" +#include "r_draw_pal.h" +#include "r_thread.h" +#include "swrenderer/scene/r_light.h" + +CVAR(Bool, r_dynlights, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +namespace swrenderer +{ + uint8_t shadetables[NUMCOLORMAPS * 16 * 256]; + FDynamicColormap ShadeFakeColormap[16]; + uint8_t identitymap[256]; + FDynamicColormap identitycolormap; + int fuzzoffset[FUZZTABLE + 1]; + int fuzzpos; + int fuzzviewheight; + + uint32_t particle_texture[PARTICLE_TEXTURE_SIZE * PARTICLE_TEXTURE_SIZE]; + + short zeroarray[MAXWIDTH]; + short screenheightarray[MAXWIDTH]; + + void R_InitShadeMaps() + { + int i, j; + // set up shading tables for shaded columns + // 16 colormap sets, progressing from full alpha to minimum visible alpha + + uint8_t *table = shadetables; + + // Full alpha + for (i = 0; i < 16; ++i) + { + ShadeFakeColormap[i].Color = ~0u; + ShadeFakeColormap[i].Desaturate = ~0u; + ShadeFakeColormap[i].Next = NULL; + ShadeFakeColormap[i].Maps = table; + + for (j = 0; j < NUMCOLORMAPS; ++j) + { + int a = (NUMCOLORMAPS - j) * 256 / NUMCOLORMAPS * (16 - i); + for (int k = 0; k < 256; ++k) + { + uint8_t v = (((k + 2) * a) + 256) >> 14; + table[k] = MIN(v, 64); + } + table += 256; + } + } + for (i = 0; i < NUMCOLORMAPS * 16 * 256; ++i) + { + assert(shadetables[i] <= 64); + } + + // Set up a guaranteed identity map + for (i = 0; i < 256; ++i) + { + identitymap[i] = i; + } + identitycolormap.Maps = identitymap; + } + + void R_InitFuzzTable(int fuzzoff) + { + /* + FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF + */ + + static const int8_t fuzzinit[FUZZTABLE] = { + 1,-1, 1,-1, 1, 1,-1, + 1, 1,-1, 1, 1, 1,-1, + 1, 1, 1,-1,-1,-1,-1, + 1,-1,-1, 1, 1, 1, 1,-1, + 1,-1, 1, 1,-1,-1, 1, + 1,-1,-1,-1,-1, 1, 1, + 1, 1,-1, 1, 1,-1, 1 + }; + + for (int i = 0; i < FUZZTABLE; i++) + { + fuzzoffset[i] = fuzzinit[i] * fuzzoff; + } + } + + void R_InitParticleTexture() + { + double center = PARTICLE_TEXTURE_SIZE * 0.5f; + for (int y = 0; y < PARTICLE_TEXTURE_SIZE; y++) + { + for (int x = 0; x < PARTICLE_TEXTURE_SIZE; x++) + { + double dx = (center - x - 0.5f) / center; + double dy = (center - y - 0.5f) / center; + double dist2 = dx * dx + dy * dy; + double alpha = clamp(1.1f - dist2 * 1.1f, 0.0f, 1.0f); + + particle_texture[x + y * PARTICLE_TEXTURE_SIZE] = (int)(alpha * 128.0f + 0.5f); + } + } + } + + void R_UpdateFuzzPos(const SpriteDrawerArgs &args) + { + int yl = MAX(args.FuzzY1(), 1); + int yh = MIN(args.FuzzY2(), fuzzviewheight); + if (yl <= yh) + fuzzpos = (fuzzpos + yh - yl + 1) % FUZZTABLE; + } +} diff --git a/src/swrenderer/drawers/r_draw.h b/src/swrenderer/drawers/r_draw.h new file mode 100644 index 0000000000..f4c7ec11e7 --- /dev/null +++ b/src/swrenderer/drawers/r_draw.h @@ -0,0 +1,99 @@ + +#pragma once + +#include "r_defs.h" +#include "c_cvars.h" +#include + +struct FSWColormap; +struct FLightNode; + +EXTERN_CVAR(Bool, r_multithreaded); +EXTERN_CVAR(Bool, r_magfilter); +EXTERN_CVAR(Bool, r_minfilter); +EXTERN_CVAR(Bool, r_mipmap); +EXTERN_CVAR(Float, r_lod_bias); +EXTERN_CVAR(Int, r_drawfuzz); +EXTERN_CVAR(Bool, r_drawtrans); +EXTERN_CVAR(Float, transsouls); +EXTERN_CVAR(Bool, r_dynlights); + +class DrawerCommandQueue; +typedef std::shared_ptr DrawerCommandQueuePtr; + +namespace swrenderer +{ + class DrawerArgs; + class SkyDrawerArgs; + class WallDrawerArgs; + class SpanDrawerArgs; + class SpriteDrawerArgs; + + extern uint8_t shadetables[/*NUMCOLORMAPS*16*256*/]; + extern FDynamicColormap ShadeFakeColormap[16]; + extern uint8_t identitymap[256]; + extern FDynamicColormap identitycolormap; + + // Constant arrays used for psprite clipping and initializing clipping. + extern short zeroarray[MAXWIDTH]; + extern short screenheightarray[MAXWIDTH]; + + // Spectre/Invisibility. + #define FUZZTABLE 50 + extern int fuzzoffset[FUZZTABLE + 1]; + extern int fuzzpos; + extern int fuzzviewheight; + + #define PARTICLE_TEXTURE_SIZE 64 + extern uint32_t particle_texture[PARTICLE_TEXTURE_SIZE * PARTICLE_TEXTURE_SIZE]; + + class SWPixelFormatDrawers + { + public: + SWPixelFormatDrawers(DrawerCommandQueuePtr queue) : Queue(queue) { } + virtual ~SWPixelFormatDrawers() { } + virtual void DrawWallColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallMaskedColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallAddColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallAddClampColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallSubClampColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallRevSubClampColumn(const WallDrawerArgs &args) = 0; + virtual void DrawSingleSkyColumn(const SkyDrawerArgs &args) = 0; + virtual void DrawDoubleSkyColumn(const SkyDrawerArgs &args) = 0; + virtual void DrawColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillAddColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillAddClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillSubClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillRevSubClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawFuzzColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawAddColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawTranslatedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawTranslatedAddColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawShadedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawAddClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawAddClampTranslatedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawSubClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawSubClampTranslatedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawRevSubClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawRevSubClampTranslatedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawSpan(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanMasked(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanTranslucent(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanMaskedTranslucent(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanAddClamp(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanMaskedAddClamp(const SpanDrawerArgs &args) = 0; + virtual void FillSpan(const SpanDrawerArgs &args) = 0; + virtual void DrawTiltedSpan(const SpanDrawerArgs &args, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) = 0; + virtual void DrawColoredSpan(const SpanDrawerArgs &args) = 0; + virtual void DrawFogBoundaryLine(const SpanDrawerArgs &args) = 0; + + DrawerCommandQueuePtr Queue; + }; + + void R_InitShadeMaps(); + void R_InitFuzzTable(int fuzzoff); + void R_InitParticleTexture(); + + void R_UpdateFuzzPos(const SpriteDrawerArgs &args); +} diff --git a/src/swrenderer/drawers/r_draw_pal.cpp b/src/swrenderer/drawers/r_draw_pal.cpp new file mode 100644 index 0000000000..5bfcf8d77c --- /dev/null +++ b/src/swrenderer/drawers/r_draw_pal.cpp @@ -0,0 +1,2946 @@ +/* +** r_draw_pal.cpp +** +**--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2016 Magnus Norddahl +** Copyright 2016 Rachael Alexanderson +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef __arm__ +#define NO_SSE +#endif + +#ifndef NO_SSE +#include +#endif +#include "templates.h" +#include "doomtype.h" +#include "doomdef.h" +#include "r_defs.h" +#include "r_draw.h" +#include "v_video.h" +#include "r_draw_pal.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" + +// [SP] r_blendmethod - false = rgb555 matching (ZDoom classic), true = rgb666 (refactored) +CVAR(Bool, r_blendmethod, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) + +/* + [RH] This translucency algorithm is based on DOSDoom 0.65's, but uses + a 32k RGB table instead of an 8k one. At least on my machine, it's + slightly faster (probably because it uses only one shift instead of + two), and it looks considerably less green at the ends of the + translucency range. The extra size doesn't appear to be an issue. + + The following note is from DOSDoom 0.65: + + New translucency algorithm, by Erik Sandberg: + + Basically, we compute the red, green and blue values for each pixel, and + then use a RGB table to check which one of the palette colours that best + represents those RGB values. The RGB table is 8k big, with 4 R-bits, + 5 G-bits and 4 B-bits. A 4k table gives a bit too bad precision, and a 32k + table takes up more memory and results in more cache misses, so an 8k + table seemed to be quite ultimate. + + The computation of the RGB for each pixel is accelerated by using two + 1k tables for each translucency level. + The xth element of one of these tables contains the r, g and b values for + the colour x, weighted for the current translucency level (for example, + the weighted rgb values for background colour at 75% translucency are 1/4 + of the original rgb values). The rgb values are stored as three + low-precision fixed point values, packed into one long per colour: + Bit 0-4: Frac part of blue (5 bits) + Bit 5-8: Int part of blue (4 bits) + Bit 9-13: Frac part of red (5 bits) + Bit 14-17: Int part of red (4 bits) + Bit 18-22: Frac part of green (5 bits) + Bit 23-27: Int part of green (5 bits) + Bit 28-31: All zeros (4 bits) + + The point of this format is that the two colours now can be added, and + then be converted to a RGB table index very easily: First, we just set + all the frac bits and the four upper zero bits to 1. It's now possible + to get the RGB table index by anding the current value >> 5 with the + current value >> 19. When asm-optimised, this should be the fastest + algorithm that uses RGB tables. +*/ + +namespace swrenderer +{ + PalWall1Command::PalWall1Command(const WallDrawerArgs &args) : args(args) + { + } + + uint8_t PalWall1Command::AddLights(const DrawerLight *lights, int num_lights, float viewpos_z, uint8_t fg, uint8_t material) + { + uint32_t lit_r = 0; + uint32_t lit_g = 0; + uint32_t lit_b = 0; + + for (int i = 0; i < num_lights; i++) + { + uint32_t light_color_r = RPART(lights[i].color); + uint32_t light_color_g = GPART(lights[i].color); + uint32_t light_color_b = BPART(lights[i].color); + + // L = light-pos + // dist = sqrt(dot(L, L)) + // distance_attenuation = 1 - MIN(dist * (1/radius), 1) + float Lxy2 = lights[i].x; // L.x*L.x + L.y*L.y + float Lz = lights[i].z - viewpos_z; + float dist2 = Lxy2 + Lz * Lz; +#ifdef NO_SSE + float rcp_dist = 1.0f / (dist2 * 0.01f); +#else + float rcp_dist = _mm_cvtss_f32(_mm_rsqrt_ss(_mm_load_ss(&dist2))); +#endif + float dist = dist2 * rcp_dist; + float distance_attenuation = (256.0f - MIN(dist * lights[i].radius, 256.0f)); + + // The simple light type + float simple_attenuation = distance_attenuation; + + // The point light type + // diffuse = dot(N,L) * attenuation + float point_attenuation = lights[i].y * rcp_dist * distance_attenuation; + uint32_t attenuation = (uint32_t)(lights[i].y == 0.0f ? simple_attenuation : point_attenuation); + + lit_r += (light_color_r * attenuation) >> 8; + lit_g += (light_color_g * attenuation) >> 8; + lit_b += (light_color_b * attenuation) >> 8; + } + + if (lit_r == 0 && lit_g == 0 && lit_b == 0) + return fg; + + uint32_t material_r = GPalette.BaseColors[material].r; + uint32_t material_g = GPalette.BaseColors[material].g; + uint32_t material_b = GPalette.BaseColors[material].b; + + lit_r = MIN(GPalette.BaseColors[fg].r + ((lit_r * material_r) >> 8), 255); + lit_g = MIN(GPalette.BaseColors[fg].g + ((lit_g * material_g) >> 8), 255); + lit_b = MIN(GPalette.BaseColors[fg].b + ((lit_b * material_b) >> 8), 255); + + return RGB256k.All[((lit_r >> 2) << 12) | ((lit_g >> 2) << 6) | (lit_b >> 2)]; + } + + void DrawWall1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(args.Viewport()); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (num_dynlights == 0) + { + do + { + *dest = colormap[source[frac >> bits]]; + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + do + { + *dest = AddLights(dynlights, num_dynlights, viewpos_z, colormap[source[frac >> bits]], source[frac >> bits]); + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallMasked1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(args.Viewport()); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (num_dynlights == 0) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + *dest = colormap[pix]; + } + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + *dest = AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix); + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallAdd1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(args.Viewport()); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (!r_blendmethod) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = colormap[pix]; + + uint32_t fg = fg2rgb[lit]; + uint32_t bg = bg2rgb[*dest]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + } + frac += fracstep; + dest += pitch; + } while (--count); + + } + else + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = colormap[pix]; + + uint32_t r = MIN(GPalette.BaseColors[lit].r + GPalette.BaseColors[*dest].r, 255); + uint32_t g = MIN(GPalette.BaseColors[lit].g + GPalette.BaseColors[*dest].g, 255); + uint32_t b = MIN(GPalette.BaseColors[lit].b + GPalette.BaseColors[*dest].b, 255); + *dest = RGB256k.RGB[r>>2][g>>2][b>>2]; + } + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallAddClamp1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(args.Viewport()); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + if (!r_blendmethod) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + uint32_t a = fg2rgb[lit] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + uint32_t r = MIN(GPalette.BaseColors[lit].r + GPalette.BaseColors[*dest].r, 255); + uint32_t g = MIN(GPalette.BaseColors[lit].g + GPalette.BaseColors[*dest].g, 255); + uint32_t b = MIN(GPalette.BaseColors[lit].b + GPalette.BaseColors[*dest].b, 255); + *dest = RGB256k.RGB[r>>2][g>>2][b>>2]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallSubClamp1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(args.Viewport()); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + if (!r_blendmethod) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + uint32_t a = (fg2rgb[lit] | 0x40100400) - bg2rgb[*dest]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + int r = clamp(-GPalette.BaseColors[lit].r + GPalette.BaseColors[*dest].r, 0, 255); + int g = clamp(-GPalette.BaseColors[lit].g + GPalette.BaseColors[*dest].g, 0, 255); + int b = clamp(-GPalette.BaseColors[lit].b + GPalette.BaseColors[*dest].b, 0, 255); + *dest = RGB256k.RGB[r>>2][g>>2][b>>2]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallRevSubClamp1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(args.Viewport()); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + if (!r_blendmethod) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[lit]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + int r = clamp(GPalette.BaseColors[lit].r - GPalette.BaseColors[*dest].r, 0, 255); + int g = clamp(GPalette.BaseColors[lit].g - GPalette.BaseColors[*dest].g, 0, 255); + int b = clamp(GPalette.BaseColors[lit].b - GPalette.BaseColors[*dest].b, 0, 255); + *dest = RGB256k.RGB[r>>2][g>>2][b>>2]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + ///////////////////////////////////////////////////////////////////////// + + PalSkyCommand::PalSkyCommand(const SkyDrawerArgs &args) : args(args) + { + } + + void DrawSingleSky1PalCommand::Execute(DrawerThread *thread) + { + uint8_t *dest = args.Dest(); + int count = args.Count(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + const uint8_t *source0 = args.FrontTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!args.FadeSky()) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + dest += pitch; + frac += fracstep; + } + + return; + } + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + + int solid_top_r = RPART(solid_top); + int solid_top_g = GPART(solid_top); + int solid_top_b = BPART(solid_top); + int solid_bottom_r = RPART(solid_bottom); + int solid_bottom_g = GPART(solid_bottom); + int solid_bottom_b = BPART(solid_bottom); + uint8_t solid_top_fill = RGB32k.RGB[(solid_top_r >> 3)][(solid_top_g >> 3)][(solid_top_b >> 3)]; + uint8_t solid_bottom_fill = RGB32k.RGB[(solid_bottom_r >> 3)][(solid_bottom_g >> 3)][(solid_bottom_b >> 3)]; + + const uint32_t *palette = (const uint32_t *)GPalette.BaseColors; + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top_fill; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + + uint32_t c = palette[fg]; + int alpha_top = MAX(MIN(frac >> (16 - start_fade), 256), 0); + int inv_alpha_top = 256 - alpha_top; + int c_red = RPART(c); + int c_green = GPART(c); + int c_blue = BPART(c); + c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; + c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; + c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; + *dest = RGB256k.RGB[(c_red >> 2)][(c_green >> 2)][(c_blue >> 2)]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + + uint32_t c = palette[fg]; + int alpha_bottom = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); + int inv_alpha_bottom = 256 - alpha_bottom; + int c_red = RPART(c); + int c_green = GPART(c); + int c_blue = BPART(c); + c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; + c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; + c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; + *dest = RGB256k.RGB[(c_red >> 2)][(c_green >> 2)][(c_blue >> 2)]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom_fill; + dest += pitch; + index += num_cores; + } + } + + void DrawDoubleSky1PalCommand::Execute(DrawerThread *thread) + { + uint8_t *dest = args.Dest(); + int count = args.Count(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + const uint8_t *source0 = args.FrontTexturePixels(); + const uint8_t *source1 = args.BackTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + uint32_t maxtextureheight1 = args.BackTextureHeight() - 1; + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!args.FadeSky()) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + *dest = fg; + dest += pitch; + frac += fracstep; + } + + return; + } + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + + int solid_top_r = RPART(solid_top); + int solid_top_g = GPART(solid_top); + int solid_top_b = BPART(solid_top); + int solid_bottom_r = RPART(solid_bottom); + int solid_bottom_g = GPART(solid_bottom); + int solid_bottom_b = BPART(solid_bottom); + uint8_t solid_top_fill = RGB32k.RGB[(solid_top_r >> 3)][(solid_top_g >> 3)][(solid_top_b >> 3)]; + uint8_t solid_bottom_fill = RGB32k.RGB[(solid_bottom_r >> 3)][(solid_bottom_g >> 3)][(solid_bottom_b >> 3)]; + + const uint32_t *palette = (const uint32_t *)GPalette.BaseColors; + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top_fill; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + uint32_t c = palette[fg]; + int alpha_top = MAX(MIN(frac >> (16 - start_fade), 256), 0); + int inv_alpha_top = 256 - alpha_top; + int c_red = RPART(c); + int c_green = GPART(c); + int c_blue = BPART(c); + c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; + c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; + c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; + *dest = RGB256k.RGB[(c_red >> 2)][(c_green >> 2)][(c_blue >> 2)]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + *dest = fg; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + uint32_t c = palette[fg]; + int alpha_bottom = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); + int inv_alpha_bottom = 256 - alpha_bottom; + int c_red = RPART(c); + int c_green = GPART(c); + int c_blue = BPART(c); + c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; + c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; + c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; + *dest = RGB256k.RGB[(c_red >> 2)][(c_green >> 2)][(c_blue >> 2)]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom_fill; + dest += pitch; + index += num_cores; + } + } + + ///////////////////////////////////////////////////////////////////////// + + PalColumnCommand::PalColumnCommand(const SpriteDrawerArgs &args) : args(args) + { + } + + uint8_t PalColumnCommand::AddLights(uint8_t fg, uint8_t material, uint32_t lit_r, uint32_t lit_g, uint32_t lit_b) + { + if (lit_r == 0 && lit_g == 0 && lit_b == 0) + return fg; + + uint32_t material_r = GPalette.BaseColors[material].r; + uint32_t material_g = GPalette.BaseColors[material].g; + uint32_t material_b = GPalette.BaseColors[material].b; + + lit_r = MIN(GPalette.BaseColors[fg].r + ((lit_r * material_r) >> 8), 255); + lit_g = MIN(GPalette.BaseColors[fg].g + ((lit_g * material_g) >> 8), 255); + lit_b = MIN(GPalette.BaseColors[fg].b + ((lit_b * material_b) >> 8), 255); + + return RGB256k.All[((lit_r >> 2) << 12) | ((lit_g >> 2) << 6) | (lit_b >> 2)]; + } + + void DrawColumnPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + + // Framebuffer destination address. + dest = args.Dest(); + + // Determine scaling, + // which is the only mapping to be done. + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + // [RH] Get local copies of these variables so that the compiler + // has a better chance of optimizing this well. + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *source = args.TexturePixels(); + + uint32_t dynlight = args.DynamicLight(); + if (dynlight == 0) + { + do + { + *dest = colormap[source[frac >> FRACBITS]]; + + dest += pitch; + frac += fracstep; + + } while (--count); + } + else + { + uint32_t lit_r = RPART(dynlight); + uint32_t lit_g = GPART(dynlight); + uint32_t lit_b = BPART(dynlight); + uint32_t light = 256 - (args.Light() >> (FRACBITS - 8)); + lit_r = MIN(light + lit_r, 256); + lit_g = MIN(light + lit_g, 256); + lit_b = MIN(light + lit_b, 256); + lit_r = lit_r - light; + lit_g = lit_g - light; + lit_b = lit_b - light; + + do + { + auto material = source[frac >> FRACBITS]; + auto fg = colormap[material]; + *dest = AddLights(fg, material, lit_r, lit_g, lit_b); + + dest += pitch; + frac += fracstep; + + } while (--count); + } + } + + void FillColumnPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + dest = args.Dest(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + uint8_t color = args.SolidColor(); + do + { + *dest = color; + dest += pitch; + } while (--count); + } + + void FillColumnAddPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + dest = args.Dest(); + uint32_t *bg2rgb; + uint32_t fg; + + bg2rgb = args.DestBlend(); + fg = args.SrcColorIndex(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + const PalEntry* pal = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t bg; + bg = (fg + bg2rgb[*dest]) | 0x1f07c1f; + *dest = RGB32k.All[bg & (bg >> 15)]; + dest += pitch; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + int src_r = ((srccolor >> 16) & 0xff) * srcalpha; + int src_g = ((srccolor >> 0) & 0xff) * srcalpha; + int src_b = ((srccolor >> 8) & 0xff) * srcalpha; + do + { + int r = clamp((src_r + pal[*dest].r * destalpha)>>18, 0, 255); + int g = clamp((src_g + pal[*dest].g * destalpha)>>18, 0, 255); + int b = clamp((src_b + pal[*dest].b * destalpha)>>18, 0, 255); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + } while (--count); + } + } + + void FillColumnAddClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + + dest = args.Dest(); + uint32_t *bg2rgb; + uint32_t fg; + + bg2rgb = args.DestBlend(); + fg = args.SrcColorIndex(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + const PalEntry* pal = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = fg + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + int src_r = ((srccolor >> 16) & 0xff) * srcalpha; + int src_g = ((srccolor >> 0) & 0xff) * srcalpha; + int src_b = ((srccolor >> 8) & 0xff) * srcalpha; + do + { + int r = clamp((src_r + pal[*dest].r * destalpha)>>18, 0, 255); + int g = clamp((src_g + pal[*dest].g * destalpha)>>18, 0, 255); + int b = clamp((src_b + pal[*dest].b * destalpha)>>18, 0, 255); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + } while (--count); + } + } + + void FillColumnSubClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + + dest = args.Dest(); + uint32_t *bg2rgb = args.DestBlend(); + uint32_t fg = args.SrcColorIndex(); + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + const PalEntry* palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = fg - bg2rgb[*dest]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int src_r = ((srccolor >> 16) & 0xff) * srcalpha; + int src_g = ((srccolor >> 0) & 0xff) * srcalpha; + int src_b = ((srccolor >> 8) & 0xff) * srcalpha; + int bg = *dest; + int r = MAX((-src_r + palette[bg].r * destalpha)>>18, 0); + int g = MAX((-src_g + palette[bg].g * destalpha)>>18, 0); + int b = MAX((-src_b + palette[bg].b * destalpha)>>18, 0); + + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + } while (--count); + } + } + + void FillColumnRevSubClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + if (count <= 0) + return; + + dest = args.Dest(); + uint32_t *bg2rgb = args.DestBlend(); + uint32_t fg = args.SrcColorIndex(); + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int src_r = ((srccolor >> 16) & 0xff) * srcalpha; + int src_g = ((srccolor >> 0) & 0xff) * srcalpha; + int src_b = ((srccolor >> 8) & 0xff) * srcalpha; + int bg = *dest; + int r = MAX((src_r - palette[bg].r * destalpha)>>18, 0); + int g = MAX((src_g - palette[bg].g * destalpha)>>18, 0); + int b = MAX((src_b - palette[bg].b * destalpha)>>18, 0); + + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + } while (--count); + } + } + + void DrawColumnAddPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *source = args.TexturePixels(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t fg = colormap[source[frac >> FRACBITS]]; + uint32_t bg = *dest; + + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + uint32_t fg = colormap[source[frac >> FRACBITS]]; + uint32_t bg = *dest; + uint32_t r = MIN((palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 63); + uint32_t g = MIN((palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 63); + uint32_t b = MIN((palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 63); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnTranslatedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t* dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + // [RH] Local copies of global vars to improve compiler optimizations + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *translation = args.TranslationMap(); + const uint8_t *source = args.TexturePixels(); + + do + { + *dest = colormap[translation[source[frac >> FRACBITS]]]; + dest += pitch; + + frac += fracstep; + } while (--count); + } + + void DrawColumnTlatedAddPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const uint8_t *translation = args.TranslationMap(); + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *source = args.TexturePixels(); + + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t fg = colormap[translation[source[frac >> FRACBITS]]]; + uint32_t bg = *dest; + + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + uint32_t fg = colormap[translation[source[frac >> FRACBITS]]]; + uint32_t bg = *dest; + uint32_t r = MIN((palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 63); + uint32_t g = MIN((palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 63); + uint32_t b = MIN((palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 63); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnShadedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac, fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *source = args.TexturePixels(); + const uint8_t *colormap = args.Colormap(args.Viewport()); + uint32_t *fgstart = &Col2RGB8[0][args.SolidColor()]; + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t val = colormap[source[frac >> FRACBITS]]; + uint32_t fg = fgstart[val << 8]; + val = (Col2RGB8[64 - val][*dest] + fg) | 0x1f07c1f; + *dest = RGB32k.All[val & (val >> 15)]; + + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + int color = args.SolidColor(); + do + { + uint32_t val = source[frac >> FRACBITS]; + + int r = (palette[*dest].r * (255-val) + palette[color].r * val) >> 10; + int g = (palette[*dest].g * (255-val) + palette[color].g * val) >> 10; + int b = (palette[*dest].b * (255-val) + palette[color].b * val) >> 10; + *dest = RGB256k.RGB[clamp(r,0,63)][clamp(g,0,63)][clamp(b,0,63)]; + + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnAddClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = fg2rgb[colormap[source[frac >> FRACBITS]]] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[source[frac >> FRACBITS]]; + int bg = *dest; + int r = MIN((palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 63); + int g = MIN((palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 63); + int b = MIN((palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 63); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnAddClampTranslatedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *translation = args.TranslationMap(); + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = fg2rgb[colormap[translation[source[frac >> FRACBITS]]]] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[(a >> 15) & a]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[translation[source[frac >> FRACBITS]]]; + int bg = *dest; + int r = MIN((palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 63); + int g = MIN((palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 63); + int b = MIN((palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 63); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnSubClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (fg2rgb[colormap[source[frac >> FRACBITS]]] | 0x40100400) - bg2rgb[*dest]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[source[frac >> FRACBITS]]; + int bg = *dest; + int r = MAX((palette[fg].r * srcalpha - palette[bg].r * destalpha)>>18, 0); + int g = MAX((palette[fg].g * srcalpha - palette[bg].g * destalpha)>>18, 0); + int b = MAX((palette[fg].b * srcalpha - palette[bg].b * destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnSubClampTranslatedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *translation = args.TranslationMap(); + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (fg2rgb[colormap[translation[source[frac >> FRACBITS]]]] | 0x40100400) - bg2rgb[*dest]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[(a >> 15) & a]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[translation[source[frac >> FRACBITS]]]; + int bg = *dest; + int r = MAX((palette[fg].r * srcalpha - palette[bg].r * destalpha)>>18, 0); + int g = MAX((palette[fg].g * srcalpha - palette[bg].g * destalpha)>>18, 0); + int b = MAX((palette[fg].b * srcalpha - palette[bg].b * destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnRevSubClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[source[frac >> FRACBITS]]]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[source[frac >> FRACBITS]]; + int bg = *dest; + int r = MAX((-palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 0); + int g = MAX((-palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 0); + int b = MAX((-palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnRevSubClampTranslatedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = args.Viewport()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *translation = args.TranslationMap(); + const uint8_t *colormap = args.Colormap(args.Viewport()); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[translation[source[frac >> FRACBITS]]]]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[(a >> 15) & a]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[translation[source[frac >> FRACBITS]]]; + int bg = *dest; + int r = MAX((-palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 0); + int g = MAX((-palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 0); + int b = MAX((-palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + ///////////////////////////////////////////////////////////////////////// + + DrawFuzzColumnPalCommand::DrawFuzzColumnPalCommand(const SpriteDrawerArgs &args) + { + _yl = args.FuzzY1(); + _yh = args.FuzzY2(); + _x = args.FuzzX(); + _destorg = args.Viewport()->GetDest(0, 0); + _pitch = args.Viewport()->RenderTarget->GetPitch(); + _fuzzpos = fuzzpos; + _fuzzviewheight = fuzzviewheight; + } + + void DrawFuzzColumnPalCommand::Execute(DrawerThread *thread) + { + int yl = MAX(_yl, 1); + int yh = MIN(_yh, _fuzzviewheight); + + int count = thread->count_for_thread(yl, yh - yl + 1); + + // Zero length. + if (count <= 0) + return; + + uint8_t *map = &NormalLight.Maps[6 * 256]; + + int pitch = _pitch; + uint8_t *dest = thread->dest_for_thread(yl, pitch, yl * pitch + _x + _destorg); + + pitch = pitch * thread->num_cores; + int fuzzstep = thread->num_cores; + int fuzz = (_fuzzpos + thread->skipped_by_thread(yl)) % FUZZTABLE; + + yl += thread->skipped_by_thread(yl); + + // Handle the case where we would go out of bounds at the top: + if (yl < fuzzstep) + { + uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep + pitch; + //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (pitch)) < viewheight); + + *dest = map[*srcdest]; + dest += pitch; + fuzz += fuzzstep; + fuzz %= FUZZTABLE; + + count--; + if (count == 0) + return; + } + + bool lowerbounds = (yl + (count + fuzzstep - 1) * fuzzstep > _fuzzviewheight); + if (lowerbounds) + count--; + + // Fuzz where fuzzoffset stays within bounds + while (count > 0) + { + int available = (FUZZTABLE - fuzz); + int next_wrap = available / fuzzstep; + if (available % fuzzstep != 0) + next_wrap++; + + int cnt = MIN(count, next_wrap); + count -= cnt; + do + { + uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep; + //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (pitch)) < viewheight); + + *dest = map[*srcdest]; + dest += pitch; + fuzz += fuzzstep; + } while (--cnt); + + fuzz %= FUZZTABLE; + } + + // Handle the case where we would go out of bounds at the bottom + if (lowerbounds) + { + uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep - pitch; + //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (pitch)) < viewheight); + + *dest = map[*srcdest]; + } + } + + ///////////////////////////////////////////////////////////////////////// + + PalSpanCommand::PalSpanCommand(const SpanDrawerArgs &args) + { + _source = args.TexturePixels(); + _colormap = args.Colormap(args.Viewport()); + _xfrac = args.TextureUPos(); + _yfrac = args.TextureVPos(); + _y = args.DestY(); + _x1 = args.DestX1(); + _x2 = args.DestX2(); + _dest = args.Viewport()->GetDest(_x1, _y); + _xstep = args.TextureUStep(); + _ystep = args.TextureVStep(); + _xbits = args.TextureWidthBits(); + _ybits = args.TextureHeightBits(); + _srcblend = args.SrcBlend(); + _destblend = args.DestBlend(); + _color = args.SolidColor(); + _srcalpha = args.SrcAlpha(); + _destalpha = args.DestAlpha(); + _dynlights = args.dc_lights; + _num_dynlights = args.dc_num_lights; + _viewpos_x = args.dc_viewpos.X; + _step_viewpos_x = args.dc_viewpos_step.X; + } + + uint8_t PalSpanCommand::AddLights(const DrawerLight *lights, int num_lights, float viewpos_x, uint8_t fg, uint8_t material) + { + uint32_t lit_r = 0; + uint32_t lit_g = 0; + uint32_t lit_b = 0; + + for (int i = 0; i < num_lights; i++) + { + uint32_t light_color_r = RPART(lights[i].color); + uint32_t light_color_g = GPART(lights[i].color); + uint32_t light_color_b = BPART(lights[i].color); + + // L = light-pos + // dist = sqrt(dot(L, L)) + // attenuation = 1 - MIN(dist * (1/radius), 1) + float Lyz2 = lights[i].y; // L.y*L.y + L.z*L.z + float Lx = lights[i].x - viewpos_x; + float dist2 = Lyz2 + Lx * Lx; +#ifdef NO_SSE + float rcp_dist = 1.0f / (dist2 * 0.01f); +#else + float rcp_dist = _mm_cvtss_f32(_mm_rsqrt_ss(_mm_load_ss(&dist2))); +#endif + float dist = dist2 * rcp_dist; + float distance_attenuation = (256.0f - MIN(dist * lights[i].radius, 256.0f)); + + // The simple light type + float simple_attenuation = distance_attenuation; + + // The point light type + // diffuse = dot(N,L) * attenuation + float point_attenuation = lights[i].z * rcp_dist * distance_attenuation; + uint32_t attenuation = (uint32_t)(lights[i].z == 0.0f ? simple_attenuation : point_attenuation); + + lit_r += (light_color_r * attenuation) >> 8; + lit_g += (light_color_g * attenuation) >> 8; + lit_b += (light_color_b * attenuation) >> 8; + } + + if (lit_r == 0 && lit_g == 0 && lit_b == 0) + return fg; + + uint32_t material_r = GPalette.BaseColors[material].r; + uint32_t material_g = GPalette.BaseColors[material].g; + uint32_t material_b = GPalette.BaseColors[material].b; + + lit_r = MIN(GPalette.BaseColors[fg].r + ((lit_r * material_r) >> 8), 255); + lit_g = MIN(GPalette.BaseColors[fg].g + ((lit_g * material_g) >> 8), 255); + lit_b = MIN(GPalette.BaseColors[fg].b + ((lit_b * material_b) >> 8), 255); + + return RGB256k.All[((lit_r >> 2) << 12) | ((lit_g >> 2) << 6) | (lit_b >> 2)]; + } + + void DrawSpanPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + if (_xbits == 6 && _ybits == 6 && num_dynlights == 0) + { + // 64x64 is the most common case by far, so special case it. + do + { + // Current texture index in u,v. + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + + // Lookup pixel from flat texture tile, + // re-index using light/colormap. + *dest++ = colormap[source[spot]]; + + // Next step in u,v. + xfrac += xstep; + yfrac += ystep; + } while (--count); + } + else if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + // Current texture index in u,v. + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + + // Lookup pixel from flat texture tile, + // re-index using light/colormap. + *dest++ = AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]); + + // Next step in u,v. + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + + do + { + // Current texture index in u,v. + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + + // Lookup pixel from flat texture tile, + // re-index using light/colormap. + *dest++ = AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]); + + // Next step in u,v. + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + + void DrawSpanMaskedPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + int texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + *dest = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + int texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + *dest = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + + void DrawSpanTranslucentPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + uint32_t *fg2rgb = _srcblend; + uint32_t *bg2rgb = _destblend; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + const PalEntry *palette = GPalette.BaseColors; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + if (!r_blendmethod) + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest++ = RGB32k.All[fg & (fg >> 15)]; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest++ = RGB32k.All[fg & (fg >> 15)]; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + else + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest++ = RGB256k.RGB[r][g][b]; + + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest++ = RGB256k.RGB[r][g][b]; + + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + } + + void DrawSpanMaskedTranslucentPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + uint32_t *fg2rgb = _srcblend; + uint32_t *bg2rgb = _destblend; + + const PalEntry *palette = GPalette.BaseColors; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + if (!r_blendmethod) + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + uint8_t texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + uint8_t texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + else + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + uint8_t texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + uint8_t texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + } + + void DrawSpanAddClampPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + uint32_t *fg2rgb = _srcblend; + uint32_t *bg2rgb = _destblend; + const PalEntry *palette = GPalette.BaseColors; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + if (!r_blendmethod) + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t a = fg2rgb[fg] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest++ = RGB32k.All[a & (a >> 15)]; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t a = fg2rgb[fg] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest++ = RGB32k.All[a & (a >> 15)]; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + else + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest++ = RGB256k.RGB[r][g][b]; + + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest++ = RGB256k.RGB[r][g][b]; + + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + } + + void DrawSpanMaskedAddClampPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + uint32_t *fg2rgb = _srcblend; + uint32_t *bg2rgb = _destblend; + const PalEntry *palette = GPalette.BaseColors; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + if (!r_blendmethod) + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + uint8_t texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t a = fg2rgb[fg] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + uint8_t texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t a = fg2rgb[fg] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + else + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + uint8_t texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + uint8_t texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + } + + void FillSpanPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + memset(_dest, _color, _x2 - _x1 + 1); + } + + ///////////////////////////////////////////////////////////////////////// + + DrawTiltedSpanPalCommand::DrawTiltedSpanPalCommand(const SpanDrawerArgs &args, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) + : plane_sz(plane_sz), plane_su(plane_su), plane_sv(plane_sv), plane_shade(plane_shade), planeshade(planeshade), planelightfloat(planelightfloat), pviewx(pviewx), pviewy(pviewy) + { + y = args.DestY(); + x1 = args.DestX1(); + x2 = args.DestX2(); + viewport = args.Viewport(); + _colormap = args.Colormap(args.Viewport()); + _dest = args.Viewport()->GetDest(x1, y); + _ybits = args.TextureHeightBits(); + _xbits = args.TextureWidthBits(); + _source = args.TexturePixels(); + basecolormapdata = basecolormap->Maps; + } + + void DrawTiltedSpanPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(y)) + return; + + const uint8_t **tiltlighting = thread->tiltlighting; + + int width = x2 - x1; + double iz, uz, vz; + uint8_t *fb; + uint32_t u, v; + int i; + + iz = plane_sz[2] + plane_sz[1] * (viewport->viewwindow.centery - y) + plane_sz[0] * (x1 - viewport->viewwindow.centerx); + + // Lighting is simple. It's just linear interpolation from start to end + if (plane_shade) + { + uz = (iz + plane_sz[0] * width) * planelightfloat; + vz = iz * planelightfloat; + CalcTiltedLighting(vz, uz, width, thread); + } + else + { + for (int i = 0; i < width; ++i) + { + tiltlighting[i] = _colormap; + } + } + + uz = plane_su[2] + plane_su[1] * (viewport->viewwindow.centery - y) + plane_su[0] * (x1 - viewport->viewwindow.centerx); + vz = plane_sv[2] + plane_sv[1] * (viewport->viewwindow.centery - y) + plane_sv[0] * (x1 - viewport->viewwindow.centerx); + + fb = _dest; + + uint8_t vshift = 32 - _ybits; + uint8_t ushift = vshift - _xbits; + int umask = ((1 << _xbits) - 1) << _ybits; + + #if 0 + // The "perfect" reference version of this routine. Pretty slow. + // Use it only to see how things are supposed to look. + i = 0; + do + { + double z = 1.f / iz; + + u = int64_t(uz*z) + pviewx; + v = int64_t(vz*z) + pviewy; + R_SetDSColorMapLight(tiltlighting[i], 0, 0); + fb[i++] = ds_colormap[ds_source[(v >> vshift) | ((u >> ushift) & umask)]]; + iz += plane_sz[0]; + uz += plane_su[0]; + vz += plane_sv[0]; + } while (--width >= 0); + #else + //#define SPANSIZE 32 + //#define INVSPAN 0.03125f + //#define SPANSIZE 8 + //#define INVSPAN 0.125f + #define SPANSIZE 16 + #define INVSPAN 0.0625f + + double startz = 1.f / iz; + double startu = uz*startz; + double startv = vz*startz; + double izstep, uzstep, vzstep; + + izstep = plane_sz[0] * SPANSIZE; + uzstep = plane_su[0] * SPANSIZE; + vzstep = plane_sv[0] * SPANSIZE; + x1 = 0; + width++; + + while (width >= SPANSIZE) + { + iz += izstep; + uz += uzstep; + vz += vzstep; + + double endz = 1.f / iz; + double endu = uz*endz; + double endv = vz*endz; + uint32_t stepu = (uint32_t)int64_t((endu - startu) * INVSPAN); + uint32_t stepv = (uint32_t)int64_t((endv - startv) * INVSPAN); + u = (uint32_t)(int64_t(startu) + pviewx); + v = (uint32_t)(int64_t(startv) + pviewy); + + for (i = SPANSIZE - 1; i >= 0; i--) + { + fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); + x1++; + u += stepu; + v += stepv; + } + startu = endu; + startv = endv; + width -= SPANSIZE; + } + if (width > 0) + { + if (width == 1) + { + u = (uint32_t)int64_t(startu); + v = (uint32_t)int64_t(startv); + fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); + } + else + { + double left = width; + iz += plane_sz[0] * left; + uz += plane_su[0] * left; + vz += plane_sv[0] * left; + + double endz = 1.f / iz; + double endu = uz*endz; + double endv = vz*endz; + left = 1.f / left; + uint32_t stepu = (uint32_t)int64_t((endu - startu) * left); + uint32_t stepv = (uint32_t)int64_t((endv - startv) * left); + u = (uint32_t)(int64_t(startu) + pviewx); + v = (uint32_t)(int64_t(startv) + pviewy); + + for (; width != 0; width--) + { + fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); + x1++; + u += stepu; + v += stepv; + } + } + } + #endif + } + + // Calculates the lighting for one row of a tilted plane. If the definition + // of GETPALOOKUP changes, this needs to change, too. + void DrawTiltedSpanPalCommand::CalcTiltedLighting(double lstart, double lend, int width, DrawerThread *thread) + { + const uint8_t **tiltlighting = thread->tiltlighting; + + uint8_t *lightstart = basecolormapdata + (GETPALOOKUP(lstart, planeshade) << COLORMAPSHIFT); + uint8_t *lightend = basecolormapdata + (GETPALOOKUP(lend, planeshade) << COLORMAPSHIFT); + + if (width == 0 || lightstart == lightend) + { + for (int i = 0; i <= width; i++) + { + tiltlighting[i] = lightstart; + } + } + else + { + double lstep = (lend - lstart) / width; + double lval = lstart; + for (int i = 0; i <= width; i++) + { + tiltlighting[i] = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); + lval += lstep; + } + } + } + + ///////////////////////////////////////////////////////////////////////// + + DrawColoredSpanPalCommand::DrawColoredSpanPalCommand(const SpanDrawerArgs &args) : PalSpanCommand(args) + { + y = args.DestY(); + x1 = args.DestX1(); + x2 = args.DestX2(); + color = args.SolidColor(); + dest = args.Viewport()->GetDest(x1, y); + } + + void DrawColoredSpanPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(y)) + return; + + memset(_dest, color, x2 - x1 + 1); + } + + ///////////////////////////////////////////////////////////////////////// + + DrawFogBoundaryLinePalCommand::DrawFogBoundaryLinePalCommand(const SpanDrawerArgs &args) : PalSpanCommand(args) + { + y = args.DestY(); + x1 = args.DestX1(); + x2 = args.DestX2(); + _colormap = args.Colormap(args.Viewport()); + _dest = args.Viewport()->GetDest(0, y); + } + + void DrawFogBoundaryLinePalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(y)) + return; + + const uint8_t *colormap = _colormap; + uint8_t *dest = _dest; + int x = x1; + do + { + dest[x] = colormap[dest[x]]; + } while (++x <= x2); + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawParticleColumnPalCommand::DrawParticleColumnPalCommand(uint8_t *dest, int dest_y, int pitch, int count, uint32_t fg, uint32_t alpha, uint32_t fracposx) + { + _dest = dest; + _pitch = pitch; + _count = count; + _fg = fg; + _alpha = alpha; + _fracposx = fracposx; + _dest_y = dest_y; + } + + void DrawParticleColumnPalCommand::Execute(DrawerThread *thread) + { + int count = thread->count_for_thread(_dest_y, _count); + if (count <= 0) + return; + + int pitch = _pitch; + uint8_t *dest = thread->dest_for_thread(_dest_y, pitch, _dest); + pitch = pitch * thread->num_cores; + + const uint32_t *source = &particle_texture[(_fracposx >> FRACBITS) * PARTICLE_TEXTURE_SIZE]; + uint32_t particle_alpha = _alpha; + + uint32_t fracstep = PARTICLE_TEXTURE_SIZE * FRACUNIT / _count; + uint32_t fracpos = fracstep * thread->skipped_by_thread(_dest_y) + fracstep / 2; + fracstep *= thread->num_cores; + + uint32_t fg_red = (_fg >> 16) & 0xff; + uint32_t fg_green = (_fg >> 8) & 0xff; + uint32_t fg_blue = _fg & 0xff; + + for (int y = 0; y < count; y++) + { + uint32_t alpha = (source[fracpos >> FRACBITS] * particle_alpha) >> 7; + uint32_t inv_alpha = 256 - alpha; + + int bg = *dest; + uint32_t bg_red = GPalette.BaseColors[bg].r; + uint32_t bg_green = GPalette.BaseColors[bg].g; + uint32_t bg_blue = GPalette.BaseColors[bg].b; + + uint32_t red = (fg_red * alpha + bg_red * inv_alpha) / 256; + uint32_t green = (fg_green * alpha + bg_green * inv_alpha) / 256; + uint32_t blue = (fg_blue * alpha + bg_blue * inv_alpha) / 256; + + *dest = RGB256k.All[((red >> 2) << 12) | ((green >> 2) << 6) | (blue >> 2)]; + dest += pitch; + fracpos += fracstep; + } + } + + FString DrawParticleColumnPalCommand::DebugInfo() + { + return "DrawParticle"; + } +} diff --git a/src/swrenderer/drawers/r_draw_pal.h b/src/swrenderer/drawers/r_draw_pal.h new file mode 100644 index 0000000000..cca11e9347 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_pal.h @@ -0,0 +1,261 @@ + +#pragma once + +#include "r_draw.h" +#include "v_palette.h" +#include "r_thread.h" +#include "swrenderer/viewport/r_skydrawer.h" +#include "swrenderer/viewport/r_spandrawer.h" +#include "swrenderer/viewport/r_walldrawer.h" +#include "swrenderer/viewport/r_spritedrawer.h" + +namespace swrenderer +{ + class PalWall1Command : public DrawerCommand + { + public: + PalWall1Command(const WallDrawerArgs &args); + FString DebugInfo() override { return "PalWallCommand"; } + + protected: + inline static uint8_t AddLights(const DrawerLight *lights, int num_lights, float viewpos_z, uint8_t fg, uint8_t material); + + WallDrawerArgs args; + }; + + class DrawWall1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallMasked1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallAdd1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallAddClamp1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallSubClamp1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallRevSubClamp1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + + class PalSkyCommand : public DrawerCommand + { + public: + PalSkyCommand(const SkyDrawerArgs &args); + FString DebugInfo() override { return "PalSkyCommand"; } + + protected: + SkyDrawerArgs args; + }; + + class DrawSingleSky1PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; + class DrawDoubleSky1PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; + + class PalColumnCommand : public DrawerCommand + { + public: + PalColumnCommand(const SpriteDrawerArgs &args); + FString DebugInfo() override { return "PalColumnCommand"; } + + protected: + uint8_t AddLights(uint8_t fg, uint8_t material, uint32_t lit_r, uint32_t lit_g, uint32_t lit_b); + + SpriteDrawerArgs args; + }; + + class DrawColumnPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnAddPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnAddClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnSubClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnRevSubClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnAddPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnTranslatedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnTlatedAddPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnShadedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnAddClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnAddClampTranslatedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnSubClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnSubClampTranslatedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnRevSubClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnRevSubClampTranslatedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + + class DrawFuzzColumnPalCommand : public DrawerCommand + { + public: + DrawFuzzColumnPalCommand(const SpriteDrawerArgs &args); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override { return "DrawFuzzColumnPalCommand"; } + + private: + int _yl; + int _yh; + int _x; + uint8_t *_destorg; + int _pitch; + int _fuzzpos; + int _fuzzviewheight; + }; + + class PalSpanCommand : public DrawerCommand + { + public: + PalSpanCommand(const SpanDrawerArgs &args); + FString DebugInfo() override { return "PalSpanCommand"; } + + protected: + inline static uint8_t AddLights(const DrawerLight *lights, int num_lights, float viewpos_x, uint8_t fg, uint8_t material); + + const uint8_t *_source; + const uint8_t *_colormap; + dsfixed_t _xfrac; + dsfixed_t _yfrac; + int _y; + int _x1; + int _x2; + uint8_t *_dest; + dsfixed_t _xstep; + dsfixed_t _ystep; + int _xbits; + int _ybits; + uint32_t *_srcblend; + uint32_t *_destblend; + int _color; + fixed_t _srcalpha; + fixed_t _destalpha; + DrawerLight *_dynlights; + int _num_dynlights; + float _viewpos_x; + float _step_viewpos_x; + }; + + class DrawSpanPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanMaskedPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanTranslucentPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanMaskedTranslucentPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanAddClampPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanMaskedAddClampPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class FillSpanPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + + class DrawTiltedSpanPalCommand : public DrawerCommand + { + public: + DrawTiltedSpanPalCommand(const SpanDrawerArgs &args, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override { return "DrawTiltedSpanPalCommand"; } + + private: + void CalcTiltedLighting(double lval, double lend, int width, DrawerThread *thread); + + int y; + int x1; + int x2; + FVector3 plane_sz; + FVector3 plane_su; + FVector3 plane_sv; + bool plane_shade; + int planeshade; + float planelightfloat; + fixed_t pviewx; + fixed_t pviewy; + + const uint8_t *_colormap; + uint8_t *_dest; + int _ybits; + int _xbits; + const uint8_t *_source; + uint8_t *basecolormapdata; + RenderViewport *viewport; + }; + + class DrawColoredSpanPalCommand : public PalSpanCommand + { + public: + DrawColoredSpanPalCommand(const SpanDrawerArgs &args); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override { return "DrawColoredSpanPalCommand"; } + + private: + int y; + int x1; + int x2; + int color; + uint8_t *dest; + }; + + class DrawFogBoundaryLinePalCommand : public PalSpanCommand + { + public: + DrawFogBoundaryLinePalCommand(const SpanDrawerArgs &args); + void Execute(DrawerThread *thread) override; + + private: + int y, x1, x2; + const uint8_t *_colormap; + uint8_t *_dest; + }; + + class DrawParticleColumnPalCommand : public DrawerCommand + { + public: + DrawParticleColumnPalCommand(uint8_t *dest, int dest_y, int pitch, int count, uint32_t fg, uint32_t alpha, uint32_t fracposx); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + + private: + uint8_t *_dest; + int _dest_y; + int _pitch; + int _count; + uint32_t _fg; + uint32_t _alpha; + uint32_t _fracposx; + }; + + class SWPalDrawers : public SWPixelFormatDrawers + { + public: + using SWPixelFormatDrawers::SWPixelFormatDrawers; + + void DrawWallColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + void DrawWallMaskedColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + + void DrawWallAddColumn(const WallDrawerArgs &args) override + { + if (args.dc_num_lights == 0) + Queue->Push(args); + else + Queue->Push(args); + } + + void DrawWallAddClampColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + void DrawWallSubClampColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + void DrawWallRevSubClampColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + void DrawSingleSkyColumn(const SkyDrawerArgs &args) override { Queue->Push(args); } + void DrawDoubleSkyColumn(const SkyDrawerArgs &args) override { Queue->Push(args); } + void DrawColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillAddColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillAddClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillSubClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillRevSubClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawFuzzColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); R_UpdateFuzzPos(args); } + void DrawAddColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawTranslatedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawTranslatedAddColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawShadedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawAddClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawAddClampTranslatedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawSubClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawSubClampTranslatedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawRevSubClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawRevSubClampTranslatedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawSpan(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanMasked(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanTranslucent(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanMaskedTranslucent(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanAddClamp(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanMaskedAddClamp(const SpanDrawerArgs &args) override { Queue->Push(args); } + void FillSpan(const SpanDrawerArgs &args) override { Queue->Push(args); } + + void DrawTiltedSpan(const SpanDrawerArgs &args, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) override + { + Queue->Push(args, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy, basecolormap); + } + + void DrawColoredSpan(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawFogBoundaryLine(const SpanDrawerArgs &args) override { Queue->Push(args); } + }; +} diff --git a/src/swrenderer/drawers/r_draw_rgba.cpp b/src/swrenderer/drawers/r_draw_rgba.cpp new file mode 100644 index 0000000000..daf4fba7f3 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_rgba.cpp @@ -0,0 +1,814 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// True color span/column drawing functions. +// +//----------------------------------------------------------------------------- + +#include + +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_draw_rgba.h" +#include "gl/data/gl_matrix.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" +#ifdef NO_SSE +#include "r_draw_wall32.h" +#include "r_draw_sprite32.h" +#include "r_draw_span32.h" +#include "r_draw_sky32.h" +#else +#include "r_draw_wall32_sse2.h" +#include "r_draw_sprite32_sse2.h" +#include "r_draw_span32_sse2.h" +#include "r_draw_sky32_sse2.h" +#endif + +#include "gi.h" +#include "stats.h" +#include "x86.h" +#include + +// Use linear filtering when scaling up +CVAR(Bool, r_magfilter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +// Use linear filtering when scaling down +CVAR(Bool, r_minfilter, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +// Use mipmapped textures +CVAR(Bool, r_mipmap, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +// Level of detail texture bias +CVAR(Float, r_lod_bias, -1.5, 0); // To do: add CVAR_ARCHIVE | CVAR_GLOBALCONFIG when a good default has been decided + +namespace swrenderer +{ + void SWTruecolorDrawers::DrawWallColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallMaskedColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallAddColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallAddClampColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallSubClampColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallRevSubClampColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillAddColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillAddClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillSubClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillRevSubClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawFuzzColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + R_UpdateFuzzPos(args); + } + + void SWTruecolorDrawers::DrawAddColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawTranslatedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawTranslatedAddColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawShadedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawAddClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawAddClampTranslatedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSubClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSubClampTranslatedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawRevSubClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawRevSubClampTranslatedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpan(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanMasked(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanTranslucent(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanMaskedTranslucent(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanAddClamp(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanMaskedAddClamp(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSingleSkyColumn(const SkyDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawDoubleSkyColumn(const SkyDrawerArgs &args) + { + Queue->Push(args); + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawFuzzColumnRGBACommand::DrawFuzzColumnRGBACommand(const SpriteDrawerArgs &drawerargs) + { + _x = drawerargs.FuzzX(); + _yl = drawerargs.FuzzY1(); + _yh = drawerargs.FuzzY2(); + _destorg = drawerargs.Viewport()->GetDest(0, 0); + _pitch = drawerargs.Viewport()->RenderTarget->GetPitch(); + _fuzzpos = fuzzpos; + _fuzzviewheight = fuzzviewheight; + } + + void DrawFuzzColumnRGBACommand::Execute(DrawerThread *thread) + { + int yl = MAX(_yl, 1); + int yh = MIN(_yh, _fuzzviewheight); + + int count = thread->count_for_thread(yl, yh - yl + 1); + + // Zero length. + if (count <= 0) + return; + + uint32_t *dest = thread->dest_for_thread(yl, _pitch, _pitch * yl + _x + (uint32_t*)_destorg); + + int pitch = _pitch * thread->num_cores; + int fuzzstep = thread->num_cores; + int fuzz = (_fuzzpos + thread->skipped_by_thread(yl)) % FUZZTABLE; + + yl += thread->skipped_by_thread(yl); + + // Handle the case where we would go out of bounds at the top: + if (yl < fuzzstep) + { + uint32_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep + pitch; + //assert(static_cast((srcdest - (uint32_t*)dc_destorg) / (_pitch)) < viewheight); + + uint32_t bg = *srcdest; + + uint32_t red = RPART(bg) * 3 / 4; + uint32_t green = GPART(bg) * 3 / 4; + uint32_t blue = BPART(bg) * 3 / 4; + + *dest = 0xff000000 | (red << 16) | (green << 8) | blue; + dest += pitch; + fuzz += fuzzstep; + fuzz %= FUZZTABLE; + + count--; + if (count == 0) + return; + } + + bool lowerbounds = (yl + (count + fuzzstep - 1) * fuzzstep > _fuzzviewheight); + if (lowerbounds) + count--; + + // Fuzz where fuzzoffset stays within bounds + while (count > 0) + { + int available = (FUZZTABLE - fuzz); + int next_wrap = available / fuzzstep; + if (available % fuzzstep != 0) + next_wrap++; + + int cnt = MIN(count, next_wrap); + count -= cnt; + do + { + uint32_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep; + //assert(static_cast((srcdest - (uint32_t*)dc_destorg) / (_pitch)) < viewheight); + + uint32_t bg = *srcdest; + + uint32_t red = RPART(bg) * 3 / 4; + uint32_t green = GPART(bg) * 3 / 4; + uint32_t blue = BPART(bg) * 3 / 4; + + *dest = 0xff000000 | (red << 16) | (green << 8) | blue; + dest += pitch; + fuzz += fuzzstep; + } while (--cnt); + + fuzz %= FUZZTABLE; + } + + // Handle the case where we would go out of bounds at the bottom + if (lowerbounds) + { + uint32_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep - pitch; + //assert(static_cast((srcdest - (uint32_t*)dc_destorg) / (_pitch)) < viewheight); + + uint32_t bg = *srcdest; + + uint32_t red = RPART(bg) * 3 / 4; + uint32_t green = GPART(bg) * 3 / 4; + uint32_t blue = BPART(bg) * 3 / 4; + + *dest = 0xff000000 | (red << 16) | (green << 8) | blue; + } + } + + FString DrawFuzzColumnRGBACommand::DebugInfo() + { + return "DrawFuzzColumn"; + } + + ///////////////////////////////////////////////////////////////////////////// + + FillSpanRGBACommand::FillSpanRGBACommand(const SpanDrawerArgs &drawerargs) + { + _x1 = drawerargs.DestX1(); + _x2 = drawerargs.DestX2(); + _y = drawerargs.DestY(); + _dest = drawerargs.Viewport()->GetDest(_x1, _y); + _light = drawerargs.Light(); + _color = drawerargs.SolidColor(); + } + + void FillSpanRGBACommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + uint32_t *dest = (uint32_t*)_dest; + int count = (_x2 - _x1 + 1); + uint32_t light = LightBgra::calc_light_multiplier(_light); + uint32_t color = LightBgra::shade_pal_index_simple(_color, light); + for (int i = 0; i < count; i++) + dest[i] = color; + } + + FString FillSpanRGBACommand::DebugInfo() + { + return "FillSpan"; + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawFogBoundaryLineRGBACommand::DrawFogBoundaryLineRGBACommand(const SpanDrawerArgs &drawerargs) + { + _y = drawerargs.DestY(); + _x = drawerargs.DestX1(); + _x2 = drawerargs.DestX2(); + _line = drawerargs.Viewport()->GetDest(0, _y); + _light = drawerargs.Light(); + _shade_constants = drawerargs.ColormapConstants(); + } + + void DrawFogBoundaryLineRGBACommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + int y = _y; + int x = _x; + int x2 = _x2; + + uint32_t *dest = (uint32_t*)_line; + + uint32_t light = LightBgra::calc_light_multiplier(_light); + ShadeConstants constants = _shade_constants; + + do + { + uint32_t red = (dest[x] >> 16) & 0xff; + uint32_t green = (dest[x] >> 8) & 0xff; + uint32_t blue = dest[x] & 0xff; + + if (constants.simple_shade) + { + red = red * light / 256; + green = green * light / 256; + blue = blue * light / 256; + } + else + { + uint32_t inv_light = 256 - light; + uint32_t inv_desaturate = 256 - constants.desaturate; + + uint32_t intensity = ((red * 77 + green * 143 + blue * 37) >> 8) * constants.desaturate; + + red = (red * inv_desaturate + intensity) / 256; + green = (green * inv_desaturate + intensity) / 256; + blue = (blue * inv_desaturate + intensity) / 256; + + red = (constants.fade_red * inv_light + red * light) / 256; + green = (constants.fade_green * inv_light + green * light) / 256; + blue = (constants.fade_blue * inv_light + blue * light) / 256; + + red = (red * constants.light_red) / 256; + green = (green * constants.light_green) / 256; + blue = (blue * constants.light_blue) / 256; + } + + dest[x] = 0xff000000 | (red << 16) | (green << 8) | blue; + } while (++x <= x2); + } + + FString DrawFogBoundaryLineRGBACommand::DebugInfo() + { + return "DrawFogBoundaryLine"; + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawTiltedSpanRGBACommand::DrawTiltedSpanRGBACommand(const SpanDrawerArgs &drawerargs, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy) + { + _x1 = drawerargs.DestX1(); + _x2 = drawerargs.DestX2(); + _y = drawerargs.DestY(); + _dest = drawerargs.Viewport()->GetDest(_x1, _y); + _light = drawerargs.Light(); + _shade_constants = drawerargs.ColormapConstants(); + _plane_sz = plane_sz; + _plane_su = plane_su; + _plane_sv = plane_sv; + _plane_shade = plane_shade; + _planeshade = planeshade; + _planelightfloat = planelightfloat; + _pviewx = pviewx; + _pviewy = pviewy; + _source = (const uint32_t*)drawerargs.TexturePixels(); + _xbits = drawerargs.TextureWidthBits(); + _ybits = drawerargs.TextureHeightBits(); + viewport = drawerargs.Viewport(); + } + + void DrawTiltedSpanRGBACommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + //#define SPANSIZE 32 + //#define INVSPAN 0.03125f + //#define SPANSIZE 8 + //#define INVSPAN 0.125f + #define SPANSIZE 16 + #define INVSPAN 0.0625f + + int source_width = 1 << _xbits; + int source_height = 1 << _ybits; + + uint32_t *dest = (uint32_t*)_dest; + int count = _x2 - _x1 + 1; + + // Depth (Z) change across the span + double iz = _plane_sz[2] + _plane_sz[1] * (viewport->viewwindow.centery - _y) + _plane_sz[0] * (_x1 - viewport->viewwindow.centerx); + + // Light change across the span + fixed_t lightstart = _light; + fixed_t lightend = lightstart; + if (_plane_shade) + { + double vis_start = iz * _planelightfloat; + double vis_end = (iz + _plane_sz[0] * count) * _planelightfloat; + + lightstart = LIGHTSCALE(vis_start, _planeshade); + lightend = LIGHTSCALE(vis_end, _planeshade); + } + fixed_t light = lightstart; + fixed_t steplight = (lightend - lightstart) / count; + + // Texture coordinates + double uz = _plane_su[2] + _plane_su[1] * (viewport->viewwindow.centery - _y) + _plane_su[0] * (_x1 - viewport->viewwindow.centerx); + double vz = _plane_sv[2] + _plane_sv[1] * (viewport->viewwindow.centery - _y) + _plane_sv[0] * (_x1 - viewport->viewwindow.centerx); + double startz = 1.f / iz; + double startu = uz*startz; + double startv = vz*startz; + double izstep = _plane_sz[0] * SPANSIZE; + double uzstep = _plane_su[0] * SPANSIZE; + double vzstep = _plane_sv[0] * SPANSIZE; + + // Linear interpolate in sizes of SPANSIZE to increase speed + while (count >= SPANSIZE) + { + iz += izstep; + uz += uzstep; + vz += vzstep; + + double endz = 1.f / iz; + double endu = uz*endz; + double endv = vz*endz; + uint32_t stepu = (uint32_t)(int64_t((endu - startu) * INVSPAN)); + uint32_t stepv = (uint32_t)(int64_t((endv - startv) * INVSPAN)); + uint32_t u = (uint32_t)(int64_t(startu) + _pviewx); + uint32_t v = (uint32_t)(int64_t(startv) + _pviewy); + + for (int i = 0; i < SPANSIZE; i++) + { + uint32_t sx = ((u >> 16) * source_width) >> 16; + uint32_t sy = ((v >> 16) * source_height) >> 16; + uint32_t fg = _source[sy + sx * source_height]; + + if (_shade_constants.simple_shade) + *(dest++) = LightBgra::shade_bgra_simple(fg, LightBgra::calc_light_multiplier(light)); + else + *(dest++) = LightBgra::shade_bgra(fg, LightBgra::calc_light_multiplier(light), _shade_constants); + + u += stepu; + v += stepv; + light += steplight; + } + startu = endu; + startv = endv; + count -= SPANSIZE; + } + + // The last few pixels at the end + while (count > 0) + { + double endz = 1.f / iz; + startu = uz*endz; + startv = vz*endz; + uint32_t u = (uint32_t)(int64_t(startu) + _pviewx); + uint32_t v = (uint32_t)(int64_t(startv) + _pviewy); + + uint32_t sx = ((u >> 16) * source_width) >> 16; + uint32_t sy = ((v >> 16) * source_height) >> 16; + uint32_t fg = _source[sy + sx * source_height]; + + if (_shade_constants.simple_shade) + *(dest++) = LightBgra::shade_bgra_simple(fg, LightBgra::calc_light_multiplier(light)); + else + *(dest++) = LightBgra::shade_bgra(fg, LightBgra::calc_light_multiplier(light), _shade_constants); + + iz += _plane_sz[0]; + uz += _plane_su[0]; + vz += _plane_sv[0]; + light += steplight; + count--; + } + } + + FString DrawTiltedSpanRGBACommand::DebugInfo() + { + return "DrawTiltedSpan"; + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawColoredSpanRGBACommand::DrawColoredSpanRGBACommand(const SpanDrawerArgs &drawerargs) + { + _y = drawerargs.DestY(); + _x1 = drawerargs.DestX1(); + _x2 = drawerargs.DestX2(); + _dest = drawerargs.Viewport()->GetDest(_x1, _y); + _light = drawerargs.Light(); + _color = drawerargs.SolidColor(); + } + + void DrawColoredSpanRGBACommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + int y = _y; + int x1 = _x1; + int x2 = _x2; + + uint32_t *dest = (uint32_t*)_dest; + int count = (x2 - x1 + 1); + uint32_t light = LightBgra::calc_light_multiplier(_light); + uint32_t color = LightBgra::shade_pal_index_simple(_color, light); + for (int i = 0; i < count; i++) + dest[i] = color; + } + + FString DrawColoredSpanRGBACommand::DebugInfo() + { + return "DrawColoredSpan"; + } + + ///////////////////////////////////////////////////////////////////////////// + + ApplySpecialColormapRGBACommand::ApplySpecialColormapRGBACommand(FSpecialColormap *colormap, DFrameBuffer *screen) + { + buffer = screen->GetBuffer(); + pitch = screen->GetPitch(); + width = screen->GetWidth(); + height = screen->GetHeight(); + + start_red = (int)(colormap->ColorizeStart[0] * 255); + start_green = (int)(colormap->ColorizeStart[1] * 255); + start_blue = (int)(colormap->ColorizeStart[2] * 255); + end_red = (int)(colormap->ColorizeEnd[0] * 255); + end_green = (int)(colormap->ColorizeEnd[1] * 255); + end_blue = (int)(colormap->ColorizeEnd[2] * 255); + } + +#ifdef NO_SSE + void ApplySpecialColormapRGBACommand::Execute(DrawerThread *thread) + { + int y = thread->skipped_by_thread(0); + int count = thread->count_for_thread(0, height); + while (count > 0) + { + uint8_t *pixels = buffer + y * pitch * 4; + for (int x = 0; x < width; x++) + { + int fg_red = pixels[2]; + int fg_green = pixels[1]; + int fg_blue = pixels[0]; + + int gray = (fg_red * 77 + fg_green * 143 + fg_blue * 37) >> 8; + gray += (gray >> 7); // gray*=256/255 + int inv_gray = 256 - gray; + + int red = clamp((start_red * inv_gray + end_red * gray) >> 8, 0, 255); + int green = clamp((start_green * inv_gray + end_green * gray) >> 8, 0, 255); + int blue = clamp((start_blue * inv_gray + end_blue * gray) >> 8, 0, 255); + + pixels[0] = (uint8_t)blue; + pixels[1] = (uint8_t)green; + pixels[2] = (uint8_t)red; + pixels[3] = 0xff; + + pixels += 4; + } + y += thread->num_cores; + count--; + } + } +#else + void ApplySpecialColormapRGBACommand::Execute(DrawerThread *thread) + { + int y = thread->skipped_by_thread(0); + int count = thread->count_for_thread(0, height); + __m128i gray_weight = _mm_set_epi16(256, 77, 143, 37, 256, 77, 143, 37); + __m128i start_end = _mm_set_epi16(255, start_red, start_green, start_blue, 255, end_red, end_green, end_blue); + while (count > 0) + { + uint8_t *pixels = buffer + y * pitch * 4; + int sse_length = width / 4; + for (int x = 0; x < sse_length; x++) + { + // Unpack to integers: + __m128i p = _mm_loadu_si128((const __m128i*)pixels); + + __m128i p16_0 = _mm_unpacklo_epi8(p, _mm_setzero_si128()); + __m128i p16_1 = _mm_unpackhi_epi8(p, _mm_setzero_si128()); + + // Add gray weighting to colors + __m128i mullo0 = _mm_mullo_epi16(p16_0, gray_weight); + __m128i mullo1 = _mm_mullo_epi16(p16_1, gray_weight); + __m128i p32_0 = _mm_unpacklo_epi16(mullo0, _mm_setzero_si128()); + __m128i p32_1 = _mm_unpackhi_epi16(mullo0, _mm_setzero_si128()); + __m128i p32_2 = _mm_unpacklo_epi16(mullo1, _mm_setzero_si128()); + __m128i p32_3 = _mm_unpackhi_epi16(mullo1, _mm_setzero_si128()); + + // Transpose to get color components in individual vectors: + __m128 tmpx = _mm_castsi128_ps(p32_0); + __m128 tmpy = _mm_castsi128_ps(p32_1); + __m128 tmpz = _mm_castsi128_ps(p32_2); + __m128 tmpw = _mm_castsi128_ps(p32_3); + _MM_TRANSPOSE4_PS(tmpx, tmpy, tmpz, tmpw); + __m128i blue = _mm_castps_si128(tmpx); + __m128i green = _mm_castps_si128(tmpy); + __m128i red = _mm_castps_si128(tmpz); + __m128i alpha = _mm_castps_si128(tmpw); + + // Calculate gray and 256-gray values: + __m128i gray = _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(red, green), blue), 8); + __m128i inv_gray = _mm_sub_epi32(_mm_set1_epi32(256), gray); + + // p32 = start * inv_gray + end * gray: + __m128i gray0 = _mm_shuffle_epi32(gray, _MM_SHUFFLE(0, 0, 0, 0)); + __m128i gray1 = _mm_shuffle_epi32(gray, _MM_SHUFFLE(1, 1, 1, 1)); + __m128i gray2 = _mm_shuffle_epi32(gray, _MM_SHUFFLE(2, 2, 2, 2)); + __m128i gray3 = _mm_shuffle_epi32(gray, _MM_SHUFFLE(3, 3, 3, 3)); + __m128i inv_gray0 = _mm_shuffle_epi32(inv_gray, _MM_SHUFFLE(0, 0, 0, 0)); + __m128i inv_gray1 = _mm_shuffle_epi32(inv_gray, _MM_SHUFFLE(1, 1, 1, 1)); + __m128i inv_gray2 = _mm_shuffle_epi32(inv_gray, _MM_SHUFFLE(2, 2, 2, 2)); + __m128i inv_gray3 = _mm_shuffle_epi32(inv_gray, _MM_SHUFFLE(3, 3, 3, 3)); + __m128i gray16_0 = _mm_packs_epi32(gray0, inv_gray0); + __m128i gray16_1 = _mm_packs_epi32(gray1, inv_gray1); + __m128i gray16_2 = _mm_packs_epi32(gray2, inv_gray2); + __m128i gray16_3 = _mm_packs_epi32(gray3, inv_gray3); + __m128i gray16_0_mullo = _mm_mullo_epi16(gray16_0, start_end); + __m128i gray16_1_mullo = _mm_mullo_epi16(gray16_1, start_end); + __m128i gray16_2_mullo = _mm_mullo_epi16(gray16_2, start_end); + __m128i gray16_3_mullo = _mm_mullo_epi16(gray16_3, start_end); + __m128i gray16_0_mulhi = _mm_mulhi_epi16(gray16_0, start_end); + __m128i gray16_1_mulhi = _mm_mulhi_epi16(gray16_1, start_end); + __m128i gray16_2_mulhi = _mm_mulhi_epi16(gray16_2, start_end); + __m128i gray16_3_mulhi = _mm_mulhi_epi16(gray16_3, start_end); + p32_0 = _mm_srli_epi32(_mm_add_epi32(_mm_unpacklo_epi16(gray16_0_mullo, gray16_0_mulhi), _mm_unpackhi_epi16(gray16_0_mullo, gray16_0_mulhi)), 8); + p32_1 = _mm_srli_epi32(_mm_add_epi32(_mm_unpacklo_epi16(gray16_1_mullo, gray16_1_mulhi), _mm_unpackhi_epi16(gray16_1_mullo, gray16_1_mulhi)), 8); + p32_2 = _mm_srli_epi32(_mm_add_epi32(_mm_unpacklo_epi16(gray16_2_mullo, gray16_2_mulhi), _mm_unpackhi_epi16(gray16_2_mullo, gray16_2_mulhi)), 8); + p32_3 = _mm_srli_epi32(_mm_add_epi32(_mm_unpacklo_epi16(gray16_3_mullo, gray16_3_mulhi), _mm_unpackhi_epi16(gray16_3_mullo, gray16_3_mulhi)), 8); + + p16_0 = _mm_packs_epi32(p32_0, p32_1); + p16_1 = _mm_packs_epi32(p32_2, p32_3); + p = _mm_packus_epi16(p16_0, p16_1); + + _mm_storeu_si128((__m128i*)pixels, p); + pixels += 16; + } + + for (int x = sse_length * 4; x < width; x++) + { + int fg_red = pixels[2]; + int fg_green = pixels[1]; + int fg_blue = pixels[0]; + + int gray = (fg_red * 77 + fg_green * 143 + fg_blue * 37) >> 8; + gray += (gray >> 7); // gray*=256/255 + int inv_gray = 256 - gray; + + int red = clamp((start_red * inv_gray + end_red * gray) >> 8, 0, 255); + int green = clamp((start_green * inv_gray + end_green * gray) >> 8, 0, 255); + int blue = clamp((start_blue * inv_gray + end_blue * gray) >> 8, 0, 255); + + pixels[0] = (uint8_t)blue; + pixels[1] = (uint8_t)green; + pixels[2] = (uint8_t)red; + pixels[3] = 0xff; + + pixels += 4; + } + + y += thread->num_cores; + count--; + } + } +#endif + + ///////////////////////////////////////////////////////////////////////////// + + DrawParticleColumnRGBACommand::DrawParticleColumnRGBACommand(uint32_t *dest, int dest_y, int pitch, int count, uint32_t fg, uint32_t alpha, uint32_t fracposx) + { + _dest = dest; + _pitch = pitch; + _count = count; + _fg = fg; + _alpha = alpha; + _fracposx = fracposx; + _dest_y = dest_y; + } + + void DrawParticleColumnRGBACommand::Execute(DrawerThread *thread) + { + int count = thread->count_for_thread(_dest_y, _count); + if (count <= 0) + return; + + uint32_t *dest = thread->dest_for_thread(_dest_y, _pitch, _dest); + int pitch = _pitch * thread->num_cores; + + const uint32_t *source = &particle_texture[(_fracposx >> FRACBITS) * PARTICLE_TEXTURE_SIZE]; + uint32_t particle_alpha = _alpha; + + uint32_t fracstep = PARTICLE_TEXTURE_SIZE * FRACUNIT / _count; + uint32_t fracpos = fracstep * thread->skipped_by_thread(_dest_y) + fracstep / 2; + fracstep *= thread->num_cores; + + uint32_t fg_red = (_fg >> 16) & 0xff; + uint32_t fg_green = (_fg >> 8) & 0xff; + uint32_t fg_blue = _fg & 0xff; + + for (int y = 0; y < count; y++) + { + uint32_t alpha = (source[fracpos >> FRACBITS] * particle_alpha) >> 7; + uint32_t inv_alpha = 256 - alpha; + + uint32_t bg_red = (*dest >> 16) & 0xff; + uint32_t bg_green = (*dest >> 8) & 0xff; + uint32_t bg_blue = (*dest) & 0xff; + + uint32_t red = (fg_red * alpha + bg_red * inv_alpha) / 256; + uint32_t green = (fg_green * alpha + bg_green * inv_alpha) / 256; + uint32_t blue = (fg_blue * alpha + bg_blue * inv_alpha) / 256; + + *dest = 0xff000000 | (red << 16) | (green << 8) | blue; + dest += pitch; + fracpos += fracstep; + } + } + + FString DrawParticleColumnRGBACommand::DebugInfo() + { + return "DrawParticle"; + } + +} diff --git a/src/swrenderer/drawers/r_draw_rgba.h b/src/swrenderer/drawers/r_draw_rgba.h new file mode 100644 index 0000000000..67768fb3b9 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_rgba.h @@ -0,0 +1,438 @@ +/* +** Drawer commands for the RT family of drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_draw.h" +#include "v_palette.h" +#include "r_thread.h" +#include "swrenderer/viewport/r_skydrawer.h" +#include "swrenderer/viewport/r_spandrawer.h" +#include "swrenderer/viewport/r_walldrawer.h" +#include "swrenderer/viewport/r_spritedrawer.h" + +#ifdef __arm__ +#define NO_SSE +#endif + +#ifndef NO_SSE +#include +#endif + +struct FSpecialColormap; + +EXTERN_CVAR(Bool, r_mipmap) +EXTERN_CVAR(Float, r_lod_bias) + +namespace swrenderer +{ + // Give the compiler a strong hint we want these functions inlined: + #ifndef FORCEINLINE + #if defined(_MSC_VER) + #define FORCEINLINE __forceinline + #elif defined(__GNUC__) + #define FORCEINLINE __attribute__((always_inline)) inline + #else + #define FORCEINLINE inline + #endif + #endif + + // Promise compiler we have no aliasing of this pointer + #ifndef RESTRICT + #if defined(_MSC_VER) + #define RESTRICT __restrict + #elif defined(__GNUC__) + #define RESTRICT __restrict__ + #else + #define RESTRICT + #endif + #endif + + // Force the compiler to use a calling convention that works for vector types + #if defined(_MSC_VER) + #define VECTORCALL __vectorcall + #else + #define VECTORCALL + #endif + + class DrawFuzzColumnRGBACommand : public DrawerCommand + { + int _x; + int _yl; + int _yh; + uint8_t * RESTRICT _destorg; + int _pitch; + int _fuzzpos; + int _fuzzviewheight; + + public: + DrawFuzzColumnRGBACommand(const SpriteDrawerArgs &drawerargs); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class FillSpanRGBACommand : public DrawerCommand + { + int _x1; + int _x2; + int _y; + uint8_t * RESTRICT _dest; + fixed_t _light; + int _color; + + public: + FillSpanRGBACommand(const SpanDrawerArgs &drawerargs); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class DrawFogBoundaryLineRGBACommand : public DrawerCommand + { + int _y; + int _x; + int _x2; + uint8_t * RESTRICT _line; + fixed_t _light; + ShadeConstants _shade_constants; + + public: + DrawFogBoundaryLineRGBACommand(const SpanDrawerArgs &drawerargs); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class DrawTiltedSpanRGBACommand : public DrawerCommand + { + int _x1; + int _x2; + int _y; + uint8_t * RESTRICT _dest; + fixed_t _light; + ShadeConstants _shade_constants; + FVector3 _plane_sz; + FVector3 _plane_su; + FVector3 _plane_sv; + bool _plane_shade; + int _planeshade; + float _planelightfloat; + fixed_t _pviewx; + fixed_t _pviewy; + int _xbits; + int _ybits; + const uint32_t * RESTRICT _source; + RenderViewport *viewport; + + public: + DrawTiltedSpanRGBACommand(const SpanDrawerArgs &drawerargs, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class DrawColoredSpanRGBACommand : public DrawerCommand + { + int _y; + int _x1; + int _x2; + uint8_t * RESTRICT _dest; + fixed_t _light; + int _color; + + public: + DrawColoredSpanRGBACommand(const SpanDrawerArgs &drawerargs); + + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class ApplySpecialColormapRGBACommand : public DrawerCommand + { + uint8_t *buffer; + int pitch; + int width; + int height; + int start_red; + int start_green; + int start_blue; + int end_red; + int end_green; + int end_blue; + + public: + ApplySpecialColormapRGBACommand(FSpecialColormap *colormap, DFrameBuffer *screen); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override { return "ApplySpecialColormapRGBACommand"; } + }; + + template + class DrawerBlendCommand : public CommandType + { + public: + void Execute(DrawerThread *thread) override + { + typename CommandType::LoopIterator loop(this, thread); + if (!loop) return; + BlendMode blend(*this, loop); + do + { + blend.Blend(*this, loop); + } while (loop.next()); + } + }; + + ///////////////////////////////////////////////////////////////////////////// + + class DrawParticleColumnRGBACommand : public DrawerCommand + { + public: + DrawParticleColumnRGBACommand(uint32_t *dest, int dest_y, int pitch, int count, uint32_t fg, uint32_t alpha, uint32_t fracposx); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + + private: + uint32_t *_dest; + int _dest_y; + int _pitch; + int _count; + uint32_t _fg; + uint32_t _alpha; + uint32_t _fracposx; + }; + + ///////////////////////////////////////////////////////////////////////////// + + class SWTruecolorDrawers : public SWPixelFormatDrawers + { + public: + using SWPixelFormatDrawers::SWPixelFormatDrawers; + + void DrawWallColumn(const WallDrawerArgs &args) override; + void DrawWallMaskedColumn(const WallDrawerArgs &args) override; + void DrawWallAddColumn(const WallDrawerArgs &args) override; + void DrawWallAddClampColumn(const WallDrawerArgs &args) override; + void DrawWallSubClampColumn(const WallDrawerArgs &args) override; + void DrawWallRevSubClampColumn(const WallDrawerArgs &args) override; + void DrawSingleSkyColumn(const SkyDrawerArgs &args) override; + void DrawDoubleSkyColumn(const SkyDrawerArgs &args) override; + void DrawColumn(const SpriteDrawerArgs &args) override; + void FillColumn(const SpriteDrawerArgs &args) override; + void FillAddColumn(const SpriteDrawerArgs &args) override; + void FillAddClampColumn(const SpriteDrawerArgs &args) override; + void FillSubClampColumn(const SpriteDrawerArgs &args) override; + void FillRevSubClampColumn(const SpriteDrawerArgs &args) override; + void DrawFuzzColumn(const SpriteDrawerArgs &args) override; + void DrawAddColumn(const SpriteDrawerArgs &args) override; + void DrawTranslatedColumn(const SpriteDrawerArgs &args) override; + void DrawTranslatedAddColumn(const SpriteDrawerArgs &args) override; + void DrawShadedColumn(const SpriteDrawerArgs &args) override; + void DrawAddClampColumn(const SpriteDrawerArgs &args) override; + void DrawAddClampTranslatedColumn(const SpriteDrawerArgs &args) override; + void DrawSubClampColumn(const SpriteDrawerArgs &args) override; + void DrawSubClampTranslatedColumn(const SpriteDrawerArgs &args) override; + void DrawRevSubClampColumn(const SpriteDrawerArgs &args) override; + void DrawRevSubClampTranslatedColumn(const SpriteDrawerArgs &args) override; + void DrawSpan(const SpanDrawerArgs &args) override; + void DrawSpanMasked(const SpanDrawerArgs &args) override; + void DrawSpanTranslucent(const SpanDrawerArgs &args) override; + void DrawSpanMaskedTranslucent(const SpanDrawerArgs &args) override; + void DrawSpanAddClamp(const SpanDrawerArgs &args) override; + void DrawSpanMaskedAddClamp(const SpanDrawerArgs &args) override; + void FillSpan(const SpanDrawerArgs &args) override { Queue->Push(args); } + + void DrawTiltedSpan(const SpanDrawerArgs &args, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) override + { + Queue->Push(args, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy); + } + + void DrawColoredSpan(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawFogBoundaryLine(const SpanDrawerArgs &args) override { Queue->Push(args); } + }; + + ///////////////////////////////////////////////////////////////////////////// + // Pixel shading inline functions: + + class LightBgra + { + public: + // calculates the light constant passed to the shade_pal_index function + FORCEINLINE static uint32_t calc_light_multiplier(dsfixed_t light) + { + return 256 - (light >> (FRACBITS - 8)); + } + + // Calculates a ARGB8 color for the given palette index and light multiplier + FORCEINLINE static uint32_t shade_pal_index_simple(uint32_t index, uint32_t light) + { + const PalEntry &color = GPalette.BaseColors[index]; + uint32_t red = color.r; + uint32_t green = color.g; + uint32_t blue = color.b; + + red = red * light / 256; + green = green * light / 256; + blue = blue * light / 256; + + return 0xff000000 | (red << 16) | (green << 8) | blue; + } + + // Calculates a ARGB8 color for the given palette index, light multiplier and dynamic colormap + FORCEINLINE static uint32_t shade_pal_index(uint32_t index, uint32_t light, const ShadeConstants &constants) + { + const PalEntry &color = GPalette.BaseColors[index]; + uint32_t alpha = color.d & 0xff000000; + uint32_t red = color.r; + uint32_t green = color.g; + uint32_t blue = color.b; + if (constants.simple_shade) + { + red = red * light / 256; + green = green * light / 256; + blue = blue * light / 256; + } + else + { + uint32_t inv_light = 256 - light; + uint32_t inv_desaturate = 256 - constants.desaturate; + + uint32_t intensity = ((red * 77 + green * 143 + blue * 37) >> 8) * constants.desaturate; + + red = (red * inv_desaturate + intensity) / 256; + green = (green * inv_desaturate + intensity) / 256; + blue = (blue * inv_desaturate + intensity) / 256; + + red = (constants.fade_red * inv_light + red * light) / 256; + green = (constants.fade_green * inv_light + green * light) / 256; + blue = (constants.fade_blue * inv_light + blue * light) / 256; + + red = (red * constants.light_red) / 256; + green = (green * constants.light_green) / 256; + blue = (blue * constants.light_blue) / 256; + } + return alpha | (red << 16) | (green << 8) | blue; + } + + FORCEINLINE static uint32_t shade_bgra_simple(uint32_t color, uint32_t light) + { + uint32_t red = RPART(color) * light / 256; + uint32_t green = GPART(color) * light / 256; + uint32_t blue = BPART(color) * light / 256; + return 0xff000000 | (red << 16) | (green << 8) | blue; + } + + FORCEINLINE static uint32_t shade_bgra(uint32_t color, uint32_t light, const ShadeConstants &constants) + { + uint32_t alpha = color & 0xff000000; + uint32_t red = (color >> 16) & 0xff; + uint32_t green = (color >> 8) & 0xff; + uint32_t blue = color & 0xff; + if (constants.simple_shade) + { + red = red * light / 256; + green = green * light / 256; + blue = blue * light / 256; + } + else + { + uint32_t inv_light = 256 - light; + uint32_t inv_desaturate = 256 - constants.desaturate; + + uint32_t intensity = ((red * 77 + green * 143 + blue * 37) >> 8) * constants.desaturate; + + red = (red * inv_desaturate + intensity) / 256; + green = (green * inv_desaturate + intensity) / 256; + blue = (blue * inv_desaturate + intensity) / 256; + + red = (constants.fade_red * inv_light + red * light) / 256; + green = (constants.fade_green * inv_light + green * light) / 256; + blue = (constants.fade_blue * inv_light + blue * light) / 256; + + red = (red * constants.light_red) / 256; + green = (green * constants.light_green) / 256; + blue = (blue * constants.light_blue) / 256; + } + return alpha | (red << 16) | (green << 8) | blue; + } + }; + + ///////////////////////////////////////////////////////////////////////////// + // Vector classes for non-SSE drawers that behave like their SSE counterparts + + namespace drawervectors + { + struct vec4ui + { + vec4ui() {} + vec4ui(uint32_t v) : a(v), r(v), g(v), b(v) { } + vec4ui(uint32_t a, uint32_t r, uint32_t g, uint32_t b) : a(a), r(r), g(g), b(b) { } + uint32_t a, r, g, b; + }; + + struct vec8us + { + vec8us() {} + vec8us(uint16_t v) : a0(v), r0(v), g0(v), b0(v) { } + vec8us(uint16_t a0, uint16_t r0, uint16_t g0, uint16_t b0, uint16_t a1, uint16_t r1, uint16_t g1, uint16_t b1) : a0(a0), r0(r0), g0(g0), b0(b0), a1(a1), r1(r1), g1(g1), b1(b1) { } + uint16_t a0, r0, g0, b0, a1, r1, g1, b1; + }; + + inline vec8us unpack(uint32_t lo, uint32_t hi) { return vec8us(APART(lo), RPART(lo), GPART(lo), BPART(lo), APART(hi), RPART(hi), GPART(hi), BPART(hi)); } + inline vec4ui unpacklo(vec8us v) { return vec4ui(v.a0, v.r0, v.g0, v.b0); } + inline vec4ui unpackhi(vec8us v) { return vec4ui(v.a1, v.r1, v.g1, v.b1); } + + inline vec8us pack(vec4ui lo, vec4ui hi) + { + return vec8us(lo.a, lo.r, lo.g, lo.b, hi.a, hi.r, hi.g, hi.b); + } + inline uint32_t packlo(vec8us v) + { + return MAKEARGB((uint32_t)clamp(v.a0, 0, 255), (uint32_t)clamp(v.r0, 0, 255), (uint32_t)clamp(v.g0, 0, 255), (uint32_t)clamp(v.b0, 0, 255)); + } + inline uint32_t packhi(vec8us v) + { + return MAKEARGB((uint32_t)clamp(v.a1, 0, 255), (uint32_t)clamp(v.r1, 0, 255), (uint32_t)clamp(v.g1, 0, 255), (uint32_t)clamp(v.b1, 0, 255)); + } + + inline vec8us operator+(vec8us a, vec8us b) + { + return vec8us(a.a0 + b.a0, a.r0 + b.r0, a.g0 + b.g0, a.b0 + b.b0, a.a1 + b.a1, a.r1 + b.r1, a.g1 + b.g1, a.b1 + b.b1); + } + + inline vec8us operator-(vec8us a, vec8us b) + { + return vec8us(a.a0 - b.a0, a.r0 - b.r0, a.g0 - b.g0, a.b0 - b.b0, a.a1 - b.a1, a.r1 - b.r1, a.g1 - b.g1, a.b1 - b.b1); + } + + inline vec8us operator*(vec8us a, vec8us b) + { + return vec8us(a.a0 * b.a0, a.r0 * b.r0, a.g0 * b.g0, a.b0 * b.b0, a.a1 * b.a1, a.r1 * b.r1, a.g1 * b.g1, a.b1 * b.b1); + } + + inline vec8us operator<<(vec8us a, int bits) + { + return vec8us(a.a0 << bits, a.r0 << bits, a.g0 << bits, a.b0 << bits, a.a1 << bits, a.r1 << bits, a.g1 << bits, a.b1 << bits); + } + + inline vec8us operator>>(vec8us a, int bits) + { + return vec8us(a.a0 >> bits, a.r0 >> bits, a.g0 >> bits, a.b0 >> bits, a.a1 >> bits, a.r1 >> bits, a.g1 >> bits, a.b1 >> bits); + } + } +} diff --git a/src/swrenderer/drawers/r_draw_sky32.h b/src/swrenderer/drawers/r_draw_sky32.h new file mode 100644 index 0000000000..ed34c340fa --- /dev/null +++ b/src/swrenderer/drawers/r_draw_sky32.h @@ -0,0 +1,319 @@ +/* +** Drawer commands for spans +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_skydrawer.h" + +namespace swrenderer +{ + + class DrawSkySingle32Command : public DrawerCommand + { + protected: + SkyDrawerArgs args; + + public: + DrawSkySingle32Command(const SkyDrawerArgs &args) : args(args) { } + + void Execute(DrawerThread *thread) override + { + using namespace drawervectors; + + uint32_t *dest = (uint32_t *)args.Dest(); + int count = args.Count(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + const uint32_t *source0 = (const uint32_t *)args.FrontTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + bool fadeSky = args.FadeSky(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!fadeSky) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + dest += pitch; + frac += fracstep; + } + + return; + } + + vec8us solid_top_fill = unpack(solid_top, 0); + vec8us solid_bottom_fill = unpack(solid_bottom, 0); + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + + vec8us alpha = MAX(MIN(frac >> (16 - start_fade), 256), 0); + vec8us inv_alpha = vec8us(256) - alpha; + + vec8us c = unpack(fg, 0); + c = (c * alpha + solid_top_fill * inv_alpha) >> 8; + *dest = packlo(c); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + + vec8us alpha = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); + vec8us inv_alpha = vec8us(256) - alpha; + + vec8us c = unpack(fg, 0); + c = (c * alpha + solid_top_fill * inv_alpha) >> 8; + *dest = packlo(c); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom; + dest += pitch; + index += num_cores; + } + } + + FString DebugInfo() override { return "DrawSkySingle32Command"; } + }; + + class DrawSkyDouble32Command : public DrawerCommand + { + protected: + SkyDrawerArgs args; + + public: + DrawSkyDouble32Command(const SkyDrawerArgs &args) : args(args) { } + + void Execute(DrawerThread *thread) override + { + using namespace drawervectors; + + uint32_t *dest = (uint32_t *)args.Dest(); + int count = args.Count(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + const uint32_t *source0 = (const uint32_t *)args.FrontTexturePixels(); + const uint32_t *source1 = (const uint32_t *)args.BackTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + uint32_t maxtextureheight1 = args.BackTextureHeight() - 1; + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + bool fadeSky = args.FadeSky(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!fadeSky) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + *dest = fg; + dest += pitch; + frac += fracstep; + } + + return; + } + + vec8us solid_top_fill = unpack(solid_top, 0); + vec8us solid_bottom_fill = unpack(solid_bottom, 0); + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + vec8us alpha = MAX(MIN(frac >> (16 - start_fade), 256), 0); + vec8us inv_alpha = vec8us(256) - alpha; + + vec8us c = unpack(fg, 0); + c = (c * alpha + solid_top_fill * inv_alpha) >> 8; + *dest = packlo(c); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + *dest = fg; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + vec8us alpha = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); + vec8us inv_alpha = vec8us(256) - alpha; + + vec8us c = unpack(fg, 0); + c = (c * alpha + solid_top_fill * inv_alpha) >> 8; + *dest = packlo(c); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom; + dest += pitch; + index += num_cores; + } + } + + FString DebugInfo() override { return "DrawSkyDouble32Command"; } + }; +} diff --git a/src/swrenderer/drawers/r_draw_sky32_sse2.h b/src/swrenderer/drawers/r_draw_sky32_sse2.h new file mode 100644 index 0000000000..997aac030f --- /dev/null +++ b/src/swrenderer/drawers/r_draw_sky32_sse2.h @@ -0,0 +1,314 @@ +/* +** Drawer commands for spans +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_skydrawer.h" + +namespace swrenderer +{ + class DrawSkySingle32Command : public DrawerCommand + { + protected: + SkyDrawerArgs args; + + public: + DrawSkySingle32Command(const SkyDrawerArgs &args) : args(args) { } + + void Execute(DrawerThread *thread) override + { + uint32_t *dest = (uint32_t *)args.Dest(); + int count = args.Count(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + const uint32_t *source0 = (const uint32_t *)args.FrontTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + bool fadeSky = args.FadeSky(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!fadeSky) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + dest += pitch; + frac += fracstep; + } + + return; + } + + __m128i solid_top_fill = _mm_unpacklo_epi8(_mm_cvtsi32_si128(solid_top), _mm_setzero_si128()); + __m128i solid_bottom_fill = _mm_unpacklo_epi8(_mm_cvtsi32_si128(solid_bottom), _mm_setzero_si128()); + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + + __m128i alpha = _mm_set1_epi16(MAX(MIN(frac >> (16 - start_fade), 256), 0)); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + __m128i c = _mm_unpacklo_epi8(_mm_cvtsi32_si128(fg), _mm_setzero_si128()); + c = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(c, alpha), _mm_mullo_epi16(solid_top_fill, inv_alpha)), 8); + *dest = _mm_cvtsi128_si32(_mm_packus_epi16(c, _mm_setzero_si128())); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + + __m128i alpha = _mm_set1_epi16(MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0)); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + __m128i c = _mm_unpacklo_epi8(_mm_cvtsi32_si128(fg), _mm_setzero_si128()); + c = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(c, alpha), _mm_mullo_epi16(solid_top_fill, inv_alpha)), 8); + *dest = _mm_cvtsi128_si32(_mm_packus_epi16(c, _mm_setzero_si128())); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom; + dest += pitch; + index += num_cores; + } + } + + FString DebugInfo() override { return "DrawSkySingle32Command"; } + }; + + class DrawSkyDouble32Command : public DrawerCommand + { + protected: + SkyDrawerArgs args; + + public: + DrawSkyDouble32Command(const SkyDrawerArgs &args) : args(args) { } + + void Execute(DrawerThread *thread) override + { + uint32_t *dest = (uint32_t *)args.Dest(); + int count = args.Count(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + const uint32_t *source0 = (const uint32_t *)args.FrontTexturePixels(); + const uint32_t *source1 = (const uint32_t *)args.BackTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + uint32_t maxtextureheight1 = args.BackTextureHeight() - 1; + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + bool fadeSky = args.FadeSky(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!fadeSky) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + *dest = fg; + dest += pitch; + frac += fracstep; + } + + return; + } + + __m128i solid_top_fill = _mm_unpacklo_epi8(_mm_cvtsi32_si128(solid_top), _mm_setzero_si128()); + __m128i solid_bottom_fill = _mm_unpacklo_epi8(_mm_cvtsi32_si128(solid_bottom), _mm_setzero_si128()); + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + __m128i alpha = _mm_set1_epi16(MAX(MIN(frac >> (16 - start_fade), 256), 0)); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + __m128i c = _mm_unpacklo_epi8(_mm_cvtsi32_si128(fg), _mm_setzero_si128()); + c = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(c, alpha), _mm_mullo_epi16(solid_top_fill, inv_alpha)), 8); + *dest = _mm_cvtsi128_si32(_mm_packus_epi16(c, _mm_setzero_si128())); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + *dest = fg; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + __m128i alpha = _mm_set1_epi16(MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0)); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + __m128i c = _mm_unpacklo_epi8(_mm_cvtsi32_si128(fg), _mm_setzero_si128()); + c = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(c, alpha), _mm_mullo_epi16(solid_top_fill, inv_alpha)), 8); + *dest = _mm_cvtsi128_si32(_mm_packus_epi16(c, _mm_setzero_si128())); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom; + dest += pitch; + index += num_cores; + } + } + + FString DebugInfo() override { return "DrawSkyDouble32Command"; } + }; +} diff --git a/src/swrenderer/drawers/r_draw_span32_sse2.h b/src/swrenderer/drawers/r_draw_span32_sse2.h new file mode 100644 index 0000000000..68d68ec82d --- /dev/null +++ b/src/swrenderer/drawers/r_draw_span32_sse2.h @@ -0,0 +1,517 @@ +/* +** Drawer commands for spans +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_spandrawer.h" + +namespace swrenderer +{ + namespace DrawSpan32TModes + { + enum class SpanBlendModes { Opaque, Masked, Translucent, AddClamp, SubClamp, RevSubClamp }; + struct OpaqueSpan { static const int Mode = (int)SpanBlendModes::Opaque; }; + struct MaskedSpan { static const int Mode = (int)SpanBlendModes::Masked; }; + struct TranslucentSpan { static const int Mode = (int)SpanBlendModes::Translucent; }; + struct AddClampSpan { static const int Mode = (int)SpanBlendModes::AddClamp; }; + struct SubClampSpan { static const int Mode = (int)SpanBlendModes::SubClamp; }; + struct RevSubClampSpan { static const int Mode = (int)SpanBlendModes::RevSubClamp; }; + + enum class FilterModes { Nearest, Linear }; + struct NearestFilter { static const int Mode = (int)FilterModes::Nearest; }; + struct LinearFilter { static const int Mode = (int)FilterModes::Linear; }; + + enum class ShadeMode { Simple, Advanced }; + struct SimpleShade { static const int Mode = (int)ShadeMode::Simple; }; + struct AdvancedShade { static const int Mode = (int)ShadeMode::Advanced; }; + + enum class SpanTextureSize { SizeAny, Size64x64 }; + struct TextureSizeAny { static const int Mode = (int)SpanTextureSize::SizeAny; }; + struct TextureSize64x64 { static const int Mode = (int)SpanTextureSize::Size64x64; }; + } + + template + class DrawSpan32T : public DrawerCommand + { + protected: + SpanDrawerArgs args; + + public: + DrawSpan32T(const SpanDrawerArgs &drawerargs) : args(drawerargs) { } + + struct TextureData + { + uint32_t xbits; + uint32_t ybits; + uint32_t xstep; + uint32_t ystep; + uint32_t xfrac; + uint32_t yfrac; + uint32_t yshift; + uint32_t xshift; + uint32_t xmask; + const uint32_t *source; + }; + + void Execute(DrawerThread *thread) override + { + using namespace DrawSpan32TModes; + + if (thread->line_skipped_by_thread(args.DestY())) return; + + TextureData texdata; + texdata.xbits = args.TextureWidthBits(); + texdata.ybits = args.TextureHeightBits(); + texdata.xstep = args.TextureUStep(); + texdata.ystep = args.TextureVStep(); + texdata.xfrac = args.TextureUPos(); + texdata.yfrac = args.TextureVPos(); + texdata.yshift = 32 - texdata.ybits; + texdata.xshift = texdata.yshift - texdata.xbits; + texdata.xmask = ((1 << texdata.xbits) - 1) << texdata.ybits; + + texdata.source = (const uint32_t*)args.TexturePixels(); + + double lod = args.TextureLOD(); + bool mipmapped = args.MipmappedTexture(); + + bool magnifying = lod < 0.0; + if (r_mipmap && mipmapped) + { + int level = (int)lod; + while (level > 0) + { + if (texdata.xbits <= 2 || texdata.ybits <= 2) + break; + + texdata.source += (1 << (texdata.xbits)) * (1 << (texdata.ybits)); + texdata.xbits -= 1; + texdata.ybits -= 1; + level--; + } + } + + bool is_nearest_filter = !((magnifying && r_magfilter) || (!magnifying && r_minfilter)); + bool is_64x64 = texdata.xbits == 6 && texdata.ybits == 6; + + auto shade_constants = args.ColormapConstants(); + if (shade_constants.simple_shade) + { + if (is_nearest_filter) + { + if (is_64x64) + Loop(thread, texdata, shade_constants); + else + Loop(thread, texdata, shade_constants); + } + else + { + if (is_64x64) + Loop(thread, texdata, shade_constants); + else + Loop(thread, texdata, shade_constants); + } + } + else + { + if (is_nearest_filter) + { + if (is_64x64) + Loop(thread, texdata, shade_constants); + else + Loop(thread, texdata, shade_constants); + } + else + { + if (is_64x64) + Loop(thread, texdata, shade_constants); + else + Loop(thread, texdata, shade_constants); + } + } + } + + template + FORCEINLINE void VECTORCALL Loop(DrawerThread *thread, TextureData texdata, ShadeConstants shade_constants) + { + using namespace DrawSpan32TModes; + + // Shade constants + int light = 256 - (args.Light() >> (FRACBITS - 8)); + __m128i mlight = _mm_set_epi16(256, light, light, light, 256, light, light, light); + __m128i inv_light = _mm_set_epi16(0, 256 - light, 256 - light, 256 - light, 0, 256 - light, 256 - light, 256 - light); + + __m128i inv_desaturate, shade_fade, shade_light; + int desaturate; + if (ShadeModeT::Mode == (int)ShadeMode::Advanced) + { + inv_desaturate = _mm_setr_epi16(256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate); + shade_fade = _mm_set_epi16(shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue, shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue); + shade_fade = _mm_mullo_epi16(shade_fade, inv_light); + shade_light = _mm_set_epi16(shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue, shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue); + desaturate = shade_constants.desaturate; + } + else + { + inv_desaturate = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_light = _mm_setzero_si128(); + desaturate = 0; + } + + auto lights = args.dc_lights; + auto num_lights = args.dc_num_lights; + float vpx = args.dc_viewpos.X; + float stepvpx = args.dc_viewpos_step.X; + __m128 viewpos_x = _mm_setr_ps(vpx, vpx + stepvpx, 0.0f, 0.0f); + __m128 step_viewpos_x = _mm_set1_ps(stepvpx * 2.0f); + + int count = args.DestX2() - args.DestX1() + 1; + int pitch = args.Viewport()->RenderTarget->GetPitch(); + uint32_t *dest = (uint32_t*)args.Viewport()->GetDest(args.DestX1(), args.DestY()); + + if (FilterModeT::Mode == (int)FilterModes::Linear) + { + texdata.xfrac -= 1 << (31 - texdata.xbits); + texdata.yfrac -= 1 << (31 - texdata.ybits); + } + + uint32_t srcalpha = args.SrcAlpha() >> (FRACBITS - 8); + uint32_t destalpha = args.DestAlpha() >> (FRACBITS - 8); + + int ssecount = count / 2; + for (int index = 0; index < ssecount; index++) + { + int offset = index * 2; + + __m128i bgcolor; + if (BlendT::Mode != (int)SpanBlendModes::Opaque) + { + bgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(dest + offset)), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + unsigned int ifgcolor[2]; + ifgcolor[0] = Sample(texdata.xbits, texdata.ybits, texdata.xstep, texdata.ystep, texdata.xfrac, texdata.yfrac, texdata.yshift, texdata.xshift, texdata.xmask, texdata.source); + texdata.xfrac += texdata.xstep; + texdata.yfrac += texdata.ystep; + + ifgcolor[1] = Sample(texdata.xbits, texdata.ybits, texdata.xstep, texdata.ystep, texdata.xfrac, texdata.yfrac, texdata.yshift, texdata.xshift, texdata.xmask, texdata.source); + texdata.xfrac += texdata.xstep; + texdata.yfrac += texdata.ystep; + + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lights, num_lights, viewpos_x); + __m128i outcolor = Blend(fgcolor, bgcolor, srcalpha, destalpha, ifgcolor[0], ifgcolor[1]); + + _mm_storel_epi64((__m128i*)(dest + offset), outcolor); + viewpos_x = _mm_add_ps(viewpos_x, step_viewpos_x); + } + + if (ssecount * 2 != count) + { + int index = ssecount * 2; + int offset = index; + + __m128i bgcolor; + if (BlendT::Mode != (int)SpanBlendModes::Opaque) + { + bgcolor = _mm_unpacklo_epi8(_mm_cvtsi32_si128(dest[offset]), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + // Sample + unsigned int ifgcolor[2]; + ifgcolor[0] = Sample(texdata.xbits, texdata.ybits, texdata.xstep, texdata.ystep, texdata.xfrac, texdata.yfrac, texdata.yshift, texdata.xshift, texdata.xmask, texdata.source); + ifgcolor[1] = 0; + + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lights, num_lights, viewpos_x); + __m128i outcolor = Blend(fgcolor, bgcolor, srcalpha, destalpha, ifgcolor[0], ifgcolor[1]); + + dest[offset] = _mm_cvtsi128_si32(outcolor); + } + + } + + template + FORCEINLINE unsigned int VECTORCALL Sample(uint32_t xbits, uint32_t ybits, uint32_t xstep, uint32_t ystep, uint32_t xfrac, uint32_t yfrac, uint32_t yshift, uint32_t xshift, uint32_t xmask, const uint32_t *source) + { + using namespace DrawSpan32TModes; + + if (FilterModeT::Mode == (int)FilterModes::Nearest && TextureSizeT::Mode == (int)SpanTextureSize::Size64x64) + { + int sample_index = ((xfrac >> (32 - 6 - 6)) & (63 * 64)) + (yfrac >> (32 - 6)); + return source[sample_index]; + } + else if (FilterModeT::Mode == (int)FilterModes::Nearest) + { + int sample_index = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + return source[sample_index]; + } + else + { + uint32_t xxbits, yybits; + if (TextureSizeT::Mode == (int)SpanTextureSize::Size64x64) + { + xxbits = 26; + yybits = 26; + } + else + { + xxbits = 32 - xbits; + yybits = 32 - ybits; + } + + uint32_t xxshift = (32 - xxbits); + uint32_t yyshift = (32 - yybits); + uint32_t xxmask = (1 << xxshift) - 1; + uint32_t yymask = (1 << yyshift) - 1; + uint32_t x = xfrac >> xxbits; + uint32_t y = yfrac >> yybits; + + uint32_t p00 = source[((y & yymask) + ((x & xxmask) << yyshift))]; + uint32_t p01 = source[(((y + 1) & yymask) + ((x & xxmask) << yyshift))]; + uint32_t p10 = source[((y & yymask) + (((x + 1) & xxmask) << yyshift))]; + uint32_t p11 = source[(((y + 1) & yymask) + (((x + 1) & xxmask) << yyshift))]; + + uint32_t inv_b = (xfrac >> (xxbits - 4)) & 15; + uint32_t inv_a = (yfrac >> (yybits - 4)) & 15; + uint32_t a = 16 - inv_a; + uint32_t b = 16 - inv_b; + + uint32_t sred = (RPART(p00) * (a * b) + RPART(p01) * (inv_a * b) + RPART(p10) * (a * inv_b) + RPART(p11) * (inv_a * inv_b) + 127) >> 8; + uint32_t sgreen = (GPART(p00) * (a * b) + GPART(p01) * (inv_a * b) + GPART(p10) * (a * inv_b) + GPART(p11) * (inv_a * inv_b) + 127) >> 8; + uint32_t sblue = (BPART(p00) * (a * b) + BPART(p01) * (inv_a * b) + BPART(p10) * (a * inv_b) + BPART(p11) * (inv_a * inv_b) + 127) >> 8; + uint32_t salpha = (APART(p00) * (a * b) + APART(p01) * (inv_a * b) + APART(p10) * (a * inv_b) + APART(p11) * (inv_a * inv_b) + 127) >> 8; + + return (salpha << 24) | (sred << 16) | (sgreen << 8) | sblue; + } + } + + template + FORCEINLINE __m128i VECTORCALL Shade(__m128i fgcolor, __m128i mlight, unsigned int ifgcolor0, unsigned int ifgcolor1, int desaturate, __m128i inv_desaturate, __m128i shade_fade, __m128i shade_light, const DrawerLight *lights, int num_lights, __m128 viewpos_x) + { + using namespace DrawSpan32TModes; + + __m128i material = fgcolor; + if (ShadeModeT::Mode == (int)ShadeMode::Simple) + { + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, mlight), 8); + } + else + { + int blue0 = BPART(ifgcolor0); + int green0 = GPART(ifgcolor0); + int red0 = RPART(ifgcolor0); + int intensity0 = ((red0 * 77 + green0 * 143 + blue0 * 37) >> 8) * desaturate; + + int blue1 = BPART(ifgcolor1); + int green1 = GPART(ifgcolor1); + int red1 = RPART(ifgcolor1); + int intensity1 = ((red1 * 77 + green1 * 143 + blue1 * 37) >> 8) * desaturate; + + __m128i intensity = _mm_set_epi16(0, intensity1, intensity1, intensity1, 0, intensity0, intensity0, intensity0); + + fgcolor = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(fgcolor, inv_desaturate), intensity), 8); + fgcolor = _mm_mullo_epi16(fgcolor, mlight); + fgcolor = _mm_srli_epi16(_mm_add_epi16(shade_fade, fgcolor), 8); + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, shade_light), 8); + } + + return AddLights(material, fgcolor, lights, num_lights, viewpos_x); + } + + FORCEINLINE __m128i VECTORCALL AddLights(__m128i material, __m128i fgcolor, const DrawerLight *lights, int num_lights, __m128 viewpos_x) + { + using namespace DrawSpan32TModes; + + __m128i lit = _mm_setzero_si128(); + + for (int i = 0; i != num_lights; i++) + { + __m128 light_x = _mm_set1_ps(lights[i].x); + __m128 light_y = _mm_set1_ps(lights[i].y); + __m128 light_z = _mm_set1_ps(lights[i].z); + __m128 light_radius = _mm_set1_ps(lights[i].radius); + __m128 m256 = _mm_set1_ps(256.0f); + + // L = light-pos + // dist = sqrt(dot(L, L)) + // distance_attenuation = 1 - MIN(dist * (1/radius), 1) + __m128 Lyz2 = light_y; // L.y*L.y + L.z*L.z + __m128 Lx = _mm_sub_ps(light_x, viewpos_x); + __m128 dist2 = _mm_add_ps(Lyz2, _mm_mul_ps(Lx, Lx)); + __m128 rcp_dist = _mm_rsqrt_ps(dist2); + __m128 dist = _mm_mul_ps(dist2, rcp_dist); + __m128 distance_attenuation = _mm_sub_ps(m256, _mm_min_ps(_mm_mul_ps(dist, light_radius), m256)); + + // The simple light type + __m128 simple_attenuation = distance_attenuation; + + // The point light type + // diffuse = dot(N,L) * attenuation + __m128 point_attenuation = _mm_mul_ps(_mm_mul_ps(light_z, rcp_dist), distance_attenuation); + + __m128 is_attenuated = _mm_cmpeq_ps(light_z, _mm_setzero_ps()); + __m128i attenuation = _mm_cvtps_epi32(_mm_or_ps(_mm_and_ps(is_attenuated, simple_attenuation), _mm_andnot_ps(is_attenuated, point_attenuation))); + attenuation = _mm_packs_epi32(_mm_shuffle_epi32(attenuation, _MM_SHUFFLE(0, 0, 0, 0)), _mm_shuffle_epi32(attenuation, _MM_SHUFFLE(1, 1, 1, 1))); + + __m128i light_color = _mm_cvtsi32_si128(lights[i].color); + light_color = _mm_unpacklo_epi8(light_color, _mm_setzero_si128()); + light_color = _mm_shuffle_epi32(light_color, _MM_SHUFFLE(1, 0, 1, 0)); + + lit = _mm_add_epi16(lit, _mm_srli_epi16(_mm_mullo_epi16(light_color, attenuation), 8)); + } + + fgcolor = _mm_add_epi16(fgcolor, _mm_srli_epi16(_mm_mullo_epi16(material, lit), 8)); + fgcolor = _mm_min_epi16(fgcolor, _mm_set1_epi16(255)); + return fgcolor; + } + + FORCEINLINE __m128i VECTORCALL Blend(__m128i fgcolor, __m128i bgcolor, uint32_t srcalpha, uint32_t destalpha, unsigned int ifgcolor0, unsigned int ifgcolor1) + { + using namespace DrawSpan32TModes; + + if (BlendT::Mode == (int)SpanBlendModes::Opaque) + { + __m128i outcolor = fgcolor; + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + return outcolor; + } + else if (BlendT::Mode == (int)SpanBlendModes::Masked) + { +#if 0 // leaving this in for alpha texture support (todo: fix in texture manager later?) + __m128i alpha = _mm_shufflelo_epi16(fgcolor, _MM_SHUFFLE(3, 3, 3, 3)); + alpha = _mm_shufflehi_epi16(alpha, _MM_SHUFFLE(3, 3, 3, 3)); + alpha = _mm_add_epi16(alpha, _mm_srli_epi16(alpha, 7)); // 255 -> 256 + + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + fgcolor = _mm_mullo_epi16(fgcolor, alpha); + bgcolor = _mm_mullo_epi16(bgcolor, inv_alpha); + __m128i outcolor = _mm_srli_epi16(_mm_add_epi16(fgcolor, bgcolor), 8); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; +#endif + __m128i mask = _mm_cmpeq_epi32(_mm_packus_epi16(fgcolor, _mm_setzero_si128()), _mm_setzero_si128()); + mask = _mm_unpacklo_epi8(mask, _mm_setzero_si128()); + __m128i outcolor = _mm_or_si128(_mm_and_si128(mask, bgcolor), _mm_andnot_si128(mask, fgcolor)); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + else if (BlendT::Mode == (int)SpanBlendModes::Translucent) + { + __m128i fgalpha = _mm_set1_epi16(srcalpha); + __m128i bgalpha = _mm_set1_epi16(destalpha); + + fgcolor = _mm_mullo_epi16(fgcolor, fgalpha); + bgcolor = _mm_mullo_epi16(bgcolor, bgalpha); + + __m128i fg_lo = _mm_unpacklo_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_lo = _mm_unpacklo_epi16(bgcolor, _mm_setzero_si128()); + __m128i fg_hi = _mm_unpackhi_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_hi = _mm_unpackhi_epi16(bgcolor, _mm_setzero_si128()); + + __m128i out_lo = _mm_add_epi32(fg_lo, bg_lo); + __m128i out_hi = _mm_add_epi32(fg_hi, bg_hi); + + out_lo = _mm_srai_epi32(out_lo, 8); + out_hi = _mm_srai_epi32(out_hi, 8); + __m128i outcolor = _mm_packs_epi32(out_lo, out_hi); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + else + { + uint32_t alpha0 = APART(ifgcolor0); + uint32_t alpha1 = APART(ifgcolor1); + alpha0 += alpha0 >> 7; // 255->256 + alpha1 += alpha1 >> 7; // 255->256 + uint32_t inv_alpha0 = 256 - alpha0; + uint32_t inv_alpha1 = 256 - alpha1; + + uint32_t bgalpha0 = (destalpha * alpha0 + (inv_alpha0 << 8) + 128) >> 8; + uint32_t bgalpha1 = (destalpha * alpha1 + (inv_alpha1 << 8) + 128) >> 8; + uint32_t fgalpha0 = (srcalpha * alpha0 + 128) >> 8; + uint32_t fgalpha1 = (srcalpha * alpha1 + 128) >> 8; + + __m128i bgalpha = _mm_set_epi16(bgalpha1, bgalpha1, bgalpha1, bgalpha1, bgalpha0, bgalpha0, bgalpha0, bgalpha0); + __m128i fgalpha = _mm_set_epi16(fgalpha1, fgalpha1, fgalpha1, fgalpha1, fgalpha0, fgalpha0, fgalpha0, fgalpha0); + + fgcolor = _mm_mullo_epi16(fgcolor, fgalpha); + bgcolor = _mm_mullo_epi16(bgcolor, bgalpha); + + __m128i fg_lo = _mm_unpacklo_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_lo = _mm_unpacklo_epi16(bgcolor, _mm_setzero_si128()); + __m128i fg_hi = _mm_unpackhi_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_hi = _mm_unpackhi_epi16(bgcolor, _mm_setzero_si128()); + + __m128i out_lo, out_hi; + if (BlendT::Mode == (int)SpanBlendModes::AddClamp) + { + out_lo = _mm_add_epi32(fg_lo, bg_lo); + out_hi = _mm_add_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)SpanBlendModes::SubClamp) + { + out_lo = _mm_sub_epi32(fg_lo, bg_lo); + out_hi = _mm_sub_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)SpanBlendModes::RevSubClamp) + { + out_lo = _mm_sub_epi32(bg_lo, fg_lo); + out_hi = _mm_sub_epi32(bg_hi, fg_hi); + } + + out_lo = _mm_srai_epi32(out_lo, 8); + out_hi = _mm_srai_epi32(out_hi, 8); + __m128i outcolor = _mm_packs_epi32(out_lo, out_hi); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + } + + FString DebugInfo() override { return "DrawSpan32T"; } + }; + + typedef DrawSpan32T DrawSpan32Command; + typedef DrawSpan32T DrawSpanMasked32Command; + typedef DrawSpan32T DrawSpanTranslucent32Command; + typedef DrawSpan32T DrawSpanAddClamp32Command; + typedef DrawSpan32T DrawSpanSubClamp32Command; + typedef DrawSpan32T DrawSpanRevSubClamp32Command; +} diff --git a/src/swrenderer/drawers/r_draw_sprite32_sse2.h b/src/swrenderer/drawers/r_draw_sprite32_sse2.h new file mode 100644 index 0000000000..3f10a86f83 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_sprite32_sse2.h @@ -0,0 +1,456 @@ +/* +** Drawer commands for sprites +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_walldrawer.h" + +namespace swrenderer +{ + namespace DrawSprite32TModes + { + enum class SpriteBlendModes { Copy, Opaque, Shaded, AddClamp, SubClamp, RevSubClamp }; + struct CopySprite { static const int Mode = (int)SpriteBlendModes::Copy; }; + struct OpaqueSprite { static const int Mode = (int)SpriteBlendModes::Opaque; }; + struct ShadedSprite { static const int Mode = (int)SpriteBlendModes::Shaded; }; + struct AddClampSprite { static const int Mode = (int)SpriteBlendModes::AddClamp; }; + struct SubClampSprite { static const int Mode = (int)SpriteBlendModes::SubClamp; }; + struct RevSubClampSprite { static const int Mode = (int)SpriteBlendModes::RevSubClamp; }; + + enum class FilterModes { Nearest, Linear }; + struct NearestFilter { static const int Mode = (int)FilterModes::Nearest; }; + struct LinearFilter { static const int Mode = (int)FilterModes::Linear; }; + + enum class ShadeMode { Simple, Advanced }; + struct SimpleShade { static const int Mode = (int)ShadeMode::Simple; }; + struct AdvancedShade { static const int Mode = (int)ShadeMode::Advanced; }; + + enum class SpriteSamplers { Texture, Fill, Shaded, Translated }; + struct TextureSampler { static const int Mode = (int)SpriteSamplers::Texture; }; + struct FillSampler { static const int Mode = (int)SpriteSamplers::Fill; }; + struct ShadedSampler { static const int Mode = (int)SpriteSamplers::Shaded; }; + struct TranslatedSampler { static const int Mode = (int)SpriteSamplers::Translated; }; + } + + template + class DrawSprite32T : public DrawerCommand + { + protected: + SpriteDrawerArgs args; + + public: + DrawSprite32T(const SpriteDrawerArgs &drawerargs) : args(drawerargs) { } + + void Execute(DrawerThread *thread) override + { + using namespace DrawSprite32TModes; + + auto shade_constants = args.ColormapConstants(); + if (SamplerT::Mode == (int)SpriteSamplers::Texture) + { + const uint32_t *source2 = (const uint32_t*)args.TexturePixels2(); + bool is_nearest_filter = (source2 == nullptr); + + if (shade_constants.simple_shade) + { + if (is_nearest_filter) + Loop(thread, shade_constants); + else + Loop(thread, shade_constants); + } + else + { + if (is_nearest_filter) + Loop(thread, shade_constants); + else + Loop(thread, shade_constants); + } + } + else // no linear filtering for translated, shaded or fill + { + if (shade_constants.simple_shade) + { + Loop(thread, shade_constants); + } + else + { + Loop(thread, shade_constants); + } + } + } + + template + FORCEINLINE void VECTORCALL Loop(DrawerThread *thread, ShadeConstants shade_constants) + { + using namespace DrawSprite32TModes; + + const uint32_t *source; + const uint32_t *source2; + const uint8_t *colormap; + const uint32_t *translation; + + if (SamplerT::Mode == (int)SpriteSamplers::Shaded || SamplerT::Mode == (int)SpriteSamplers::Translated) + { + source = (const uint32_t*)args.TexturePixels(); + source2 = nullptr; + colormap = args.Colormap(args.Viewport()); + translation = (const uint32_t*)args.TranslationMap(); + } + else + { + source = (const uint32_t*)args.TexturePixels(); + source2 = (const uint32_t*)args.TexturePixels2(); + colormap = nullptr; + translation = nullptr; + } + + int textureheight = args.TextureHeight(); + uint32_t one = ((0x80000000 + textureheight - 1) / textureheight) * 2 + 1; + + // Shade constants + __m128i dynlight = _mm_cvtsi32_si128(args.DynamicLight()); + dynlight = _mm_unpacklo_epi8(dynlight, _mm_setzero_si128()); + dynlight = _mm_shuffle_epi32(dynlight, _MM_SHUFFLE(1, 0, 1, 0)); + int light = 256 - (args.Light() >> (FRACBITS - 8)); + __m128i mlight = _mm_set_epi16(256, light, light, light, 256, light, light, light); + + __m128i inv_desaturate, shade_fade, shade_light; + int desaturate; + __m128i lightcontrib; + if (ShadeModeT::Mode == (int)ShadeMode::Advanced) + { + __m128i inv_light = _mm_set_epi16(0, 256 - light, 256 - light, 256 - light, 0, 256 - light, 256 - light, 256 - light); + inv_desaturate = _mm_setr_epi16(256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate); + shade_fade = _mm_set_epi16(shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue, shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue); + shade_fade = _mm_mullo_epi16(shade_fade, inv_light); + shade_light = _mm_set_epi16(shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue, shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue); + desaturate = shade_constants.desaturate; + + lightcontrib = _mm_min_epi16(_mm_add_epi16(mlight, dynlight), _mm_set1_epi16(256)); + lightcontrib = _mm_sub_epi16(lightcontrib, mlight); + } + else + { + inv_desaturate = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_light = _mm_setzero_si128(); + desaturate = 0; + lightcontrib = _mm_setzero_si128(); + + mlight = _mm_min_epi16(_mm_add_epi16(mlight, dynlight), _mm_set1_epi16(256)); + } + + int count = args.Count(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint32_t texturefracx = args.TextureUPos(); + uint32_t *dest = (uint32_t*)args.Dest(); + int dest_y = args.DestY(); + + count = thread->count_for_thread(dest_y, count); + if (count <= 0) return; + frac += thread->skipped_by_thread(dest_y) * fracstep; + dest = thread->dest_for_thread(dest_y, pitch, dest); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (FilterModeT::Mode == (int)FilterModes::Linear) + { + frac -= one / 2; + } + + uint32_t srcalpha = args.SrcAlpha() >> (FRACBITS - 8); + uint32_t destalpha = args.DestAlpha() >> (FRACBITS - 8); + uint32_t srccolor = args.SrcColorBgra(); + uint32_t color = LightBgra::shade_pal_index_simple(args.SolidColor(), light); + + int ssecount = count / 2; + for (int index = 0; index < ssecount; index++) + { + int offset = index * pitch * 2; + uint32_t desttmp[2]; + desttmp[0] = dest[offset]; + desttmp[1] = dest[offset + pitch]; + + __m128i bgcolor; + if (BlendT::Mode != (int)SpriteBlendModes::Opaque && BlendT::Mode != (int)SpriteBlendModes::Copy) + { + bgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)desttmp), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + unsigned int ifgcolor[2], ifgshade[2]; + ifgcolor[0] = Sample(frac, source, source2, translation, textureheight, one, texturefracx, color, srccolor); + ifgshade[0] = SampleShade(frac, source, colormap); + frac += fracstep; + + ifgcolor[1] = Sample(frac, source, source2, translation, textureheight, one, texturefracx, color, srccolor); + ifgshade[1] = SampleShade(frac, source, colormap); + frac += fracstep; + + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lightcontrib); + __m128i outcolor = Blend(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], ifgshade[0], ifgshade[1], srcalpha, destalpha); + + _mm_storel_epi64((__m128i*)desttmp, outcolor); + dest[offset] = desttmp[0]; + dest[offset + pitch] = desttmp[1]; + } + + if (ssecount * 2 != count) + { + int index = ssecount * 2; + int offset = index * pitch; + + __m128i bgcolor; + if (BlendT::Mode != (int)SpriteBlendModes::Opaque && BlendT::Mode != (int)SpriteBlendModes::Copy) + { + bgcolor = _mm_unpacklo_epi8(_mm_cvtsi32_si128(dest[offset]), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + // Sample + unsigned int ifgcolor[2], ifgshade[2]; + ifgcolor[0] = Sample(frac, source, source2, translation, textureheight, one, texturefracx, color, srccolor); + ifgcolor[1] = 0; + ifgshade[0] = SampleShade(frac, source, colormap); + ifgshade[1] = 0; + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lightcontrib); + __m128i outcolor = Blend(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], ifgshade[0], ifgshade[1], srcalpha, destalpha); + + dest[offset] = _mm_cvtsi128_si32(outcolor); + } + } + + template + FORCEINLINE unsigned int VECTORCALL Sample(uint32_t frac, const uint32_t *source, const uint32_t *source2, const uint32_t *translation, int textureheight, uint32_t one, uint32_t texturefracx, uint32_t color, uint32_t srccolor) + { + using namespace DrawSprite32TModes; + + if (SamplerT::Mode == (int)SpriteSamplers::Shaded) + { + return color; + } + else if (SamplerT::Mode == (int)SpriteSamplers::Translated) + { + const uint8_t *sourcepal = (const uint8_t *)source; + return translation[sourcepal[frac >> FRACBITS]]; + } + else if (SamplerT::Mode == (int)SpriteSamplers::Fill) + { + return srccolor; + } + else if (FilterModeT::Mode == (int)FilterModes::Nearest) + { + int sample_index = (((frac << 2) >> FRACBITS) * textureheight) >> FRACBITS; + return source[sample_index]; + } + else + { + // Clamp to edge + unsigned int frac_y0 = (clamp(frac, 0, 1 << 30) >> (FRACBITS - 2)) * textureheight; + unsigned int frac_y1 = (clamp(frac + one, 0, 1 << 30) >> (FRACBITS - 2)) * textureheight; + unsigned int y0 = frac_y0 >> FRACBITS; + unsigned int y1 = frac_y1 >> FRACBITS; + + unsigned int p00 = source[y0]; + unsigned int p01 = source[y1]; + unsigned int p10 = source2[y0]; + unsigned int p11 = source2[y1]; + + unsigned int inv_b = texturefracx; + unsigned int inv_a = (frac_y1 >> (FRACBITS - 4)) & 15; + unsigned int a = 16 - inv_a; + unsigned int b = 16 - inv_b; + + unsigned int sred = (RPART(p00) * (a * b) + RPART(p01) * (inv_a * b) + RPART(p10) * (a * inv_b) + RPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int sgreen = (GPART(p00) * (a * b) + GPART(p01) * (inv_a * b) + GPART(p10) * (a * inv_b) + GPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int sblue = (BPART(p00) * (a * b) + BPART(p01) * (inv_a * b) + BPART(p10) * (a * inv_b) + BPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int salpha = (APART(p00) * (a * b) + APART(p01) * (inv_a * b) + APART(p10) * (a * inv_b) + APART(p11) * (inv_a * inv_b) + 127) >> 8; + + return (salpha << 24) | (sred << 16) | (sgreen << 8) | sblue; + } + } + + FORCEINLINE unsigned int VECTORCALL SampleShade(uint32_t frac, const uint32_t *source, const uint8_t *colormap) + { + using namespace DrawSprite32TModes; + + if (SamplerT::Mode == (int)SpriteSamplers::Shaded) + { + const uint8_t *sourcepal = (const uint8_t *)source; + unsigned int sampleshadeout = colormap[sourcepal[frac >> FRACBITS]]; + return clamp(sampleshadeout, 0, 64) * 4; + } + else + { + return 0; + } + } + + template + FORCEINLINE __m128i VECTORCALL Shade(__m128i fgcolor, __m128i mlight, unsigned int ifgcolor0, unsigned int ifgcolor1, int desaturate, __m128i inv_desaturate, __m128i shade_fade, __m128i shade_light, __m128i lightcontrib) + { + using namespace DrawSprite32TModes; + + if (BlendT::Mode == (int)SpriteBlendModes::Copy || BlendT::Mode == (int)SpriteBlendModes::Shaded) + return fgcolor; + + if (ShadeModeT::Mode == (int)ShadeMode::Simple) + { + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, mlight), 8); + return fgcolor; + } + else + { + __m128i lit_dynlight = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, lightcontrib), 8); + + int blue0 = BPART(ifgcolor0); + int green0 = GPART(ifgcolor0); + int red0 = RPART(ifgcolor0); + int intensity0 = ((red0 * 77 + green0 * 143 + blue0 * 37) >> 8) * desaturate; + + int blue1 = BPART(ifgcolor1); + int green1 = GPART(ifgcolor1); + int red1 = RPART(ifgcolor1); + int intensity1 = ((red1 * 77 + green1 * 143 + blue1 * 37) >> 8) * desaturate; + + __m128i intensity = _mm_set_epi16(0, intensity1, intensity1, intensity1, 0, intensity0, intensity0, intensity0); + + fgcolor = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(fgcolor, inv_desaturate), intensity), 8); + fgcolor = _mm_mullo_epi16(fgcolor, mlight); + fgcolor = _mm_srli_epi16(_mm_add_epi16(shade_fade, fgcolor), 8); + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, shade_light), 8); + + fgcolor = _mm_add_epi16(fgcolor, lit_dynlight); + fgcolor = _mm_min_epi16(fgcolor, _mm_set1_epi16(256)); + return fgcolor; + } + } + + FORCEINLINE __m128i VECTORCALL Blend(__m128i fgcolor, __m128i bgcolor, unsigned int ifgcolor0, unsigned int ifgcolor1, unsigned int ifgshade0, unsigned int ifgshade1, uint32_t srcalpha, uint32_t destalpha) + { + using namespace DrawSprite32TModes; + + if (BlendT::Mode == (int)SpriteBlendModes::Opaque) + { + __m128i outcolor = fgcolor; + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + return outcolor; + } + else if (BlendT::Mode == (int)SpriteBlendModes::Shaded) + { + __m128i alpha = _mm_set_epi16(ifgshade1, ifgshade1, ifgshade1, ifgshade1, ifgshade0, ifgshade0, ifgshade0, ifgshade0); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + fgcolor = _mm_mullo_epi16(fgcolor, alpha); + bgcolor = _mm_mullo_epi16(bgcolor, inv_alpha); + __m128i outcolor = _mm_srli_epi16(_mm_add_epi16(fgcolor, bgcolor), 8); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + else + { + uint32_t alpha0 = APART(ifgcolor0); + uint32_t alpha1 = APART(ifgcolor1); + alpha0 += alpha0 >> 7; // 255->256 + alpha1 += alpha1 >> 7; // 255->256 + uint32_t inv_alpha0 = 256 - alpha0; + uint32_t inv_alpha1 = 256 - alpha1; + + uint32_t bgalpha0 = (destalpha * alpha0 + (inv_alpha0 << 8) + 128) >> 8; + uint32_t bgalpha1 = (destalpha * alpha1 + (inv_alpha1 << 8) + 128) >> 8; + uint32_t fgalpha0 = (srcalpha * alpha0 + 128) >> 8; + uint32_t fgalpha1 = (srcalpha * alpha1 + 128) >> 8; + + __m128i bgalpha = _mm_set_epi16(bgalpha1, bgalpha1, bgalpha1, bgalpha1, bgalpha0, bgalpha0, bgalpha0, bgalpha0); + __m128i fgalpha = _mm_set_epi16(fgalpha1, fgalpha1, fgalpha1, fgalpha1, fgalpha0, fgalpha0, fgalpha0, fgalpha0); + + fgcolor = _mm_mullo_epi16(fgcolor, fgalpha); + bgcolor = _mm_mullo_epi16(bgcolor, bgalpha); + + __m128i fg_lo = _mm_unpacklo_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_lo = _mm_unpacklo_epi16(bgcolor, _mm_setzero_si128()); + __m128i fg_hi = _mm_unpackhi_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_hi = _mm_unpackhi_epi16(bgcolor, _mm_setzero_si128()); + + __m128i out_lo, out_hi; + if (BlendT::Mode == (int)SpriteBlendModes::AddClamp) + { + out_lo = _mm_add_epi32(fg_lo, bg_lo); + out_hi = _mm_add_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)SpriteBlendModes::SubClamp) + { + out_lo = _mm_sub_epi32(fg_lo, bg_lo); + out_hi = _mm_sub_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)SpriteBlendModes::RevSubClamp) + { + out_lo = _mm_sub_epi32(bg_lo, fg_lo); + out_hi = _mm_sub_epi32(bg_hi, fg_hi); + } + + out_lo = _mm_srai_epi32(out_lo, 8); + out_hi = _mm_srai_epi32(out_hi, 8); + __m128i outcolor = _mm_packs_epi32(out_lo, out_hi); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + } + + FString DebugInfo() override { return "DrawSprite32T"; } + }; + + typedef DrawSprite32T DrawSpriteCopy32Command; + + typedef DrawSprite32T DrawSprite32Command; + typedef DrawSprite32T DrawSpriteAddClamp32Command; + typedef DrawSprite32T DrawSpriteSubClamp32Command; + typedef DrawSprite32T DrawSpriteRevSubClamp32Command; + + typedef DrawSprite32T FillSprite32Command; + typedef DrawSprite32T FillSpriteAddClamp32Command; + typedef DrawSprite32T FillSpriteSubClamp32Command; + typedef DrawSprite32T FillSpriteRevSubClamp32Command; + + typedef DrawSprite32T DrawSpriteShaded32Command; + + typedef DrawSprite32T DrawSpriteTranslated32Command; + typedef DrawSprite32T DrawSpriteTranslatedAddClamp32Command; + typedef DrawSprite32T DrawSpriteTranslatedSubClamp32Command; + typedef DrawSprite32T DrawSpriteTranslatedRevSubClamp32Command; +} diff --git a/src/swrenderer/drawers/r_draw_wall32_sse2.h b/src/swrenderer/drawers/r_draw_wall32_sse2.h new file mode 100644 index 0000000000..8ac116da44 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_wall32_sse2.h @@ -0,0 +1,415 @@ +/* +** Drawer commands for walls +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_walldrawer.h" + +namespace swrenderer +{ + namespace DrawWall32TModes + { + enum class WallBlendModes { Opaque, Masked, AddClamp, SubClamp, RevSubClamp }; + struct OpaqueWall { static const int Mode = (int)WallBlendModes::Opaque; }; + struct MaskedWall { static const int Mode = (int)WallBlendModes::Masked; }; + struct AddClampWall { static const int Mode = (int)WallBlendModes::AddClamp; }; + struct SubClampWall { static const int Mode = (int)WallBlendModes::SubClamp; }; + struct RevSubClampWall { static const int Mode = (int)WallBlendModes::RevSubClamp; }; + + enum class FilterModes { Nearest, Linear }; + struct NearestFilter { static const int Mode = (int)FilterModes::Nearest; }; + struct LinearFilter { static const int Mode = (int)FilterModes::Linear; }; + + enum class ShadeMode { Simple, Advanced }; + struct SimpleShade { static const int Mode = (int)ShadeMode::Simple; }; + struct AdvancedShade { static const int Mode = (int)ShadeMode::Advanced; }; + } + + template + class DrawWall32T : public DrawerCommand + { + protected: + WallDrawerArgs args; + + public: + DrawWall32T(const WallDrawerArgs &drawerargs) : args(drawerargs) { } + + void Execute(DrawerThread *thread) override + { + using namespace DrawWall32TModes; + + const uint32_t *source2 = (const uint32_t*)args.TexturePixels2(); + bool is_nearest_filter = (source2 == nullptr); + auto shade_constants = args.ColormapConstants(); + if (shade_constants.simple_shade) + { + if (is_nearest_filter) + Loop(thread, shade_constants); + else + Loop(thread, shade_constants); + } + else + { + if (is_nearest_filter) + Loop(thread, shade_constants); + else + Loop(thread, shade_constants); + } + } + + template + FORCEINLINE void VECTORCALL Loop(DrawerThread *thread, ShadeConstants shade_constants) + { + using namespace DrawWall32TModes; + + const uint32_t *source = (const uint32_t*)args.TexturePixels(); + const uint32_t *source2 = (const uint32_t*)args.TexturePixels2(); + int textureheight = args.TextureHeight(); + uint32_t one = ((0x80000000 + textureheight - 1) / textureheight) * 2 + 1; + + // Shade constants + int light = 256 - (args.Light() >> (FRACBITS - 8)); + __m128i mlight = _mm_set_epi16(256, light, light, light, 256, light, light, light); + __m128i inv_light = _mm_set_epi16(0, 256 - light, 256 - light, 256 - light, 0, 256 - light, 256 - light, 256 - light); + + __m128i inv_desaturate, shade_fade, shade_light; + int desaturate; + if (ShadeModeT::Mode == (int)ShadeMode::Advanced) + { + inv_desaturate = _mm_setr_epi16(256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate); + shade_fade = _mm_set_epi16(shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue, shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue); + shade_fade = _mm_mullo_epi16(shade_fade, inv_light); + shade_light = _mm_set_epi16(shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue, shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue); + desaturate = shade_constants.desaturate; + } + else + { + inv_desaturate = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_light = _mm_setzero_si128(); + desaturate = 0; + } + + int count = args.Count(); + int pitch = args.Viewport()->RenderTarget->GetPitch(); + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint32_t texturefracx = args.TextureUPos(); + uint32_t *dest = (uint32_t*)args.Dest(); + int dest_y = args.DestY(); + + auto lights = args.dc_lights; + auto num_lights = args.dc_num_lights; + float vpz = args.dc_viewpos.Z + args.dc_viewpos_step.Z * thread->skipped_by_thread(dest_y); + float stepvpz = args.dc_viewpos_step.Z * thread->num_cores; + __m128 viewpos_z = _mm_setr_ps(vpz, vpz + stepvpz, 0.0f, 0.0f); + __m128 step_viewpos_z = _mm_set1_ps(stepvpz * 2.0f); + + count = thread->count_for_thread(dest_y, count); + if (count <= 0) return; + frac += thread->skipped_by_thread(dest_y) * fracstep; + dest = thread->dest_for_thread(dest_y, pitch, dest); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (FilterModeT::Mode == (int)FilterModes::Linear) + { + frac -= one / 2; + } + + uint32_t srcalpha = args.SrcAlpha() >> (FRACBITS - 8); + uint32_t destalpha = args.DestAlpha() >> (FRACBITS - 8); + + int ssecount = count / 2; + for (int index = 0; index < ssecount; index++) + { + int offset = index * pitch * 2; + uint32_t desttmp[2]; + desttmp[0] = dest[offset]; + desttmp[1] = dest[offset + pitch]; + + __m128i bgcolor; + if (BlendT::Mode != (int)WallBlendModes::Opaque) + { + bgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)desttmp), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + unsigned int ifgcolor[2]; + ifgcolor[0] = Sample(frac, source, source2, textureheight, one, texturefracx); + frac += fracstep; + + ifgcolor[1] = Sample(frac, source, source2, textureheight, one, texturefracx); + frac += fracstep; + + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lights, num_lights, viewpos_z); + __m128i outcolor = Blend(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], srcalpha, destalpha); + + _mm_storel_epi64((__m128i*)desttmp, outcolor); + dest[offset] = desttmp[0]; + dest[offset + pitch] = desttmp[1]; + viewpos_z = _mm_add_ps(viewpos_z, step_viewpos_z); + } + + if (ssecount * 2 != count) + { + int index = ssecount * 2; + int offset = index * pitch; + + __m128i bgcolor; + if (BlendT::Mode != (int)WallBlendModes::Opaque) + { + bgcolor = _mm_unpacklo_epi8(_mm_cvtsi32_si128(dest[offset]), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + unsigned int ifgcolor[2]; + ifgcolor[0] = Sample(frac, source, source2, textureheight, one, texturefracx); + ifgcolor[1] = 0; + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lights, num_lights, viewpos_z); + __m128i outcolor = Blend(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], srcalpha, destalpha); + + dest[offset] = _mm_cvtsi128_si32(outcolor); + } + } + + template + FORCEINLINE unsigned int VECTORCALL Sample(uint32_t frac, const uint32_t *source, const uint32_t *source2, int textureheight, uint32_t one, uint32_t texturefracx) + { + using namespace DrawWall32TModes; + + if (FilterModeT::Mode == (int)FilterModes::Nearest) + { + int sample_index = ((frac >> FRACBITS) * textureheight) >> FRACBITS; + return source[sample_index]; + } + else + { + unsigned int frac_y0 = (frac >> FRACBITS) * textureheight; + unsigned int frac_y1 = ((frac + one) >> FRACBITS) * textureheight; + unsigned int y0 = frac_y0 >> FRACBITS; + unsigned int y1 = frac_y1 >> FRACBITS; + + unsigned int p00 = source[y0]; + unsigned int p01 = source[y1]; + unsigned int p10 = source2[y0]; + unsigned int p11 = source2[y1]; + + unsigned int inv_b = texturefracx; + unsigned int inv_a = (frac_y1 >> (FRACBITS - 4)) & 15; + unsigned int a = 16 - inv_a; + unsigned int b = 16 - inv_b; + + unsigned int sred = (RPART(p00) * (a * b) + RPART(p01) * (inv_a * b) + RPART(p10) * (a * inv_b) + RPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int sgreen = (GPART(p00) * (a * b) + GPART(p01) * (inv_a * b) + GPART(p10) * (a * inv_b) + GPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int sblue = (BPART(p00) * (a * b) + BPART(p01) * (inv_a * b) + BPART(p10) * (a * inv_b) + BPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int salpha = (APART(p00) * (a * b) + APART(p01) * (inv_a * b) + APART(p10) * (a * inv_b) + APART(p11) * (inv_a * inv_b) + 127) >> 8; + + return (salpha << 24) | (sred << 16) | (sgreen << 8) | sblue; + } + } + + template + FORCEINLINE __m128i VECTORCALL Shade(__m128i fgcolor, __m128i mlight, unsigned int ifgcolor0, unsigned int ifgcolor1, int desaturate, __m128i inv_desaturate, __m128i shade_fade, __m128i shade_light, const DrawerLight *lights, int num_lights, __m128 viewpos_z) + { + using namespace DrawWall32TModes; + + __m128i material = fgcolor; + if (ShadeModeT::Mode == (int)ShadeMode::Simple) + { + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, mlight), 8); + } + else + { + int blue0 = BPART(ifgcolor0); + int green0 = GPART(ifgcolor0); + int red0 = RPART(ifgcolor0); + int intensity0 = ((red0 * 77 + green0 * 143 + blue0 * 37) >> 8) * desaturate; + + int blue1 = BPART(ifgcolor1); + int green1 = GPART(ifgcolor1); + int red1 = RPART(ifgcolor1); + int intensity1 = ((red1 * 77 + green1 * 143 + blue1 * 37) >> 8) * desaturate; + + __m128i intensity = _mm_set_epi16(0, intensity1, intensity1, intensity1, 0, intensity0, intensity0, intensity0); + + fgcolor = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(fgcolor, inv_desaturate), intensity), 8); + fgcolor = _mm_mullo_epi16(fgcolor, mlight); + fgcolor = _mm_srli_epi16(_mm_add_epi16(shade_fade, fgcolor), 8); + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, shade_light), 8); + } + + return AddLights(material, fgcolor, lights, num_lights, viewpos_z); + } + + FORCEINLINE __m128i VECTORCALL AddLights(__m128i material, __m128i fgcolor, const DrawerLight *lights, int num_lights, __m128 viewpos_z) + { + using namespace DrawWall32TModes; + + __m128i lit = _mm_setzero_si128(); + + for (int i = 0; i != num_lights; i++) + { + __m128 light_x = _mm_set1_ps(lights[i].x); + __m128 light_y = _mm_set1_ps(lights[i].y); + __m128 light_z = _mm_set1_ps(lights[i].z); + __m128 light_radius = _mm_set1_ps(lights[i].radius); + __m128 m256 = _mm_set1_ps(256.0f); + + // L = light-pos + // dist = sqrt(dot(L, L)) + // distance_attenuation = 1 - MIN(dist * (1/radius), 1) + __m128 Lxy2 = light_x; // L.x*L.x + L.y*L.y + __m128 Lz = _mm_sub_ps(light_z, viewpos_z); + __m128 dist2 = _mm_add_ps(Lxy2, _mm_mul_ps(Lz, Lz)); + __m128 rcp_dist = _mm_rsqrt_ps(dist2); + __m128 dist = _mm_mul_ps(dist2, rcp_dist); + __m128 distance_attenuation = _mm_sub_ps(m256, _mm_min_ps(_mm_mul_ps(dist, light_radius), m256)); + + // The simple light type + __m128 simple_attenuation = distance_attenuation; + + // The point light type + // diffuse = dot(N,L) * attenuation + __m128 point_attenuation = _mm_mul_ps(_mm_mul_ps(light_y, rcp_dist), distance_attenuation); + + __m128 is_attenuated = _mm_cmpeq_ps(light_y, _mm_setzero_ps()); + __m128i attenuation = _mm_cvtps_epi32(_mm_or_ps(_mm_and_ps(is_attenuated, simple_attenuation), _mm_andnot_ps(is_attenuated, point_attenuation))); + attenuation = _mm_packs_epi32(_mm_shuffle_epi32(attenuation, _MM_SHUFFLE(0, 0, 0, 0)), _mm_shuffle_epi32(attenuation, _MM_SHUFFLE(1, 1, 1, 1))); + + __m128i light_color = _mm_cvtsi32_si128(lights[i].color); + light_color = _mm_unpacklo_epi8(light_color, _mm_setzero_si128()); + light_color = _mm_shuffle_epi32(light_color, _MM_SHUFFLE(1, 0, 1, 0)); + + lit = _mm_add_epi16(lit, _mm_srli_epi16(_mm_mullo_epi16(light_color, attenuation), 8)); + } + + fgcolor = _mm_add_epi16(fgcolor, _mm_srli_epi16(_mm_mullo_epi16(material, lit), 8)); + fgcolor = _mm_min_epi16(fgcolor, _mm_set1_epi16(255)); + return fgcolor; + } + + FORCEINLINE __m128i VECTORCALL Blend(__m128i fgcolor, __m128i bgcolor, unsigned int ifgcolor0, unsigned int ifgcolor1, uint32_t srcalpha, uint32_t destalpha) + { + using namespace DrawWall32TModes; + + if (BlendT::Mode == (int)WallBlendModes::Opaque) + { + __m128i outcolor = fgcolor; + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + return outcolor; + } + else if (BlendT::Mode == (int)WallBlendModes::Masked) + { +#if 0 // leaving this in for alpha texture support (todo: fix in texture manager later?) + __m128i alpha = _mm_shufflelo_epi16(fgcolor, _MM_SHUFFLE(3, 3, 3, 3)); + alpha = _mm_shufflehi_epi16(alpha, _MM_SHUFFLE(3, 3, 3, 3)); + alpha = _mm_add_epi16(alpha, _mm_srli_epi16(alpha, 7)); // 255 -> 256 + + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + fgcolor = _mm_mullo_epi16(fgcolor, alpha); + bgcolor = _mm_mullo_epi16(bgcolor, inv_alpha); + __m128i outcolor = _mm_srli_epi16(_mm_add_epi16(fgcolor, bgcolor), 8); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; +#endif + __m128i mask = _mm_cmpeq_epi32(_mm_packus_epi16(fgcolor, _mm_setzero_si128()), _mm_setzero_si128()); + mask = _mm_unpacklo_epi8(mask, _mm_setzero_si128()); + __m128i outcolor = _mm_or_si128(_mm_and_si128(mask, bgcolor), _mm_andnot_si128(mask, fgcolor)); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + else + { + uint32_t alpha0 = APART(ifgcolor0); + uint32_t alpha1 = APART(ifgcolor1); + alpha0 += alpha0 >> 7; // 255->256 + alpha1 += alpha1 >> 7; // 255->256 + uint32_t inv_alpha0 = 256 - alpha0; + uint32_t inv_alpha1 = 256 - alpha1; + + uint32_t bgalpha0 = (destalpha * alpha0 + (inv_alpha0 << 8) + 128) >> 8; + uint32_t bgalpha1 = (destalpha * alpha1 + (inv_alpha1 << 8) + 128) >> 8; + uint32_t fgalpha0 = (srcalpha * alpha0 + 128) >> 8; + uint32_t fgalpha1 = (srcalpha * alpha1 + 128) >> 8; + + __m128i bgalpha = _mm_set_epi16(bgalpha1, bgalpha1, bgalpha1, bgalpha1, bgalpha0, bgalpha0, bgalpha0, bgalpha0); + __m128i fgalpha = _mm_set_epi16(fgalpha1, fgalpha1, fgalpha1, fgalpha1, fgalpha0, fgalpha0, fgalpha0, fgalpha0); + + fgcolor = _mm_mullo_epi16(fgcolor, fgalpha); + bgcolor = _mm_mullo_epi16(bgcolor, bgalpha); + + __m128i fg_lo = _mm_unpacklo_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_lo = _mm_unpacklo_epi16(bgcolor, _mm_setzero_si128()); + __m128i fg_hi = _mm_unpackhi_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_hi = _mm_unpackhi_epi16(bgcolor, _mm_setzero_si128()); + + __m128i out_lo, out_hi; + if (BlendT::Mode == (int)WallBlendModes::AddClamp) + { + out_lo = _mm_add_epi32(fg_lo, bg_lo); + out_hi = _mm_add_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)WallBlendModes::SubClamp) + { + out_lo = _mm_sub_epi32(fg_lo, bg_lo); + out_hi = _mm_sub_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)WallBlendModes::RevSubClamp) + { + out_lo = _mm_sub_epi32(bg_lo, fg_lo); + out_hi = _mm_sub_epi32(bg_hi, fg_hi); + } + + out_lo = _mm_srai_epi32(out_lo, 8); + out_hi = _mm_srai_epi32(out_hi, 8); + __m128i outcolor = _mm_packs_epi32(out_lo, out_hi); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + } + + FString DebugInfo() override { return "DrawWall32T"; } + }; + + typedef DrawWall32T DrawWall32Command; + typedef DrawWall32T DrawWallMasked32Command; + typedef DrawWall32T DrawWallAddClamp32Command; + typedef DrawWall32T DrawWallSubClamp32Command; + typedef DrawWall32T DrawWallRevSubClamp32Command; +} diff --git a/src/r_thread.cpp b/src/swrenderer/drawers/r_thread.cpp similarity index 71% rename from src/r_thread.cpp rename to src/swrenderer/drawers/r_thread.cpp index c96f14e74b..adcee27685 100644 --- a/src/r_thread.cpp +++ b/src/swrenderer/drawers/r_thread.cpp @@ -25,87 +25,48 @@ #include "doomdef.h" #include "i_system.h" #include "w_wad.h" -#include "r_local.h" #include "v_video.h" #include "doomstat.h" #include "st_stuff.h" #include "g_game.h" #include "g_level.h" #include "r_thread.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" CVAR(Bool, r_multithreaded, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -void R_BeginDrawerCommands() -{ - DrawerCommandQueue::Begin(); -} - -void R_EndDrawerCommands() -{ - DrawerCommandQueue::End(); -} - ///////////////////////////////////////////////////////////////////////////// -DrawerCommandQueue *DrawerCommandQueue::Instance() +DrawerThreads *DrawerThreads::Instance() { - static DrawerCommandQueue queue; - return &queue; + static DrawerThreads threads; + return &threads; } -DrawerCommandQueue::DrawerCommandQueue() +DrawerThreads::DrawerThreads() { } -DrawerCommandQueue::~DrawerCommandQueue() +DrawerThreads::~DrawerThreads() { StopThreads(); } -void* DrawerCommandQueue::AllocMemory(size_t size) +void DrawerThreads::Execute(const std::vector &queues) { - // Make sure allocations remain 16-byte aligned - size = (size + 15) / 16 * 16; - - auto queue = Instance(); - if (queue->memorypool_pos + size > memorypool_size) - return nullptr; - - void *data = queue->memorypool + queue->memorypool_pos; - queue->memorypool_pos += size; - return data; -} - -void DrawerCommandQueue::Begin() -{ - auto queue = Instance(); - queue->Finish(); - queue->threaded_render++; -} - -void DrawerCommandQueue::End() -{ - auto queue = Instance(); - queue->Finish(); - if (queue->threaded_render > 0) - queue->threaded_render--; -} - -void DrawerCommandQueue::WaitForWorkers() -{ - Instance()->Finish(); -} - -void DrawerCommandQueue::Finish() -{ - auto queue = Instance(); - if (queue->commands.empty()) + bool hasWork = false; + for (const auto &queue : queues) + hasWork = hasWork || !queue->commands.empty(); + if (!hasWork) return; + + auto queue = Instance(); // Give worker threads something to do: std::unique_lock start_lock(queue->start_mutex); - queue->active_commands.swap(queue->commands); + queue->active_commands = queues; queue->run_id++; start_lock.unlock(); @@ -114,19 +75,21 @@ void DrawerCommandQueue::Finish() // Do one thread ourselves: - DrawerThread thread; + static DrawerThread thread; thread.core = 0; thread.num_cores = (int)(queue->threads.size() + 1); struct TryCatchData { - DrawerCommandQueue *queue; + DrawerThreads *queue; DrawerThread *thread; + size_t list_index; size_t command_index; } data; data.queue = queue; data.thread = &thread; + data.list_index = 0; data.command_index = 0; VectoredTryCatch(&data, [](void *data) @@ -140,18 +103,22 @@ void DrawerCommandQueue::Finish() if (pass + 1 == d->queue->num_passes) d->thread->pass_end_y = MAX(d->thread->pass_end_y, MAXHEIGHT); - size_t size = d->queue->active_commands.size(); - for (d->command_index = 0; d->command_index < size; d->command_index++) + for (auto &list : d->queue->active_commands) { - auto &command = d->queue->active_commands[d->command_index]; - command->Execute(d->thread); + size_t size = list->commands.size(); + for (d->command_index = 0; d->command_index < size; d->command_index++) + { + auto &command = list->commands[d->command_index]; + command->Execute(d->thread); + } + d->list_index++; } } }, [](void *data, const char *reason, bool fatal) { TryCatchData *d = (TryCatchData*)data; - ReportDrawerError(d->queue->active_commands[d->command_index], true, reason, fatal); + ReportDrawerError(d->queue->active_commands[d->list_index]->commands[d->command_index], true, reason, fatal); }); // Wait for everyone to finish: @@ -171,14 +138,17 @@ void DrawerCommandQueue::Finish() // Clean up batch: - for (auto &command : queue->active_commands) - command->~DrawerCommand(); + for (auto &list : queue->active_commands) + { + for (auto &command : list->commands) + command->~DrawerCommand(); + list->Clear(); + } queue->active_commands.clear(); - queue->memorypool_pos = 0; queue->finished_threads = 0; } -void DrawerCommandQueue::StartThreads() +void DrawerThreads::StartThreads() { if (!threads.empty()) return; @@ -191,7 +161,7 @@ void DrawerCommandQueue::StartThreads() for (int i = 0; i < num_threads - 1; i++) { - DrawerCommandQueue *queue = this; + DrawerThreads *queue = this; DrawerThread *thread = &threads[i]; thread->core = i + 1; thread->num_cores = num_threads; @@ -212,13 +182,15 @@ void DrawerCommandQueue::StartThreads() struct TryCatchData { - DrawerCommandQueue *queue; + DrawerThreads *queue; DrawerThread *thread; + size_t list_index; size_t command_index; } data; data.queue = queue; data.thread = thread; + data.list_index = 0; data.command_index = 0; VectoredTryCatch(&data, [](void *data) @@ -232,18 +204,22 @@ void DrawerCommandQueue::StartThreads() if (pass + 1 == d->queue->num_passes) d->thread->pass_end_y = MAX(d->thread->pass_end_y, MAXHEIGHT); - size_t size = d->queue->active_commands.size(); - for (d->command_index = 0; d->command_index < size; d->command_index++) + for (auto &list : d->queue->active_commands) { - auto &command = d->queue->active_commands[d->command_index]; - command->Execute(d->thread); + size_t size = list->commands.size(); + for (d->command_index = 0; d->command_index < size; d->command_index++) + { + auto &command = list->commands[d->command_index]; + command->Execute(d->thread); + } + d->list_index++; } } }, [](void *data, const char *reason, bool fatal) { TryCatchData *d = (TryCatchData*)data; - ReportDrawerError(d->queue->active_commands[d->command_index], true, reason, fatal); + ReportDrawerError(d->queue->active_commands[d->list_index]->commands[d->command_index], true, reason, fatal); }); // Notify main thread that we finished: @@ -256,7 +232,7 @@ void DrawerCommandQueue::StartThreads() } } -void DrawerCommandQueue::StopThreads() +void DrawerThreads::StopThreads() { std::unique_lock lock(start_mutex); shutdown_flag = true; @@ -269,7 +245,7 @@ void DrawerCommandQueue::StopThreads() shutdown_flag = false; } -void DrawerCommandQueue::ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal) +void DrawerThreads::ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal) { if (worker_thread) { @@ -291,7 +267,20 @@ void DrawerCommandQueue::ReportDrawerError(DrawerCommand *command, bool worker_t } } +#ifndef WIN32 + void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data, const char *reason, bool fatal)) { tryBlock(data); } + +#endif + +DrawerCommandQueue::DrawerCommandQueue(swrenderer::RenderThread *renderthread) : renderthread(renderthread) +{ +} + +void *DrawerCommandQueue::AllocMemory(size_t size) +{ + return renderthread->FrameMemory->AllocMemory((int)size); +} diff --git a/src/r_thread.h b/src/swrenderer/drawers/r_thread.h similarity index 61% rename from src/r_thread.h rename to src/swrenderer/drawers/r_thread.h index e44d872c23..cc8d33eabe 100644 --- a/src/r_thread.h +++ b/src/swrenderer/drawers/r_thread.h @@ -23,6 +23,7 @@ #pragma once #include "r_draw.h" +#include "polyrenderer/drawers/screen_triangle.h" #include #include #include @@ -32,20 +33,14 @@ // Use multiple threads when drawing EXTERN_CVAR(Bool, r_multithreaded) -// Redirect drawer commands to worker threads -void R_BeginDrawerCommands(); - -// Wait until all drawers finished executing -void R_EndDrawerCommands(); - // Worker data for each thread executing drawer commands class DrawerThread { public: DrawerThread() { - dc_temp = dc_temp_buff; - dc_temp_rgba = dc_temp_rgbabuff_rgba; + FullSpansBuffer.resize(MAXWIDTH / 8 * (MAXHEIGHT / 8)); + PartialBlocksBuffer.resize(MAXWIDTH / 8 * (MAXHEIGHT / 8)); } std::thread thread; @@ -60,17 +55,13 @@ public: int pass_start_y = 0; int pass_end_y = MAXHEIGHT; - // Working buffer used by Rt drawers - uint8_t dc_temp_buff[MAXHEIGHT * 4]; - uint8_t *dc_temp = nullptr; - - // Working buffer used by Rt drawers, true color edition - uint32_t dc_temp_rgbabuff_rgba[MAXHEIGHT * 4]; - uint32_t *dc_temp_rgba = nullptr; - // Working buffer used by the tilted (sloped) span drawer const uint8_t *tiltlighting[MAXWIDTH]; + // Working buffer used by the triangler drawer + std::vector FullSpansBuffer; + std::vector PartialBlocksBuffer; + // Checks if a line is rendered by this thread bool line_skipped_by_thread(int line) { @@ -111,39 +102,7 @@ public: // Task to be executed by each worker thread class DrawerCommand { -protected: - int _dest_y; - - void DetectRangeError(uint32_t *&dest, int &dest_y, int &count) - { -#if defined(_MSC_VER) && defined(_DEBUG) - if (dest_y < 0 || count < 0 || dest_y + count > swrenderer::drawerargs::dc_destheight) - __debugbreak(); // Buffer overrun detected! -#endif - - if (dest_y < 0) - { - count += dest_y; - dest_y = 0; - dest = (uint32_t*)swrenderer::drawerargs::dc_destorg; - } - else if (dest_y >= swrenderer::drawerargs::dc_destheight) - { - dest_y = 0; - count = 0; - } - - if (count < 0 || count > MAXHEIGHT) count = 0; - if (dest_y + count >= swrenderer::drawerargs::dc_destheight) - count = swrenderer::drawerargs::dc_destheight - dest_y; - } - public: - DrawerCommand() - { - _dest_y = static_cast((swrenderer::drawerargs::dc_dest - swrenderer::drawerargs::dc_destorg) / (swrenderer::drawerargs::dc_pitch)); - } - virtual ~DrawerCommand() { } virtual void Execute(DrawerThread *thread) = 0; @@ -152,20 +111,30 @@ public: void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data, const char *reason, bool fatal)); -// Manages queueing up commands and executing them on worker threads -class DrawerCommandQueue +class DrawerCommandQueue; +typedef std::shared_ptr DrawerCommandQueuePtr; + +class DrawerThreads { - enum { memorypool_size = 16 * 1024 * 1024 }; - char memorypool[memorypool_size]; - size_t memorypool_pos = 0; - - std::vector commands; +public: + // Runs the collected commands on worker threads + static void Execute(const std::vector &queues); + +private: + DrawerThreads(); + ~DrawerThreads(); + + void StartThreads(); + void StopThreads(); + static DrawerThreads *Instance(); + static void ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal); + std::vector threads; std::mutex start_mutex; std::condition_variable start_condition; - std::vector active_commands; + std::vector active_commands; bool shutdown_flag = false; int run_id = 0; @@ -179,63 +148,45 @@ class DrawerCommandQueue DrawerThread single_core_thread; int num_passes = 1; int rows_in_pass = MAXHEIGHT; + + friend class DrawerCommandQueue; +}; - void StartThreads(); - void StopThreads(); - void Finish(); - - static DrawerCommandQueue *Instance(); - static void ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal); - - DrawerCommandQueue(); - ~DrawerCommandQueue(); +namespace swrenderer { class RenderThread; } +class DrawerCommandQueue +{ public: - // Allocate memory valid for the duration of a command execution - static void* AllocMemory(size_t size); - + DrawerCommandQueue(swrenderer::RenderThread *renderthread); + + void Clear() { commands.clear(); } + // Queue command to be executed by drawer worker threads template - static void QueueCommand(Types &&... args) + void Push(Types &&... args) { - auto queue = Instance(); - if (queue->threaded_render == 0 || !r_multithreaded) + DrawerThreads *threads = DrawerThreads::Instance(); + if (ThreadedRender && r_multithreaded) { - T command(std::forward(args)...); - VectoredTryCatch(&command, - [](void *data) - { - T *c = (T*)data; - c->Execute(&Instance()->single_core_thread); - }, - [](void *data, const char *reason, bool fatal) - { - T *c = (T*)data; - ReportDrawerError(c, false, reason, fatal); - }); + void *ptr = AllocMemory(sizeof(T)); + T *command = new (ptr)T(std::forward(args)...); + commands.push_back(command); } else { - void *ptr = AllocMemory(sizeof(T)); - if (!ptr) // Out of memory - render what we got - { - queue->Finish(); - ptr = AllocMemory(sizeof(T)); - if (!ptr) - return; - } - T *command = new (ptr)T(std::forward(args)...); - queue->commands.push_back(command); + T command(std::forward(args)...); + command.Execute(&threads->single_core_thread); } } - - // Redirects all drawing commands to worker threads until End is called - // Begin/End blocks can be nested. - static void Begin(); - - // End redirection and wait until all worker threads finished executing - static void End(); - - // Waits until all worker threads finished executing - static void WaitForWorkers(); + + bool ThreadedRender = true; + +private: + // Allocate memory valid for the duration of a command execution + void *AllocMemory(size_t size); + + std::vector commands; + swrenderer::RenderThread *renderthread; + + friend class DrawerThreads; }; diff --git a/src/swrenderer/line/r_fogboundary.cpp b/src/swrenderer/line/r_fogboundary.cpp new file mode 100644 index 0000000000..8bd907c708 --- /dev/null +++ b/src/swrenderer/line/r_fogboundary.cpp @@ -0,0 +1,148 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/line/r_fogboundary.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/scene/r_light.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +namespace swrenderer +{ + void RenderFogBoundary::Render(RenderThread *thread, int x1, int x2, short *uclip, short *dclip, int wallshade, float lightleft, float lightstep, FDynamicColormap *basecolormap) + { + // This is essentially the same as R_MapVisPlane but with an extra step + // to create new horizontal spans whenever the light changes enough that + // we need to use a new colormap. + + float light = lightleft + lightstep*(x2 - x1 - 1); + int x = x2 - 1; + int t2 = uclip[x]; + int b2 = dclip[x]; + int rcolormap = GETPALOOKUP(light, wallshade); + int lcolormap; + uint8_t *basecolormapdata = basecolormap->Maps; + + if (b2 > t2) + { + fillshort(spanend + t2, b2 - t2, x); + } + + drawerargs.SetLight(basecolormap, (float)light, wallshade); + + uint8_t *fake_dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); + + for (--x; x >= x1; --x) + { + int t1 = uclip[x]; + int b1 = dclip[x]; + const int xr = x + 1; + int stop; + + light -= lightstep; + lcolormap = GETPALOOKUP(light, wallshade); + if (lcolormap != rcolormap) + { + if (t2 < b2 && rcolormap != 0) + { // Colormap 0 is always the identity map, so rendering it is + // just a waste of time. + RenderSection(thread, t2, b2, xr); + } + if (t1 < t2) t2 = t1; + if (b1 > b2) b2 = b1; + if (t2 < b2) + { + fillshort(spanend + t2, b2 - t2, x); + } + rcolormap = lcolormap; + drawerargs.SetLight(basecolormap, (float)light, wallshade); + fake_dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); + } + else + { + if (fake_dc_colormap != basecolormapdata) + { + stop = MIN(t1, b2); + while (t2 < stop) + { + int y = t2++; + drawerargs.DrawFogBoundaryLine(thread, y, xr, spanend[y]); + } + stop = MAX(b1, t2); + while (b2 > stop) + { + int y = --b2; + drawerargs.DrawFogBoundaryLine(thread, y, xr, spanend[y]); + } + } + else + { + t2 = MAX(t2, MIN(t1, b2)); + b2 = MIN(b2, MAX(b1, t2)); + } + + stop = MIN(t2, b1); + while (t1 < stop) + { + spanend[t1++] = x; + } + stop = MAX(b2, t2); + while (b1 > stop) + { + spanend[--b1] = x; + } + } + + t2 = uclip[x]; + b2 = dclip[x]; + } + if (t2 < b2 && rcolormap != 0) + { + RenderSection(thread, t2, b2, x1); + } + } + + void RenderFogBoundary::RenderSection(RenderThread *thread, int y, int y2, int x1) + { + for (; y < y2; ++y) + { + drawerargs.DrawFogBoundaryLine(thread, y, x1, spanend[y]); + } + } +} diff --git a/src/swrenderer/line/r_fogboundary.h b/src/swrenderer/line/r_fogboundary.h new file mode 100644 index 0000000000..c7df265a4b --- /dev/null +++ b/src/swrenderer/line/r_fogboundary.h @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/viewport/r_spandrawer.h" + +namespace swrenderer +{ + class RenderThread; + + class RenderFogBoundary + { + public: + void Render(RenderThread *thread, int x1, int x2, short *uclip, short *dclip, int wallshade, float lightleft, float lightstep, FDynamicColormap *basecolormap); + + private: + void RenderSection(RenderThread *thread, int y, int y2, int x1); + + short spanend[MAXHEIGHT]; + SpanDrawerArgs drawerargs; + }; +} diff --git a/src/swrenderer/line/r_line.cpp b/src/swrenderer/line/r_line.cpp new file mode 100644 index 0000000000..0352a879fd --- /dev/null +++ b/src/swrenderer/line/r_line.cpp @@ -0,0 +1,1375 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "r_sky.h" +#include "v_video.h" +#include "m_swap.h" +#include "w_wad.h" +#include "stats.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "g_levellocals.h" +#include "r_wallsetup.h" +#include "v_palette.h" +#include "r_utility.h" +#include "r_data/colormaps.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/line/r_line.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/things/r_decal.h" +#include "swrenderer/r_renderthread.h" + +CVAR(Bool, r_fogboundary, true, 0) +CVAR(Bool, r_drawmirrors, true, 0) +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + SWRenderLine::SWRenderLine(RenderThread *thread) + { + Thread = thread; + } + + void SWRenderLine::Render(seg_t *line, subsector_t *subsector, sector_t *sector, sector_t *fakebacksector, VisiblePlane *linefloorplane, VisiblePlane *lineceilingplane, bool infog, FDynamicColormap *colormap) + { + mSubsector = subsector; + mFrontSector = sector; + mBackSector = fakebacksector; + mFloorPlane = linefloorplane; + mCeilingPlane = lineceilingplane; + foggy = infog; + basecolormap = colormap; + mLineSegment = line; + + DVector2 pt1 = line->v1->fPos() - Thread->Viewport->viewpoint.Pos; + DVector2 pt2 = line->v2->fPos() - Thread->Viewport->viewpoint.Pos; + + // Reject lines not facing viewer + if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) + return; + + if (WallC.Init(Thread, pt1, pt2, 32.0 / (1 << 12))) + return; + + RenderPortal *renderportal = Thread->Portal.get(); + if (WallC.sx1 >= renderportal->WindowRight || WallC.sx2 <= renderportal->WindowLeft) + return; + + if (line->linedef == nullptr) + { + if (Thread->ClipSegments->Check(WallC.sx1, WallC.sx2)) + { + mSubsector->flags |= SSECF_DRAWN; + } + return; + } + + // reject lines that aren't seen from the portal (if any) + // [ZZ] 10.01.2016: lines inside a skybox shouldn't be clipped, although this imposes some limitations on portals in skyboxes. + if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && P_ClipLineToPortal(line->linedef, renderportal->CurrentPortal->dst, Thread->Viewport->viewpoint.Pos)) + return; + + vertex_t *v1 = line->linedef->v1; + vertex_t *v2 = line->linedef->v2; + + if ((v1 == line->v1 && v2 == line->v2) || (v2 == line->v1 && v1 == line->v2)) + { // The seg is the entire wall. + WallT.InitFromWallCoords(Thread, &WallC); + } + else + { // The seg is only part of the wall. + if (line->linedef->sidedef[0] != line->sidedef) + { + swapvalues(v1, v2); + } + WallT.InitFromLine(Thread, v1->fPos() - Thread->Viewport->viewpoint.Pos, v2->fPos() - Thread->Viewport->viewpoint.Pos); + } + + mFrontCeilingZ1 = mFrontSector->ceilingplane.ZatPoint(line->v1); + mFrontFloorZ1 = mFrontSector->floorplane.ZatPoint(line->v1); + mFrontCeilingZ2 = mFrontSector->ceilingplane.ZatPoint(line->v2); + mFrontFloorZ2 = mFrontSector->floorplane.ZatPoint(line->v2); + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + if (!(clip3d->fake3D & FAKE3D_FAKEBACK)) + { + mBackSector = line->backsector; + } + + if (mBackSector) + { + // kg3D - its fake, no transfer_heights + if (!(clip3d->fake3D & FAKE3D_FAKEBACK)) + { // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water + mBackSector = Thread->OpaquePass->FakeFlat(mBackSector, &tempsec, nullptr, nullptr, mLineSegment, WallC.sx1, WallC.sx2, mFrontCeilingZ1, mFrontCeilingZ2); + } + + mBackCeilingZ1 = mBackSector->ceilingplane.ZatPoint(line->v1); + mBackFloorZ1 = mBackSector->floorplane.ZatPoint(line->v1); + mBackCeilingZ2 = mBackSector->ceilingplane.ZatPoint(line->v2); + mBackFloorZ2 = mBackSector->floorplane.ZatPoint(line->v2); + + if (clip3d->fake3D & FAKE3D_FAKEBACK) + { + if (mFrontFloorZ1 >= mBackFloorZ1 && mFrontFloorZ2 >= mBackFloorZ2) + { + clip3d->fake3D |= FAKE3D_CLIPBOTFRONT; + } + if (mFrontCeilingZ1 <= mBackCeilingZ1 && mFrontCeilingZ2 <= mBackCeilingZ2) + { + clip3d->fake3D |= FAKE3D_CLIPTOPFRONT; + } + } + } + + mDoorClosed = IsDoorClosed(); + + if (IsInvisibleLine()) + { + // When using GL nodes, do a clipping test for these lines so we can + // mark their subsectors as visible for automap texturing. + if (hasglnodes && !(mSubsector->flags & SSECF_DRAWN)) + { + if (Thread->ClipSegments->Check(WallC.sx1, WallC.sx2)) + { + mSubsector->flags |= SSECF_DRAWN; + } + } + return; + } + + rw_prepped = false; + + bool visible = Thread->ClipSegments->Clip(WallC.sx1, WallC.sx2, IsSolid(), this); + + if (visible) + { + mSubsector->flags |= SSECF_DRAWN; + } + } + + bool SWRenderLine::IsInvisibleLine() const + { + // Reject empty lines used for triggers and special events. + // Identical floor and ceiling on both sides, identical light levels + // on both sides, and no middle texture. + + if (!mBackSector) return false; + + // Portal + if (mLineSegment->linedef->isVisualPortal() && mLineSegment->sidedef == mLineSegment->linedef->sidedef[0]) return false; + + // Closed door. + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return false; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return false; + if (IsDoorClosed()) return false; + + // Window. + if (mFrontSector->ceilingplane != mBackSector->ceilingplane || mFrontSector->floorplane != mBackSector->floorplane) return false; + if (SkyboxCompare(mFrontSector, mBackSector)) return false; + + if (mBackSector->lightlevel != mFrontSector->lightlevel) return false; + if (mBackSector->GetTexture(sector_t::floor) != mFrontSector->GetTexture(sector_t::floor)) return false; + if (mBackSector->GetTexture(sector_t::ceiling) != mFrontSector->GetTexture(sector_t::ceiling)) return false; + if (mLineSegment->sidedef->GetTexture(side_t::mid).isValid()) return false; + + if (mBackSector->planes[sector_t::floor].xform != mFrontSector->planes[sector_t::floor].xform) return false; + if (mBackSector->planes[sector_t::ceiling].xform != mFrontSector->planes[sector_t::ceiling].xform) return false; + + if (mBackSector->GetPlaneLight(sector_t::floor) != mFrontSector->GetPlaneLight(sector_t::floor)) return false; + if (mBackSector->GetPlaneLight(sector_t::ceiling) != mFrontSector->GetPlaneLight(sector_t::ceiling)) return false; + if (mBackSector->GetVisFlags(sector_t::floor) != mFrontSector->GetVisFlags(sector_t::floor)) return false; + if (mBackSector->GetVisFlags(sector_t::ceiling) != mFrontSector->GetVisFlags(sector_t::ceiling)) return false; + + if (mBackSector->ColorMap != mFrontSector->ColorMap) return false; + + if (mFrontSector->e && mFrontSector->e->XFloor.lightlist.Size()) return false; + if (mBackSector->e && mBackSector->e->XFloor.lightlist.Size()) return false; + + return true; + } + + bool SWRenderLine::IsSolid() const + { + // One sided + if (mBackSector == nullptr) return true; + + // Portal + if (mLineSegment->linedef->isVisualPortal() && mLineSegment->sidedef == mLineSegment->linedef->sidedef[0]) return true; + + // Closed door + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return true; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return true; + if (IsDoorClosed()) return true; + + return false; + } + + bool SWRenderLine::IsDoorClosed() const + { + if (!mBackSector) return false; + + // Portal + if (mLineSegment->linedef->isVisualPortal() && mLineSegment->sidedef == mLineSegment->linedef->sidedef[0]) return false; + + // Closed door. + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return false; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return false; + + // properly render skies (consider door "open" if both ceilings are sky) + if (mBackSector->GetTexture(sector_t::ceiling) == skyflatnum && mFrontSector->GetTexture(sector_t::ceiling) == skyflatnum) return false; + + // if door is closed because back is shut: + if (!(mBackCeilingZ1 <= mBackFloorZ1 && mBackCeilingZ2 <= mBackFloorZ2)) return false; + + // preserve a kind of transparent door/lift special effect: + if (((mBackCeilingZ1 >= mFrontCeilingZ1 && mBackCeilingZ2 >= mFrontCeilingZ2) || mLineSegment->sidedef->GetTexture(side_t::top).isValid()) + && ((mBackFloorZ1 <= mFrontFloorZ1 && mBackFloorZ2 <= mFrontFloorZ2) || mLineSegment->sidedef->GetTexture(side_t::bottom).isValid())) + { + // killough 1/18/98 -- This function is used to fix the automap bug which + // showed lines behind closed doors simply because the door had a dropoff. + // + // It assumes that Doom has already ruled out a door being closed because + // of front-back closure (e.g. front floor is taller than back ceiling). + + // This fixes the automap floor height bug -- killough 1/18/98: + // killough 4/7/98: optimize: save result in doorclosed for use in r_segs.c + return true; + } + + return false; + } + + bool SWRenderLine::SkyboxCompare(sector_t *frontsector, sector_t *backsector) const + { + FSectorPortal *frontc = frontsector->GetPortal(sector_t::ceiling); + FSectorPortal *frontf = frontsector->GetPortal(sector_t::floor); + FSectorPortal *backc = backsector->GetPortal(sector_t::ceiling); + FSectorPortal *backf = backsector->GetPortal(sector_t::floor); + + // return true if any of the planes has a linedef-based portal (unless both sides have the same one. + // Ideally this should also check thing based portals but the omission of this check had been abused to hell and back for those. + // (Note: This may require a compatibility option if some maps ran into this for line based portals as well.) + if (!frontc->MergeAllowed()) return (frontc != backc); + if (!frontf->MergeAllowed()) return (frontf != backf); + if (!backc->MergeAllowed()) return true; + if (!backf->MergeAllowed()) return true; + return false; + } + + // A wall segment will be drawn between start and stop pixels (inclusive). + bool SWRenderLine::RenderWallSegment(int start, int stop) + { + int i; + bool maskedtexture = false; + +#ifdef RANGECHECK + if (start >= viewwidth || start >= stop) + I_FatalError("Bad R_StoreWallRange: %i to %i", start, stop); +#endif + + if (!rw_prepped) + { + rw_prepped = true; + SetWallVariables(true); + } + + side_t *sidedef = mLineSegment->sidedef; + + RenderPortal *renderportal = Thread->Portal.get(); + + DrawSegment *draw_segment = Thread->FrameMemory->NewObject(); + Thread->DrawSegments->Push(draw_segment); + + draw_segment->CurrentPortalUniq = renderportal->CurrentPortalUniq; + draw_segment->sx1 = WallC.sx1; + draw_segment->sx2 = WallC.sx2; + draw_segment->sz1 = WallC.sz1; + draw_segment->sz2 = WallC.sz2; + draw_segment->cx = WallC.tleft.X;; + draw_segment->cy = WallC.tleft.Y; + draw_segment->cdx = WallC.tright.X - WallC.tleft.X; + draw_segment->cdy = WallC.tright.Y - WallC.tleft.Y; + draw_segment->tmapvals = WallT; + draw_segment->siz1 = 1 / WallC.sz1; + draw_segment->siz2 = 1 / WallC.sz2; + draw_segment->x1 = start; + draw_segment->x2 = stop; + draw_segment->curline = mLineSegment; + draw_segment->bFogBoundary = false; + draw_segment->bFakeBoundary = false; + draw_segment->foggy = foggy; + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + if (clip3d->fake3D & FAKE3D_FAKEMASK) draw_segment->fake = 1; + else draw_segment->fake = 0; + + draw_segment->sprtopclip = nullptr; + draw_segment->sprbottomclip = nullptr; + draw_segment->maskedtexturecol = nullptr; + draw_segment->bkup = nullptr; + draw_segment->swall = nullptr; + + bool markportal = ShouldMarkPortal(); + + if (markportal) + { + draw_segment->silhouette = SIL_BOTH; + } + else if (mBackSector == NULL) + { + draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory(stop - start); + draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory(stop - start); + fillshort(draw_segment->sprtopclip, stop - start, viewheight); + memset(draw_segment->sprbottomclip, -1, (stop - start) * sizeof(short)); + draw_segment->silhouette = SIL_BOTH; + } + else + { + // two sided line + draw_segment->silhouette = 0; + + if (mFrontFloorZ1 > mBackFloorZ1 || mFrontFloorZ2 > mBackFloorZ2 || + mBackSector->floorplane.PointOnSide(Thread->Viewport->viewpoint.Pos) < 0) + { + draw_segment->silhouette = SIL_BOTTOM; + } + + if (mFrontCeilingZ1 < mBackCeilingZ1 || mFrontCeilingZ2 < mBackCeilingZ2 || + mBackSector->ceilingplane.PointOnSide(Thread->Viewport->viewpoint.Pos) < 0) + { + draw_segment->silhouette |= SIL_TOP; + } + + // killough 1/17/98: this test is required if the fix + // for the automap bug (r_bsp.c) is used, or else some + // sprites will be displayed behind closed doors. That + // fix prevents lines behind closed doors with dropoffs + // from being displayed on the automap. + // + // killough 4/7/98: make doorclosed external variable + + { + if (mDoorClosed || (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2)) + { + draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory(stop - start); + memset(draw_segment->sprbottomclip, -1, (stop - start) * sizeof(short)); + draw_segment->silhouette |= SIL_BOTTOM; + } + if (mDoorClosed || (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2)) + { // killough 1/17/98, 2/8/98 + draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory(stop - start); + fillshort(draw_segment->sprtopclip, stop - start, viewheight); + draw_segment->silhouette |= SIL_TOP; + } + } + + if (!draw_segment->fake && r_3dfloors && mBackSector->e && mBackSector->e->XFloor.ffloors.Size()) { + for (i = 0; i < (int)mBackSector->e->XFloor.ffloors.Size(); i++) { + F3DFloor *rover = mBackSector->e->XFloor.ffloors[i]; + if (rover->flags & FF_RENDERSIDES && (!(rover->flags & FF_INVERTSIDES) || rover->flags & FF_ALLSIDES)) { + draw_segment->bFakeBoundary |= 1; + break; + } + } + } + if (!draw_segment->fake && r_3dfloors && mFrontSector->e && mFrontSector->e->XFloor.ffloors.Size()) { + for (i = 0; i < (int)mFrontSector->e->XFloor.ffloors.Size(); i++) { + F3DFloor *rover = mFrontSector->e->XFloor.ffloors[i]; + if (rover->flags & FF_RENDERSIDES && (rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) { + draw_segment->bFakeBoundary |= 2; + break; + } + } + } + // kg3D - no for fakes + if (!draw_segment->fake) + // allocate space for masked texture tables, if needed + // [RH] Don't just allocate the space; fill it in too. + if ((TexMan(sidedef->GetTexture(side_t::mid), true)->UseType != FTexture::TEX_Null || draw_segment->bFakeBoundary || IsFogBoundary(mFrontSector, mBackSector)) && + (mCeilingClipped != ProjectedWallCull::OutsideBelow || !sidedef->GetTexture(side_t::top).isValid()) && + (mFloorClipped != ProjectedWallCull::OutsideAbove || !sidedef->GetTexture(side_t::bottom).isValid()) && + (WallC.sz1 >= TOO_CLOSE_Z && WallC.sz2 >= TOO_CLOSE_Z)) + { + float *swal; + fixed_t *lwal; + int i; + + maskedtexture = true; + + // kg3D - backup for mid and fake walls + draw_segment->bkup = Thread->FrameMemory->AllocMemory(stop - start); + memcpy(draw_segment->bkup, &Thread->OpaquePass->ceilingclip[start], sizeof(short)*(stop - start)); + + draw_segment->bFogBoundary = IsFogBoundary(mFrontSector, mBackSector); + if (sidedef->GetTexture(side_t::mid).isValid() || draw_segment->bFakeBoundary) + { + if (sidedef->GetTexture(side_t::mid).isValid()) + draw_segment->bFakeBoundary |= 4; // it is also mid texture + + draw_segment->maskedtexturecol = Thread->FrameMemory->AllocMemory(stop - start); + draw_segment->swall = Thread->FrameMemory->AllocMemory(stop - start); + + lwal = draw_segment->maskedtexturecol; + swal = draw_segment->swall; + FTexture *pic = TexMan(sidedef->GetTexture(side_t::mid), true); + double yscale = pic->Scale.Y * sidedef->GetTextureYScale(side_t::mid); + fixed_t xoffset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); + + if (pic->bWorldPanning) + { + xoffset = xs_RoundToInt(xoffset * lwallscale); + } + + for (i = start; i < stop; i++) + { + *lwal++ = walltexcoords.UPos[i] + xoffset; + *swal++ = walltexcoords.VStep[i]; + } + + double istart = draw_segment->swall[0] * yscale; + double iend = *(swal - 1) * yscale; +#if 0 + ///This was for avoiding overflow when using fixed point. It might not be needed anymore. + const double mini = 3 / 65536.0; + if (istart < mini && istart >= 0) istart = mini; + if (istart > -mini && istart < 0) istart = -mini; + if (iend < mini && iend >= 0) iend = mini; + if (iend > -mini && iend < 0) iend = -mini; +#endif + istart = 1 / istart; + iend = 1 / iend; + draw_segment->yscale = (float)yscale; + draw_segment->iscale = (float)istart; + if (stop - start > 0) + { + draw_segment->iscalestep = float((iend - istart) / (stop - start)); + } + else + { + draw_segment->iscalestep = 0; + } + } + draw_segment->light = rw_lightleft + rw_lightstep * (start - WallC.sx1); + draw_segment->lightstep = rw_lightstep; + + // Masked mMiddlePart.Textures should get the light level from the sector they reference, + // not from the current subsector, which is what the current wallshade value + // comes from. We make an exeption for polyobjects, however, since their "home" + // sector should be whichever one they move into. + if (mLineSegment->sidedef->Flags & WALLF_POLYOBJ) + { + draw_segment->shade = wallshade; + } + else + { + draw_segment->shade = LightVisibility::LightLevelToShade(mLineSegment->sidedef->GetLightLevel(foggy, mLineSegment->frontsector->lightlevel) + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), foggy); + } + + if (draw_segment->bFogBoundary || draw_segment->maskedtexturecol != nullptr) + { + Thread->DrawSegments->PushInteresting(draw_segment); + } + } + } + + ClipSegmentTopBottom(start, stop); + + MarkCeilingPlane(start, stop); + MarkFloorPlane(start, stop); + Mark3DFloors(start, stop); + + if (clip3d->fake3D & FAKE3D_FAKEMASK) + { + return (clip3d->fake3D & FAKE3D_FAKEMASK) == 0; + } + + MarkOpaquePassClip(start, stop); + + // save sprite clipping info + if (((draw_segment->silhouette & SIL_TOP) || maskedtexture) && draw_segment->sprtopclip == nullptr) + { + draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory(stop - start); + memcpy(draw_segment->sprtopclip, &Thread->OpaquePass->ceilingclip[start], sizeof(short)*(stop - start)); + } + + if (((draw_segment->silhouette & SIL_BOTTOM) || maskedtexture) && draw_segment->sprbottomclip == nullptr) + { + draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory(stop - start); + memcpy(draw_segment->sprbottomclip, &Thread->OpaquePass->floorclip[start], sizeof(short)*(stop - start)); + } + + if (maskedtexture && mLineSegment->sidedef->GetTexture(side_t::mid).isValid()) + { + draw_segment->silhouette |= SIL_TOP | SIL_BOTTOM; + } + + RenderMiddleTexture(start, stop); + RenderTopTexture(start, stop); + RenderBottomTexture(start, stop); + + // [RH] Draw any decals bound to the seg + // [ZZ] Only if not an active mirror + if (!markportal) + { + RenderDecal::RenderDecals(Thread, mLineSegment->sidedef, draw_segment, wallshade, rw_lightleft, rw_lightstep, mLineSegment, WallC, foggy, basecolormap, walltop.ScreenY, wallbottom.ScreenY); + } + + if (markportal) + { + Thread->Portal->AddLinePortal(mLineSegment->linedef, draw_segment->x1, draw_segment->x2, draw_segment->sprtopclip, draw_segment->sprbottomclip); + } + + return (clip3d->fake3D & FAKE3D_FAKEMASK) == 0; + } + + bool SWRenderLine::ShouldMarkFloor() const + { + if (!mFloorPlane) + return false; + + // deep water check + if (mFrontSector->GetHeightSec() == nullptr) + { + int planeside = mFrontSector->floorplane.PointOnSide(Thread->Viewport->viewpoint.Pos); + if (mFrontSector->floorplane.fC() < 0) // 3D floors have the floor backwards + planeside = -planeside; + if (planeside <= 0) // above view plane + return false; + } + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) + { + return true; + } + else if (mBackSector == nullptr) // single sided line + { + return true; + } + else // two-sided line + { + if (linedef->isVisualPortal()) return true; + + // closed door + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return true; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return true; + + if (mBackSector->floorplane != mFrontSector->floorplane) return true; + if (mBackSector->lightlevel != mFrontSector->lightlevel) return true; + if (mBackSector->GetTexture(sector_t::floor) != mFrontSector->GetTexture(sector_t::floor)) return true; + if (mBackSector->GetPlaneLight(sector_t::floor) != mFrontSector->GetPlaneLight(sector_t::floor)) return true; + + // Add checks for (x,y) offsets + if (mBackSector->planes[sector_t::floor].xform != mFrontSector->planes[sector_t::floor].xform) return true; + if (mBackSector->GetAlpha(sector_t::floor) != mFrontSector->GetAlpha(sector_t::floor)) return true; + + // prevent 2s normals from bleeding through deep water + if (mFrontSector->heightsec) return true; + + if (mBackSector->GetVisFlags(sector_t::floor) != mFrontSector->GetVisFlags(sector_t::floor)) return true; + if (mBackSector->ColorMap != mFrontSector->ColorMap) return true; + if (mFrontSector->e && mFrontSector->e->XFloor.lightlist.Size()) return true; + if (mBackSector->e && mBackSector->e->XFloor.lightlist.Size()) return true; + + if (sidedef->GetTexture(side_t::mid).isValid() && ((ib_compatflags & BCOMPATF_CLIPMIDTEX) || (linedef->flags & (ML_CLIP_MIDTEX | ML_WRAP_MIDTEX)) || sidedef->Flags & (WALLF_CLIP_MIDTEX | WALLF_WRAP_MIDTEX))) return true; + + return false; + } + } + + bool SWRenderLine::ShouldMarkCeiling() const + { + if (!mCeilingPlane) + return false; + + // deep water check + if (mFrontSector->GetHeightSec() == nullptr && mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum) + { + int planeside = mFrontSector->ceilingplane.PointOnSide(Thread->Viewport->viewpoint.Pos); + if (mFrontSector->ceilingplane.fC() > 0) // 3D floors have the ceiling backwards + planeside = -planeside; + if (planeside <= 0) // below view plane + return false; + } + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) + { + return true; + } + else if (mBackSector == nullptr) // single sided line + { + return true; + } + else // two-sided line + { + if (linedef->isVisualPortal()) return true; + + // closed door + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return true; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return true; + + if (mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum || mBackSector->GetTexture(sector_t::ceiling) != skyflatnum) + { + if (mBackSector->ceilingplane != mFrontSector->ceilingplane) return true; + if (mBackSector->lightlevel != mFrontSector->lightlevel) return true; + if (mBackSector->GetTexture(sector_t::ceiling) != mFrontSector->GetTexture(sector_t::ceiling)) return true; + + // Add checks for (x,y) offsets + if (mBackSector->planes[sector_t::ceiling].xform != mFrontSector->planes[sector_t::ceiling].xform) return true; + if (mBackSector->GetAlpha(sector_t::ceiling) != mFrontSector->GetAlpha(sector_t::ceiling)) return true; + + // prevent 2s normals from bleeding through fake ceilings + if (mFrontSector->heightsec && mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum) return true; + + if (mBackSector->GetPlaneLight(sector_t::ceiling) != mFrontSector->GetPlaneLight(sector_t::ceiling)) return true; + if (mBackSector->GetFlags(sector_t::ceiling) != mFrontSector->GetFlags(sector_t::ceiling)) return true; + + if (mBackSector->ColorMap != mFrontSector->ColorMap) return true; + if (mFrontSector->e && mFrontSector->e->XFloor.lightlist.Size()) return true; + if (mBackSector->e && mBackSector->e->XFloor.lightlist.Size()) return true; + + if (sidedef->GetTexture(side_t::mid).isValid()) + { + if (ib_compatflags & BCOMPATF_CLIPMIDTEX) return true; + if (linedef->flags & (ML_CLIP_MIDTEX | ML_WRAP_MIDTEX)) return true; + if (sidedef->Flags & (WALLF_CLIP_MIDTEX | WALLF_WRAP_MIDTEX)) return true; + } + } + return false; + } + } + + bool SWRenderLine::ShouldMarkPortal() const + { + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) + { + return true; + } + else + { + return linedef->isVisualPortal(); + } + } + + void SWRenderLine::SetWallVariables(bool needlights) + { + RenderPortal *renderportal = Thread->Portal.get(); + + bool rw_havehigh = false; + bool rw_havelow = false; + if (mBackSector) + { + // Cannot make these walls solid, because it can result in + // sprite clipping problems for sprites near the wall + if (mFrontCeilingZ1 > mBackCeilingZ1 || mFrontCeilingZ2 > mBackCeilingZ2) + { + rw_havehigh = true; + wallupper.Project(Thread->Viewport.get(), mBackSector->ceilingplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP); + } + if (mFrontFloorZ1 < mBackFloorZ1 || mFrontFloorZ2 < mBackFloorZ2) + { + rw_havelow = true; + walllower.Project(Thread->Viewport.get(), mBackSector->floorplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP); + } + } + + if (mLineSegment->linedef->special == Line_Horizon) + { + // Be aware: Line_Horizon does not work properly with sloped planes + fillshort(walltop.ScreenY + WallC.sx1, WallC.sx2 - WallC.sx1, Thread->Viewport->viewwindow.centery); + fillshort(wallbottom.ScreenY + WallC.sx1, WallC.sx2 - WallC.sx1, Thread->Viewport->viewwindow.centery); + } + else + { + mCeilingClipped = walltop.Project(Thread->Viewport.get(), mFrontSector->ceilingplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP); + mFloorClipped = wallbottom.Project(Thread->Viewport.get(), mFrontSector->floorplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP); + } + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + + // mark the segment as visible for auto map + if (!Thread->Scene->DontMapLines()) linedef->flags |= ML_MAPPED; + + markfloor = ShouldMarkFloor(); + markceiling = ShouldMarkCeiling(); + + SetTopTexture(); + SetMiddleTexture(); + SetBottomTexture(); + + if (mBackSector && !(sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors))) + { + // skyhack to allow height changes in outdoor areas + if (mFrontSector->GetTexture(sector_t::ceiling) == skyflatnum && + mBackSector->GetTexture(sector_t::ceiling) == skyflatnum) + { + if (rw_havehigh) + { // front ceiling is above back ceiling + memcpy(&walltop.ScreenY[WallC.sx1], &wallupper.ScreenY[WallC.sx1], (WallC.sx2 - WallC.sx1) * sizeof(walltop.ScreenY[0])); + rw_havehigh = false; + } + else if (rw_havelow && mFrontSector->ceilingplane != mBackSector->ceilingplane) + { // back ceiling is above front ceiling + // The check for rw_havelow is not Doom-compliant, but it avoids HoM that + // would otherwise occur because there is space made available for this + // wall but nothing to draw for it. + // Recalculate walltop so that the wall is clipped by the back sector's + // ceiling instead of the front sector's ceiling. + walltop.Project(Thread->Viewport.get(), mBackSector->ceilingplane, &WallC, mLineSegment, Thread->Portal->MirrorFlags & RF_XFLIP); + } + } + } + + FTexture *midtex = TexMan(sidedef->GetTexture(side_t::mid), true); + + bool segtextured = midtex != NULL || mTopPart.Texture != NULL || mBottomPart.Texture != NULL; + + // calculate light table + if (needlights && (segtextured || (mBackSector && IsFogBoundary(mFrontSector, mBackSector)))) + { + lwallscale = + midtex ? (midtex->Scale.X * sidedef->GetTextureXScale(side_t::mid)) : + mTopPart.Texture ? (mTopPart.Texture->Scale.X * sidedef->GetTextureXScale(side_t::top)) : + mBottomPart.Texture ? (mBottomPart.Texture->Scale.X * sidedef->GetTextureXScale(side_t::bottom)) : + 1.; + + walltexcoords.Project(Thread->Viewport.get(), sidedef->TexelLength * lwallscale, WallC.sx1, WallC.sx2, WallT); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap() == nullptr && cameraLight->FixedLightLevel() < 0) + { + wallshade = LightVisibility::LightLevelToShade(mLineSegment->sidedef->GetLightLevel(foggy, mFrontSector->lightlevel) + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), foggy); + double GlobVis = LightVisibility::Instance()->WallGlobVis(foggy); + rw_lightleft = float(GlobVis / WallC.sz1); + rw_lightstep = float((GlobVis / WallC.sz2 - rw_lightleft) / (WallC.sx2 - WallC.sx1)); + } + else + { + rw_lightleft = 1; + rw_lightstep = 0; + } + } + } + + void SWRenderLine::SetTopTexture() + { + mTopPart.Texture = nullptr; + + if (!(mFrontCeilingZ1 > mBackCeilingZ1 || mFrontCeilingZ2 > mBackCeilingZ2)) return; + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) return; + if (!mBackSector) return; + + // No top texture for skyhack lines + if (mFrontSector->GetTexture(sector_t::ceiling) == skyflatnum && mBackSector->GetTexture(sector_t::ceiling) == skyflatnum) return; + + mTopPart.Texture = TexMan(sidedef->GetTexture(side_t::top), true); + + mTopPart.TextureOffsetU = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::top)); + double rowoffset = sidedef->GetTextureYOffset(side_t::top); + mTopPart.TextureScaleU = sidedef->GetTextureXScale(side_t::top); + mTopPart.TextureScaleV = sidedef->GetTextureYScale(side_t::top); + double yrepeat = mTopPart.Texture->Scale.Y * mTopPart.TextureScaleV; + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGTOP) + { // top of texture at top + mTopPart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat; + if (rowoffset < 0 && mTopPart.Texture != NULL) + { + rowoffset += mTopPart.Texture->GetHeight(); + } + } + else + { // bottom of texture at bottom + mTopPart.TextureMid = (mBackSector->GetPlaneTexZ(sector_t::ceiling) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat + mTopPart.Texture->GetHeight(); + } + } + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGTOP) + { // bottom of texture at top + mTopPart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat + mTopPart.Texture->GetHeight(); + } + else + { // top of texture at bottom + mTopPart.TextureMid = (mBackSector->GetPlaneTexZ(sector_t::ceiling) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat; + } + } + if (mTopPart.Texture->bWorldPanning) + { + mTopPart.TextureMid += rowoffset * yrepeat; + } + else + { + mTopPart.TextureMid += rowoffset; + } + } + + void SWRenderLine::SetMiddleTexture() + { + mMiddlePart.Texture = nullptr; + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) return; + if (mBackSector) return; + + // [RH] Horizon lines do not need to be textured + if (linedef->isVisualPortal()) return; + if (linedef->special == Line_Horizon) return; + + mMiddlePart.Texture = TexMan(sidedef->GetTexture(side_t::mid), true); + mMiddlePart.TextureOffsetU = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); + double rowoffset = sidedef->GetTextureYOffset(side_t::mid); + mMiddlePart.TextureScaleU = sidedef->GetTextureXScale(side_t::mid); + mMiddlePart.TextureScaleV = sidedef->GetTextureYScale(side_t::mid); + double yrepeat = mMiddlePart.Texture->Scale.Y * mMiddlePart.TextureScaleV; + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + mMiddlePart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::floor) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat + mMiddlePart.Texture->GetHeight(); + } + else + { // top of texture at top + mMiddlePart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat; + if (rowoffset < 0 && mMiddlePart.Texture != NULL) + { + rowoffset += mMiddlePart.Texture->GetHeight(); + } + } + } + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGBOTTOM) + { // top of texture at bottom + mMiddlePart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::floor) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat; + } + else + { // bottom of texture at top + mMiddlePart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat + mMiddlePart.Texture->GetHeight(); + } + } + if (mMiddlePart.Texture->bWorldPanning) + { + mMiddlePart.TextureMid += rowoffset * yrepeat; + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + mMiddlePart.TextureMid += rowoffset; + } + } + + void SWRenderLine::SetBottomTexture() + { + mBottomPart.Texture = nullptr; + + if (!(mFrontFloorZ1 < mBackFloorZ1 || mFrontFloorZ2 < mBackFloorZ2)) return; + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) return; + if (!mBackSector) return; + + double frontlowertop = mFrontSector->GetPlaneTexZ(sector_t::ceiling); + if (mFrontSector->GetTexture(sector_t::ceiling) == skyflatnum && mBackSector->GetTexture(sector_t::ceiling) == skyflatnum) + { + // Putting sky ceilings on the front and back of a line alters the way unpegged + // positioning works. + frontlowertop = mBackSector->GetPlaneTexZ(sector_t::ceiling); + } + + mBottomPart.Texture = TexMan(sidedef->GetTexture(side_t::bottom), true); + + mBottomPart.TextureOffsetU = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::bottom)); + double rowoffset = sidedef->GetTextureYOffset(side_t::bottom); + mBottomPart.TextureScaleU = sidedef->GetTextureXScale(side_t::bottom); + mBottomPart.TextureScaleV = sidedef->GetTextureYScale(side_t::bottom); + double yrepeat = mBottomPart.Texture->Scale.Y * mBottomPart.TextureScaleV; + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + mBottomPart.TextureMid = (frontlowertop - Thread->Viewport->viewpoint.Pos.Z) * yrepeat; + } + else + { // top of texture at top + mBottomPart.TextureMid = (mBackSector->GetPlaneTexZ(sector_t::floor) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat; + if (rowoffset < 0 && mBottomPart.Texture != NULL) + { + rowoffset += mBottomPart.Texture->GetHeight(); + } + } + } + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGBOTTOM) + { // top of texture at bottom + mBottomPart.TextureMid = (frontlowertop - Thread->Viewport->viewpoint.Pos.Z) * yrepeat; + } + else + { // bottom of texture at top + mBottomPart.TextureMid = (mBackSector->GetPlaneTexZ(sector_t::floor) - Thread->Viewport->viewpoint.Pos.Z) * yrepeat + mBottomPart.Texture->GetHeight(); + } + } + if (mBottomPart.Texture->bWorldPanning) + { + mBottomPart.TextureMid += rowoffset * yrepeat; + } + else + { + mBottomPart.TextureMid += rowoffset; + } + } + + bool SWRenderLine::IsFogBoundary(sector_t *front, sector_t *back) const + { + return r_fogboundary && CameraLight::Instance()->FixedColormap() == nullptr && front->ColorMap->Fade && + front->ColorMap->Fade != back->ColorMap->Fade && + (front->GetTexture(sector_t::ceiling) != skyflatnum || back->GetTexture(sector_t::ceiling) != skyflatnum); + } + + void SWRenderLine::ClipSegmentTopBottom(int x1, int x2) + { + // clip wall to the floor and ceiling + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + for (int x = x1; x < x2; ++x) + { + if (walltop.ScreenY[x] < ceilingclip[x]) + { + walltop.ScreenY[x] = ceilingclip[x]; + } + if (wallbottom.ScreenY[x] > floorclip[x]) + { + wallbottom.ScreenY[x] = floorclip[x]; + } + } + } + + void SWRenderLine::MarkCeilingPlane(int x1, int x2) + { + // mark ceiling areas + if (markceiling) + { + mCeilingPlane = Thread->PlaneList->GetRange(mCeilingPlane, x1, x2); + + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + for (int x = x1; x < x2; ++x) + { + short top = (clip3d->fakeFloor && clip3d->fake3D & FAKE3D_FAKECEILING) ? clip3d->fakeFloor->ceilingclip[x] : ceilingclip[x]; + short bottom = MIN(walltop.ScreenY[x], floorclip[x]); + if (top < bottom) + { + mCeilingPlane->top[x] = top; + mCeilingPlane->bottom[x] = bottom; + } + } + } + } + + void SWRenderLine::MarkFloorPlane(int x1, int x2) + { + if (markfloor) + { + mFloorPlane = Thread->PlaneList->GetRange(mFloorPlane, x1, x2); + + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + for (int x = x1; x < x2; ++x) + { + short top = MAX(wallbottom.ScreenY[x], ceilingclip[x]); + short bottom = (clip3d->fakeFloor && clip3d->fake3D & FAKE3D_FAKEFLOOR) ? clip3d->fakeFloor->floorclip[x] : floorclip[x]; + if (top < bottom) + { + assert(bottom <= viewheight); + mFloorPlane->top[x] = top; + mFloorPlane->bottom[x] = bottom; + } + } + } + } + + void SWRenderLine::Mark3DFloors(int x1, int x2) + { + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + // kg3D - fake planes clipping + if (clip3d->fake3D & FAKE3D_REFRESHCLIP) + { + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + + if (clip3d->fake3D & FAKE3D_CLIPBOTFRONT) + { + memcpy(clip3d->fakeFloor->floorclip + x1, wallbottom.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + else + { + for (int x = x1; x < x2; ++x) + { + walllower.ScreenY[x] = MIN(MAX(walllower.ScreenY[x], ceilingclip[x]), wallbottom.ScreenY[x]); + } + memcpy(clip3d->fakeFloor->floorclip + x1, walllower.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + if (clip3d->fake3D & FAKE3D_CLIPTOPFRONT) + { + memcpy(clip3d->fakeFloor->ceilingclip + x1, walltop.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + else + { + for (int x = x1; x < x2; ++x) + { + wallupper.ScreenY[x] = MAX(MIN(wallupper.ScreenY[x], floorclip[x]), walltop.ScreenY[x]); + } + memcpy(clip3d->fakeFloor->ceilingclip + x1, wallupper.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + } + } + + void SWRenderLine::MarkOpaquePassClip(int x1, int x2) + { + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + + if (mMiddlePart.Texture) // one sided line + { + fillshort(ceilingclip + x1, x2 - x1, viewheight); + fillshort(floorclip + x1, x2 - x1, 0xffff); + } + else + { // two sided line + if (mTopPart.Texture != NULL && mTopPart.Texture->UseType != FTexture::TEX_Null) + { // top wall + for (int x = x1; x < x2; ++x) + { + wallupper.ScreenY[x] = MAX(MIN(wallupper.ScreenY[x], floorclip[x]), walltop.ScreenY[x]); + } + memcpy(ceilingclip + x1, wallupper.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + else if (markceiling) + { // no top wall + memcpy(ceilingclip + x1, walltop.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + + if (mBottomPart.Texture != NULL && mBottomPart.Texture->UseType != FTexture::TEX_Null) + { // bottom wall + for (int x = x1; x < x2; ++x) + { + walllower.ScreenY[x] = MIN(MAX(walllower.ScreenY[x], ceilingclip[x]), wallbottom.ScreenY[x]); + } + memcpy(floorclip + x1, walllower.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + else if (markfloor) + { // no bottom wall + memcpy(floorclip + x1, wallbottom.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + } + } + + void SWRenderLine::RenderTopTexture(int x1, int x2) + { + if (mMiddlePart.Texture) return; + if (!mTopPart.Texture || mTopPart.Texture->UseType == FTexture::TEX_Null) return; + if (!viewactive) return; + + FTexture *rw_pic = mTopPart.Texture; + double xscale = rw_pic->Scale.X * mTopPart.TextureScaleU; + double yscale = rw_pic->Scale.Y * mTopPart.TextureScaleV; + if (xscale != lwallscale) + { + walltexcoords.ProjectPos(Thread->Viewport.get(), mLineSegment->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2, WallT); + lwallscale = xscale; + } + fixed_t offset; + if (mTopPart.Texture->bWorldPanning) + { + offset = xs_RoundToInt(mTopPart.TextureOffsetU * xscale); + } + else + { + offset = mTopPart.TextureOffsetU; + } + if (xscale < 0) + { + offset = -offset; + } + + WallDrawerArgs drawerargs; + drawerargs.SetStyle(false, false, OPAQUE); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != nullptr) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + + float rw_light = rw_lightleft + rw_lightstep * (x1 - WallC.sx1); + + FLightNode *light_list = (mLineSegment && mLineSegment->sidedef) ? mLineSegment->sidedef->lighthead : nullptr; + + if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr)) + light_list = nullptr; // [SP] Don't draw dynlights if invul/lightamp active + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(drawerargs, mFrontSector, mLineSegment, WallC, rw_pic, x1, x2, walltop.ScreenY, wallupper.ScreenY, mTopPart.TextureMid, walltexcoords.VStep, walltexcoords.UPos, yscale, MAX(mFrontCeilingZ1, mFrontCeilingZ2), MIN(mBackCeilingZ1, mBackCeilingZ2), false, wallshade, offset, rw_light, rw_lightstep, light_list, foggy, basecolormap); + } + + void SWRenderLine::RenderMiddleTexture(int x1, int x2) + { + if (!mMiddlePart.Texture || mMiddlePart.Texture->UseType == FTexture::TEX_Null) return; + if (!viewactive) return; + + FTexture *rw_pic = mMiddlePart.Texture; + double xscale = rw_pic->Scale.X * mMiddlePart.TextureScaleU; + double yscale = rw_pic->Scale.Y * mMiddlePart.TextureScaleV; + if (xscale != lwallscale) + { + walltexcoords.ProjectPos(Thread->Viewport.get(), mLineSegment->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2, WallT); + lwallscale = xscale; + } + fixed_t offset; + if (mMiddlePart.Texture->bWorldPanning) + { + offset = xs_RoundToInt(mMiddlePart.TextureOffsetU * xscale); + } + else + { + offset = mMiddlePart.TextureOffsetU; + } + if (xscale < 0) + { + offset = -offset; + } + + WallDrawerArgs drawerargs; + drawerargs.SetStyle(false, false, OPAQUE); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != nullptr) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + + float rw_light = rw_lightleft + rw_lightstep * (x1 - WallC.sx1); + + FLightNode *light_list = (mLineSegment && mLineSegment->sidedef) ? mLineSegment->sidedef->lighthead : nullptr; + + if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr)) + light_list = nullptr; // [SP] Don't draw dynlights if invul/lightamp active + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(drawerargs, mFrontSector, mLineSegment, WallC, rw_pic, x1, x2, walltop.ScreenY, wallbottom.ScreenY, mMiddlePart.TextureMid, walltexcoords.VStep, walltexcoords.UPos, yscale, MAX(mFrontCeilingZ1, mFrontCeilingZ2), MIN(mFrontFloorZ1, mFrontFloorZ2), false, wallshade, offset, rw_light, rw_lightstep, light_list, foggy, basecolormap); + } + + void SWRenderLine::RenderBottomTexture(int x1, int x2) + { + if (mMiddlePart.Texture) return; + if (!mBottomPart.Texture || mBottomPart.Texture->UseType == FTexture::TEX_Null) return; + if (!viewactive) return; + + FTexture *rw_pic = mBottomPart.Texture; + double xscale = rw_pic->Scale.X * mBottomPart.TextureScaleU; + double yscale = rw_pic->Scale.Y * mBottomPart.TextureScaleV; + if (xscale != lwallscale) + { + walltexcoords.ProjectPos(Thread->Viewport.get(), mLineSegment->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2, WallT); + lwallscale = xscale; + } + fixed_t offset; + if (mBottomPart.Texture->bWorldPanning) + { + offset = xs_RoundToInt(mBottomPart.TextureOffsetU * xscale); + } + else + { + offset = mBottomPart.TextureOffsetU; + } + if (xscale < 0) + { + offset = -offset; + } + + WallDrawerArgs drawerargs; + drawerargs.SetStyle(false, false, OPAQUE); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != nullptr) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + + float rw_light = rw_lightleft + rw_lightstep * (x1 - WallC.sx1); + + FLightNode *light_list = (mLineSegment && mLineSegment->sidedef) ? mLineSegment->sidedef->lighthead : nullptr; + + if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr)) + light_list = nullptr; // [SP] Don't draw dynlights if invul/lightamp active + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(drawerargs, mFrontSector, mLineSegment, WallC, rw_pic, x1, x2, walllower.ScreenY, wallbottom.ScreenY, mBottomPart.TextureMid, walltexcoords.VStep, walltexcoords.UPos, yscale, MAX(mBackFloorZ1, mBackFloorZ2), MIN(mFrontFloorZ1, mFrontFloorZ2), false, wallshade, offset, rw_light, rw_lightstep, light_list, foggy, basecolormap); + } + + //////////////////////////////////////////////////////////////////////////// + + // Transform and clip coordinates. Returns true if it was clipped away + bool FWallCoords::Init(RenderThread *thread, const DVector2 &pt1, const DVector2 &pt2, double too_close) + { + auto viewport = thread->Viewport.get(); + RenderPortal *renderportal = thread->Portal.get(); + + tleft.X = float(pt1.X * viewport->viewpoint.Sin - pt1.Y * viewport->viewpoint.Cos); + tright.X = float(pt2.X * viewport->viewpoint.Sin - pt2.Y * viewport->viewpoint.Cos); + + tleft.Y = float(pt1.X * viewport->viewpoint.TanCos + pt1.Y * viewport->viewpoint.TanSin); + tright.Y = float(pt2.X * viewport->viewpoint.TanCos + pt2.Y * viewport->viewpoint.TanSin); + + if (renderportal->MirrorFlags & RF_XFLIP) + { + float t = -tleft.X; + tleft.X = -tright.X; + tright.X = t; + swapvalues(tleft.Y, tright.Y); + } + + if (tleft.X >= -tleft.Y) + { + if (tleft.X > tleft.Y) return true; // left edge is off the right side + if (tleft.Y == 0) return true; + sx1 = xs_RoundToInt(viewport->CenterX + tleft.X * viewport->CenterX / tleft.Y); + sz1 = tleft.Y; + } + else + { + if (tright.X < -tright.Y) return true; // wall is off the left side + float den = tleft.X - tright.X - tright.Y + tleft.Y; + if (den == 0) return true; + sx1 = 0; + sz1 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X + tleft.Y) / den; + } + + if (sz1 < too_close) + return true; + + if (tright.X <= tright.Y) + { + if (tright.X < -tright.Y) return true; // right edge is off the left side + if (tright.Y == 0) return true; + sx2 = xs_RoundToInt(viewport->CenterX + tright.X * viewport->CenterX / tright.Y); + sz2 = tright.Y; + } + else + { + if (tleft.X > tleft.Y) return true; // wall is off the right side + float den = tright.Y - tleft.Y - tright.X + tleft.X; + if (den == 0) return true; + sx2 = viewwidth; + sz2 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X - tleft.Y) / den; + } + + if (sz2 < too_close || sx2 <= sx1) + return true; + + return false; + } + + ///////////////////////////////////////////////////////////////////////// + + void FWallTmapVals::InitFromWallCoords(RenderThread *thread, const FWallCoords *wallc) + { + const FVector2 *left = &wallc->tleft; + const FVector2 *right = &wallc->tright; + + RenderPortal *renderportal = thread->Portal.get(); + + if (renderportal->MirrorFlags & RF_XFLIP) + { + swapvalues(left, right); + } + UoverZorg = left->X * thread->Viewport->viewwindow.centerx; + UoverZstep = -left->Y; + InvZorg = (left->X - right->X) * thread->Viewport->viewwindow.centerx; + InvZstep = right->Y - left->Y; + } + + void FWallTmapVals::InitFromLine(RenderThread *thread, const DVector2 &left, const DVector2 &right) + { + // Coordinates should have already had viewx,viewy subtracted + + auto viewport = thread->Viewport.get(); + + double fullx1 = left.X * viewport->viewpoint.Sin - left.Y * viewport->viewpoint.Cos; + double fullx2 = right.X * viewport->viewpoint.Sin - right.Y * viewport->viewpoint.Cos; + double fully1 = left.X * viewport->viewpoint.TanCos + left.Y * viewport->viewpoint.TanSin; + double fully2 = right.X * viewport->viewpoint.TanCos + right.Y * viewport->viewpoint.TanSin; + + RenderPortal *renderportal = thread->Portal.get(); + + if (renderportal->MirrorFlags & RF_XFLIP) + { + fullx1 = -fullx1; + fullx2 = -fullx2; + } + + UoverZorg = float(fullx1 * viewport->viewwindow.centerx); + UoverZstep = float(-fully1); + InvZorg = float((fullx1 - fullx2) * viewport->viewwindow.centerx); + InvZstep = float(fully2 - fully1); + } +} diff --git a/src/swrenderer/line/r_line.h b/src/swrenderer/line/r_line.h new file mode 100644 index 0000000000..b0e3c416d9 --- /dev/null +++ b/src/swrenderer/line/r_line.h @@ -0,0 +1,149 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "vectors.h" +#include "r_wallsetup.h" +#include "swrenderer/segments/r_clipsegment.h" + +struct seg_t; +struct subsector_t; +struct sector_t; +struct side_t; +struct line_t; +struct FDynamicColormap; + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlane; + + struct FWallCoords + { + FVector2 tleft; // coords at left of wall in view space rx1,ry1 + FVector2 tright; // coords at right of wall in view space rx2,ry2 + + float sz1, sz2; // depth at left, right of wall in screen space yb1,yb2 + short sx1, sx2; // x coords at left, right of wall in screen space xb1,xb2 + + bool Init(RenderThread *thread, const DVector2 &pt1, const DVector2 &pt2, double too_close); + }; + + struct FWallTmapVals + { + float UoverZorg, UoverZstep; + float InvZorg, InvZstep; + + void InitFromWallCoords(RenderThread *thread, const FWallCoords *wallc); + void InitFromLine(RenderThread *thread, const DVector2 &left, const DVector2 &right); + }; + + struct WallPartTexture + { + fixed_t TextureOffsetU; + double TextureMid; + double TextureScaleU; + double TextureScaleV; + FTexture *Texture; + }; + + class SWRenderLine : VisibleSegmentRenderer + { + public: + SWRenderLine(RenderThread *thread); + void Render(seg_t *line, subsector_t *subsector, sector_t *sector, sector_t *fakebacksector, VisiblePlane *floorplane, VisiblePlane *ceilingplane, bool foggy, FDynamicColormap *basecolormap); + + RenderThread *Thread = nullptr; + + private: + bool RenderWallSegment(int x1, int x2) override; + void SetWallVariables(bool needlights); + void SetTopTexture(); + void SetMiddleTexture(); + void SetBottomTexture(); + void ClipSegmentTopBottom(int x1, int x2); + void MarkCeilingPlane(int x1, int x2); + void MarkFloorPlane(int x1, int x2); + void Mark3DFloors(int x1, int x2); + void MarkOpaquePassClip(int x1, int x2); + void RenderTopTexture(int x1, int x2); + void RenderMiddleTexture(int x1, int x2); + void RenderBottomTexture(int x1, int x2); + + bool IsFogBoundary(sector_t *front, sector_t *back) const; + bool SkyboxCompare(sector_t *frontsector, sector_t *backsector) const; + + bool IsInvisibleLine() const; + bool IsDoorClosed() const; + bool IsSolid() const; + + bool ShouldMarkFloor() const; + bool ShouldMarkCeiling() const; + bool ShouldMarkPortal() const; + + // Line variables: + + subsector_t *mSubsector; + sector_t *mFrontSector; + sector_t *mBackSector; + VisiblePlane *mFloorPlane; + VisiblePlane *mCeilingPlane; + seg_t *mLineSegment; + + double mBackCeilingZ1; + double mBackCeilingZ2; + double mBackFloorZ1; + double mBackFloorZ2; + double mFrontCeilingZ1; + double mFrontCeilingZ2; + double mFrontFloorZ1; + double mFrontFloorZ2; + + bool mDoorClosed; + + FWallCoords WallC; + FWallTmapVals WallT; + + bool foggy; + FDynamicColormap *basecolormap; + + // Wall segment variables: + + bool rw_prepped; + + int wallshade; + float rw_lightstep; + float rw_lightleft; + + double lwallscale; + + bool markfloor; // False if the back side is the same plane. + bool markceiling; + + WallPartTexture mTopPart; + WallPartTexture mMiddlePart; + WallPartTexture mBottomPart; + + ProjectedWallCull mCeilingClipped; + ProjectedWallCull mFloorClipped; + + ProjectedWallLine walltop; + ProjectedWallLine wallbottom; + ProjectedWallLine wallupper; + ProjectedWallLine walllower; + ProjectedWallTexcoords walltexcoords; + + sector_t tempsec; // killough 3/8/98: ceiling/water hack + }; +} diff --git a/src/swrenderer/line/r_renderdrawsegment.cpp b/src/swrenderer/line/r_renderdrawsegment.cpp new file mode 100644 index 0000000000..b84568b2de --- /dev/null +++ b/src/swrenderer/line/r_renderdrawsegment.cpp @@ -0,0 +1,959 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "g_levellocals.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "d_net.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/line/r_fogboundary.h" +#include "swrenderer/line/r_renderdrawsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/viewport/r_spritedrawer.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + RenderDrawSegment::RenderDrawSegment(RenderThread *thread) + { + Thread = thread; + } + + void RenderDrawSegment::Render(DrawSegment *ds, int x1, int x2) + { + auto viewport = Thread->Viewport.get(); + RenderFogBoundary renderfog; + float *MaskedSWall = nullptr, MaskedScaleY = 0, rw_scalestep = 0; + fixed_t *maskedtexturecol = nullptr; + + FTexture *tex; + int i; + sector_t tempsec; // killough 4/13/98 + double texheight, texheightscale; + bool notrelevant = false; + double rowoffset; + bool wrap = false; + + const sector_t *sec; + + bool sprflipvert = false; + + curline = ds->curline; + + float alpha = (float)MIN(curline->linedef->alpha, 1.); + bool additive = (curline->linedef->flags & ML_ADDTRANS) != 0; + + WallDrawerArgs walldrawerargs; + walldrawerargs.SetStyle(true, additive, FLOAT2FIXED(alpha)); + + SpriteDrawerArgs columndrawerargs; + FDynamicColormap *patchstylecolormap = nullptr; + bool visible = columndrawerargs.SetStyle(viewport, LegacyRenderStyles[additive ? STYLE_Add : STYLE_Translucent], alpha, 0, 0, patchstylecolormap); + + if (!visible && !ds->bFogBoundary && !ds->bFakeBoundary) + { + return; + } + + if (Thread->MainThread) + NetUpdate(); + + frontsector = curline->frontsector; + backsector = curline->backsector; + + tex = TexMan(curline->sidedef->GetTexture(side_t::mid), true); + if (i_compatflags & COMPATF_MASKEDMIDTEX) + { + tex = tex->GetRawTexture(); + } + + // killough 4/13/98: get correct lightlevel for 2s normal textures + sec = Thread->OpaquePass->FakeFlat(frontsector, &tempsec, nullptr, nullptr, nullptr, 0, 0, 0, 0); + + FDynamicColormap *basecolormap = sec->ColorMap; // [RH] Set basecolormap + + int wallshade = ds->shade; + rw_lightstep = ds->lightstep; + rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() < 0) + { + if (!(clip3d->fake3D & FAKE3D_CLIPTOP)) + { + clip3d->sclipTop = sec->ceilingplane.ZatPoint(Thread->Viewport->viewpoint.Pos); + } + for (i = frontsector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) + { + if (clip3d->sclipTop <= frontsector->e->XFloor.lightlist[i].plane.Zat0()) + { + lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, viewport), foggy); + break; + } + } + } + + short *mfloorclip = ds->sprbottomclip - ds->x1; + short *mceilingclip = ds->sprtopclip - ds->x1; + double spryscale; + + // [RH] Draw fog partition + if (ds->bFogBoundary) + { + renderfog.Render(Thread, x1, x2, mceilingclip, mfloorclip, wallshade, rw_light, rw_lightstep, basecolormap); + if (ds->maskedtexturecol == nullptr) + { + goto clearfog; + } + } + if ((ds->bFakeBoundary && !(ds->bFakeBoundary & 4)) || !visible) + { + goto clearfog; + } + + MaskedSWall = ds->swall - ds->x1; + MaskedScaleY = ds->yscale; + maskedtexturecol = ds->maskedtexturecol - ds->x1; + spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); + rw_scalestep = ds->iscalestep; + + if (cameraLight->FixedLightLevel() >= 0) + { + walldrawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + columndrawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + } + else if (cameraLight->FixedColormap() != nullptr) + { + walldrawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + columndrawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + } + + // find positioning + texheight = tex->GetScaledHeightDouble(); + texheightscale = fabs(curline->sidedef->GetTextureYScale(side_t::mid)); + if (texheightscale != 1) + { + texheight = texheight / texheightscale; + } + + double texturemid; + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + texturemid = MAX(frontsector->GetPlaneTexZ(sector_t::floor), backsector->GetPlaneTexZ(sector_t::floor)) + texheight; + } + else + { + texturemid = MIN(frontsector->GetPlaneTexZ(sector_t::ceiling), backsector->GetPlaneTexZ(sector_t::ceiling)); + } + + rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid); + + wrap = (curline->linedef->flags & ML_WRAP_MIDTEX) || (curline->sidedef->Flags & WALLF_WRAP_MIDTEX); + if (!wrap) + { // Texture does not wrap vertically. + double textop; + + if (MaskedScaleY < 0) + { + MaskedScaleY = -MaskedScaleY; + sprflipvert = true; + } + if (tex->bWorldPanning) + { + // rowoffset is added before the multiply so that the masked texture will + // still be positioned in world units rather than texels. + texturemid += rowoffset - Thread->Viewport->viewpoint.Pos.Z; + textop = texturemid; + texturemid *= MaskedScaleY; + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + textop = texturemid + rowoffset / MaskedScaleY - Thread->Viewport->viewpoint.Pos.Z; + texturemid = (texturemid - Thread->Viewport->viewpoint.Pos.Z) * MaskedScaleY + rowoffset; + } + if (sprflipvert) + { + MaskedScaleY = -MaskedScaleY; + texturemid -= tex->GetHeight() << FRACBITS; + } + + // [RH] Don't bother drawing segs that are completely offscreen + if (viewport->globaldclip * ds->sz1 < -textop && viewport->globaldclip * ds->sz2 < -textop) + { // Texture top is below the bottom of the screen + goto clearfog; + } + + if (viewport->globaluclip * ds->sz1 > texheight - textop && viewport->globaluclip * ds->sz2 > texheight - textop) + { // Texture bottom is above the top of the screen + goto clearfog; + } + + if ((clip3d->fake3D & FAKE3D_CLIPBOTTOM) && textop < clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z) + { + notrelevant = true; + goto clearfog; + } + if ((clip3d->fake3D & FAKE3D_CLIPTOP) && textop - texheight > clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z) + { + notrelevant = true; + goto clearfog; + } + + WallC.sz1 = ds->sz1; + WallC.sz2 = ds->sz2; + WallC.sx1 = ds->sx1; + WallC.sx2 = ds->sx2; + + if (clip3d->fake3D & FAKE3D_CLIPTOP) + { + wallupper.Project(Thread->Viewport.get(), textop < clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z ? textop : clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z, &WallC); + } + else + { + wallupper.Project(Thread->Viewport.get(), textop, &WallC); + } + if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) + { + walllower.Project(Thread->Viewport.get(), textop - texheight > clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z ? textop - texheight : clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z, &WallC); + } + else + { + walllower.Project(Thread->Viewport.get(), textop - texheight, &WallC); + } + + for (i = x1; i < x2; i++) + { + if (wallupper.ScreenY[i] < mceilingclip[i]) + wallupper.ScreenY[i] = mceilingclip[i]; + } + for (i = x1; i < x2; i++) + { + if (walllower.ScreenY[i] > mfloorclip[i]) + walllower.ScreenY[i] = mfloorclip[i]; + } + + if (clip3d->CurrentSkybox) + { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor + // or above the ceiling, so the appropriate end won't be clipped automatically when adding + // this drawseg. + if ((curline->linedef->flags & ML_CLIP_MIDTEX) || + (curline->sidedef->Flags & WALLF_CLIP_MIDTEX) || + (ib_compatflags & BCOMPATF_CLIPMIDTEX)) + { + ClipMidtex(x1, x2); + } + } + + mfloorclip = walllower.ScreenY; + mceilingclip = wallupper.ScreenY; + + // draw the columns one at a time + if (visible) + { + for (int x = x1; x < x2; ++x) + { + if (cameraLight->FixedColormap() == nullptr && cameraLight->FixedLightLevel() < 0) + { + columndrawerargs.SetLight(basecolormap, rw_light, wallshade); + } + + fixed_t iscale = xs_Fix<16>::ToFix(MaskedSWall[x] * MaskedScaleY); + double sprtopscreen; + if (sprflipvert) + sprtopscreen = viewport->CenterY + texturemid * spryscale; + else + sprtopscreen = viewport->CenterY - texturemid * spryscale; + + columndrawerargs.DrawMaskedColumn(Thread, x, iscale, tex, maskedtexturecol[x], spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip); + + rw_light += rw_lightstep; + spryscale += rw_scalestep; + } + } + } + else + { // Texture does wrap vertically. + if (tex->bWorldPanning) + { + // rowoffset is added before the multiply so that the masked texture will + // still be positioned in world units rather than texels. + texturemid = (texturemid - Thread->Viewport->viewpoint.Pos.Z + rowoffset) * MaskedScaleY; + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + texturemid = (texturemid - Thread->Viewport->viewpoint.Pos.Z) * MaskedScaleY + rowoffset; + } + + WallC.sz1 = ds->sz1; + WallC.sz2 = ds->sz2; + WallC.sx1 = ds->sx1; + WallC.sx2 = ds->sx2; + + if (clip3d->CurrentSkybox) + { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor + // or above the ceiling, so the appropriate end won't be clipped automatically when adding + // this drawseg. + if ((curline->linedef->flags & ML_CLIP_MIDTEX) || + (curline->sidedef->Flags & WALLF_CLIP_MIDTEX) || + (ib_compatflags & BCOMPATF_CLIPMIDTEX)) + { + ClipMidtex(x1, x2); + } + } + + if (clip3d->fake3D & FAKE3D_CLIPTOP) + { + wallupper.Project(Thread->Viewport.get(), clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z, &WallC); + for (i = x1; i < x2; i++) + { + if (wallupper.ScreenY[i] < mceilingclip[i]) + wallupper.ScreenY[i] = mceilingclip[i]; + } + mceilingclip = wallupper.ScreenY; + } + if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) + { + walllower.Project(Thread->Viewport.get(), clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z, &WallC); + for (i = x1; i < x2; i++) + { + if (walllower.ScreenY[i] > mfloorclip[i]) + walllower.ScreenY[i] = mfloorclip[i]; + } + mfloorclip = walllower.ScreenY; + } + + rw_offset = 0; + rw_pic = tex; + + double top, bot; + GetMaskedWallTopBottom(ds, top, bot); + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(walldrawerargs, frontsector, curline, WallC, rw_pic, x1, x2, mceilingclip, mfloorclip, texturemid, MaskedSWall, maskedtexturecol, ds->yscale, top, bot, true, wallshade, rw_offset, rw_light, rw_lightstep, nullptr, ds->foggy, basecolormap); + } + + clearfog: + if (ds->bFakeBoundary & 3) + { + RenderFakeWallRange(ds, x1, x2, wallshade); + } + if (!notrelevant) + { + if (clip3d->fake3D & FAKE3D_REFRESHCLIP) + { + if (!wrap) + { + assert(ds->bkup != nullptr); + memcpy(ds->sprtopclip, ds->bkup, (ds->x2 - ds->x1) * 2); + } + } + else + { + fillshort(ds->sprtopclip - ds->x1 + x1, x2 - x1, viewheight); + } + } + return; + } + + // kg3D - render one fake wall + void RenderDrawSegment::RenderFakeWall(DrawSegment *ds, int x1, int x2, F3DFloor *rover, int wallshade, FDynamicColormap *basecolormap) + { + int i; + double xscale; + double yscale; + + fixed_t Alpha = Scale(rover->alpha, OPAQUE, 255); + if (Alpha <= 0) + return; + + WallDrawerArgs drawerargs; + drawerargs.SetStyle(true, (rover->flags & FF_ADDITIVETRANS) != 0, Alpha); + + rw_lightstep = ds->lightstep; + rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; + + short *mfloorclip = ds->sprbottomclip - ds->x1; + short *mceilingclip = ds->sprtopclip - ds->x1; + + //double spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); + float *MaskedSWall = ds->swall - ds->x1; + + // find positioning + side_t *scaledside; + side_t::ETexpart scaledpart; + if (rover->flags & FF_UPPERTEXTURE) + { + scaledside = curline->sidedef; + scaledpart = side_t::top; + } + else if (rover->flags & FF_LOWERTEXTURE) + { + scaledside = curline->sidedef; + scaledpart = side_t::bottom; + } + else + { + scaledside = rover->master->sidedef[0]; + scaledpart = side_t::mid; + } + xscale = rw_pic->Scale.X * scaledside->GetTextureXScale(scaledpart); + yscale = rw_pic->Scale.Y * scaledside->GetTextureYScale(scaledpart); + + double rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureYOffset(side_t::mid); + double planez = rover->model->GetPlaneTexZ(sector_t::ceiling); + rw_offset = FLOAT2FIXED(curline->sidedef->GetTextureXOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureXOffset(side_t::mid)); + if (rowoffset < 0) + { + rowoffset += rw_pic->GetHeight(); + } + double texturemid = (planez - Thread->Viewport->viewpoint.Pos.Z) * yscale; + if (rw_pic->bWorldPanning) + { + // rowoffset is added before the multiply so that the masked texture will + // still be positioned in world units rather than texels. + + texturemid = texturemid + rowoffset * yscale; + rw_offset = xs_RoundToInt(rw_offset * xscale); + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + texturemid += rowoffset; + } + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != nullptr) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + + WallC.sz1 = ds->sz1; + WallC.sz2 = ds->sz2; + WallC.sx1 = ds->sx1; + WallC.sx2 = ds->sx2; + WallC.tleft.X = ds->cx; + WallC.tleft.Y = ds->cy; + WallC.tright.X = ds->cx + ds->cdx; + WallC.tright.Y = ds->cy + ds->cdy; + WallT = ds->tmapvals; + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + wallupper.Project(Thread->Viewport.get(), clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z, &WallC); + walllower.Project(Thread->Viewport.get(), clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z, &WallC); + + for (i = x1; i < x2; i++) + { + if (wallupper.ScreenY[i] < mceilingclip[i]) + wallupper.ScreenY[i] = mceilingclip[i]; + } + for (i = x1; i < x2; i++) + { + if (walllower.ScreenY[i] > mfloorclip[i]) + walllower.ScreenY[i] = mfloorclip[i]; + } + + ProjectedWallTexcoords walltexcoords; + walltexcoords.ProjectPos(Thread->Viewport.get(), curline->sidedef->TexelLength*xscale, ds->sx1, ds->sx2, WallT); + + double top, bot; + GetMaskedWallTopBottom(ds, top, bot); + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(drawerargs, frontsector, curline, WallC, rw_pic, x1, x2, wallupper.ScreenY, walllower.ScreenY, texturemid, MaskedSWall, walltexcoords.UPos, yscale, top, bot, true, wallshade, rw_offset, rw_light, rw_lightstep, nullptr, ds->foggy, basecolormap); + } + + // kg3D - walls of fake floors + void RenderDrawSegment::RenderFakeWallRange(DrawSegment *ds, int x1, int x2, int wallshade) + { + FTexture *const DONT_DRAW = ((FTexture*)(intptr_t)-1); + int i, j; + F3DFloor *rover, *fover = nullptr; + int passed, last; + double floorHeight; + double ceilingHeight; + + curline = ds->curline; + + frontsector = curline->frontsector; + backsector = curline->backsector; + + if (backsector == nullptr) + { + return; + } + if ((ds->bFakeBoundary & 3) == 2) + { + sector_t *sec = backsector; + backsector = frontsector; + frontsector = sec; + } + + floorHeight = backsector->CenterFloor(); + ceilingHeight = backsector->CenterCeiling(); + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + // maybe fix clipheights + if (!(clip3d->fake3D & FAKE3D_CLIPBOTTOM)) clip3d->sclipBottom = floorHeight; + if (!(clip3d->fake3D & FAKE3D_CLIPTOP)) clip3d->sclipTop = ceilingHeight; + + // maybe not visible + if (clip3d->sclipBottom >= frontsector->CenterCeiling()) return; + if (clip3d->sclipTop <= frontsector->CenterFloor()) return; + + if (clip3d->fake3D & FAKE3D_DOWN2UP) + { // bottom to viewz + last = 0; + for (i = backsector->e->XFloor.ffloors.Size() - 1; i >= 0; i--) + { + rover = backsector->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + + // visible? + passed = 0; + if (!(rover->flags & FF_RENDERSIDES) || rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || + rover->top.plane->Zat0() <= clip3d->sclipBottom || + rover->bottom.plane->Zat0() >= ceilingHeight || + rover->top.plane->Zat0() <= floorHeight) + { + if (!i) + { + passed = 1; + } + else + { + continue; + } + } + + rw_pic = nullptr; + if (rover->bottom.plane->Zat0() >= clip3d->sclipTop || passed) + { + if (last) + { + break; + } + // maybe wall from inside rendering? + fover = nullptr; + for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) + { // never + fover = nullptr; + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, it's bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->top.plane->Zat0() <= clip3d->sclipBottom) continue; // no + if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) + { // no, last possible + fover = nullptr; + break; + } + // it is, render inside? + if (!(fover->flags & (FF_BOTHPLANES | FF_INVERTPLANES))) + { // no + fover = nullptr; + } + break; + } + // nothing + if (!fover || j == -1) + { + break; + } + // correct texture + if (fover->flags & rover->flags & FF_SWIMMABLE) + { // don't ever draw (but treat as something has been found) + rw_pic = DONT_DRAW; + } + else if (fover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (fover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + else if (frontsector->e->XFloor.ffloors.Size()) + { + // maybe not visible? + fover = nullptr; + for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) // never + { + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, it's bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->top.plane->Zat0() <= clip3d->sclipBottom) continue; // no + if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) + { // visible, last possible + fover = nullptr; + break; + } + if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && + !(!(fover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) + ) + { + break; + } + if (fover->flags & rover->flags & FF_SWIMMABLE) + { // don't ever draw (but treat as something has been found) + rw_pic = DONT_DRAW; + } + fover = nullptr; // visible + break; + } + if (fover && j != -1) + { + fover = nullptr; + last = 1; + continue; // not visible + } + } + if (!rw_pic) + { + fover = nullptr; + if (rover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (rover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + // correct colors now + FDynamicColormap *basecolormap = frontsector->ColorMap; + wallshade = ds->shade; + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() < 0) + { + if ((ds->bFakeBoundary & 3) == 2) + { + for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (clip3d->sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, Thread->Viewport.get()), foggy); + break; + } + } + } + else + { + for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (clip3d->sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, Thread->Viewport.get()), foggy); + break; + } + } + } + } + if (rw_pic != DONT_DRAW) + { + RenderFakeWall(ds, x1, x2, fover ? fover : rover, wallshade, basecolormap); + } + else rw_pic = nullptr; + break; + } + } + else + { // top to viewz + for (i = 0; i < (int)backsector->e->XFloor.ffloors.Size(); i++) + { + rover = backsector->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + + // visible? + passed = 0; + if (!(rover->flags & FF_RENDERSIDES) || + rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || + rover->bottom.plane->Zat0() >= clip3d->sclipTop || + rover->top.plane->Zat0() <= floorHeight || + rover->bottom.plane->Zat0() >= ceilingHeight) + { + if ((unsigned)i == backsector->e->XFloor.ffloors.Size() - 1) + { + passed = 1; + } + else + { + continue; + } + } + rw_pic = nullptr; + if (rover->top.plane->Zat0() <= clip3d->sclipBottom || passed) + { // maybe wall from inside rendering? + fover = nullptr; + for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) + { // never + fover = nullptr; + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, it's bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) continue; // no + if (fover->top.plane->Zat0() <= clip3d->sclipBottom) + { // no, last possible + fover = nullptr; + break; + } + // it is, render inside? + if (!(fover->flags & (FF_BOTHPLANES | FF_INVERTPLANES))) + { // no + fover = nullptr; + } + break; + } + // nothing + if (!fover || (unsigned)j == frontsector->e->XFloor.ffloors.Size()) + { + break; + } + // correct texture + if (fover->flags & rover->flags & FF_SWIMMABLE) + { + rw_pic = DONT_DRAW; // don't ever draw (but treat as something has been found) + } + else if (fover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (fover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + else if (frontsector->e->XFloor.ffloors.Size()) + { // maybe not visible? + fover = nullptr; + for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) + { // never + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, its bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) continue; // no + if (fover->top.plane->Zat0() <= clip3d->sclipBottom) + { // visible, last possible + fover = nullptr; + break; + } + if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && + !(!(rover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) + ) + { + break; + } + if (fover->flags & rover->flags & FF_SWIMMABLE) + { // don't ever draw (but treat as something has been found) + rw_pic = DONT_DRAW; + } + fover = nullptr; // visible + break; + } + if (fover && (unsigned)j != frontsector->e->XFloor.ffloors.Size()) + { // not visible + break; + } + } + if (rw_pic == nullptr) + { + fover = nullptr; + if (rover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (rover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + // correct colors now + FDynamicColormap *basecolormap = frontsector->ColorMap; + wallshade = ds->shade; + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() < 0) + { + if ((ds->bFakeBoundary & 3) == 2) + { + for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (clip3d->sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, Thread->Viewport.get()), foggy); + break; + } + } + } + else + { + for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (clip3d->sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, Thread->Viewport.get()), foggy); + break; + } + } + } + } + + if (rw_pic != DONT_DRAW) + { + RenderFakeWall(ds, x1, x2, fover ? fover : rover, wallshade, basecolormap); + } + else + { + rw_pic = nullptr; + } + break; + } + } + return; + } + + // Clip a midtexture to the floor and ceiling of the sector in front of it. + void RenderDrawSegment::ClipMidtex(int x1, int x2) + { + ProjectedWallLine most; + + RenderPortal *renderportal = Thread->Portal.get(); + + most.Project(Thread->Viewport.get(), curline->frontsector->ceilingplane, &WallC, curline, renderportal->MirrorFlags & RF_XFLIP); + for (int i = x1; i < x2; ++i) + { + if (wallupper.ScreenY[i] < most.ScreenY[i]) + wallupper.ScreenY[i] = most.ScreenY[i]; + } + most.Project(Thread->Viewport.get(), curline->frontsector->floorplane, &WallC, curline, renderportal->MirrorFlags & RF_XFLIP); + for (int i = x1; i < x2; ++i) + { + if (walllower.ScreenY[i] > most.ScreenY[i]) + walllower.ScreenY[i] = most.ScreenY[i]; + } + } + + void RenderDrawSegment::GetMaskedWallTopBottom(DrawSegment *ds, double &top, double &bot) + { + double frontcz1 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v1); + double frontfz1 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v1); + double frontcz2 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v2); + double frontfz2 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v2); + top = MAX(frontcz1, frontcz2); + bot = MIN(frontfz1, frontfz2); + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + if (clip3d->fake3D & FAKE3D_CLIPTOP) + { + top = MIN(top, clip3d->sclipTop); + } + if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) + { + bot = MAX(bot, clip3d->sclipBottom); + } + } +} diff --git a/src/swrenderer/line/r_renderdrawsegment.h b/src/swrenderer/line/r_renderdrawsegment.h new file mode 100644 index 0000000000..a13d1341df --- /dev/null +++ b/src/swrenderer/line/r_renderdrawsegment.h @@ -0,0 +1,52 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/segments/r_drawsegment.h" + +namespace swrenderer +{ + class RenderThread; + + class RenderDrawSegment + { + public: + RenderDrawSegment(RenderThread *thread); + void Render(DrawSegment *ds, int x1, int x2); + + RenderThread *Thread = nullptr; + + private: + void ClipMidtex(int x1, int x2); + void RenderFakeWall(DrawSegment *ds, int x1, int x2, F3DFloor *rover, int wallshade, FDynamicColormap *basecolormap); + void RenderFakeWallRange(DrawSegment *ds, int x1, int x2, int wallshade); + void GetMaskedWallTopBottom(DrawSegment *ds, double &top, double &bot); + + sector_t *frontsector = nullptr; + sector_t *backsector = nullptr; + + seg_t *curline = nullptr; + + FWallCoords WallC; + FWallTmapVals WallT; + + float rw_light = 0.0f; + float rw_lightstep = 0.0f; + fixed_t rw_offset = 0; + FTexture *rw_pic = nullptr; + + ProjectedWallLine wallupper; + ProjectedWallLine walllower; + }; +} diff --git a/src/swrenderer/line/r_walldraw.cpp b/src/swrenderer/line/r_walldraw.cpp new file mode 100644 index 0000000000..d5eacce397 --- /dev/null +++ b/src/swrenderer/line/r_walldraw.cpp @@ -0,0 +1,532 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" + +#include "r_sky.h" +#include "v_video.h" + +#include "m_swap.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "r_walldraw.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/r_memory.h" + +namespace swrenderer +{ + WallSampler::WallSampler(RenderViewport *viewport, int y1, double texturemid, float swal, double yrepeat, fixed_t xoffset, double xmagnitude, FTexture *texture) + { + xoffset += FLOAT2FIXED(xmagnitude * 0.5); + + if (!viewport->RenderTarget->IsBgra()) + { + height = texture->GetHeight(); + + int uv_fracbits = 32 - texture->HeightBits; + if (uv_fracbits != 32) + { + uv_max = height << uv_fracbits; + + // Find start uv in [0-base_height[ range. + // Not using xs_ToFixed because it rounds the result and we need something that always rounds down to stay within the range. + double uv_stepd = swal * yrepeat; + double v = (texturemid + uv_stepd * (y1 - viewport->CenterY + 0.5)) / height; + v = v - floor(v); + v *= height; + v *= (1 << uv_fracbits); + + uv_pos = (uint32_t)v; + uv_step = xs_ToFixed(uv_fracbits, uv_stepd); + if (uv_step == 0) // To prevent divide by zero elsewhere + uv_step = 1; + } + else + { // Hack for one pixel tall textures + uv_pos = 0; + uv_step = 0; + uv_max = 1; + } + + int col = xoffset >> FRACBITS; + + // If the texture's width isn't a power of 2, then we need to make it a + // positive offset for proper clamping. + int width; + if (col < 0 && (width = texture->GetWidth()) != (1 << texture->WidthBits)) + { + col = width + (col % width); + } + + if (viewport->RenderTarget->IsBgra()) + source = (const uint8_t *)texture->GetColumnBgra(col, nullptr); + else + source = texture->GetColumn(col, nullptr); + + source2 = nullptr; + texturefracx = 0; + } + else + { + // Normalize to 0-1 range: + double uv_stepd = swal * yrepeat; + double v = (texturemid + uv_stepd * (y1 - viewport->CenterY + 0.5)) / texture->GetHeight(); + v = v - floor(v); + double v_step = uv_stepd / texture->GetHeight(); + + if (std::isnan(v) || std::isnan(v_step)) // this should never happen, but it apparently does.. + { + uv_stepd = 0.0; + v = 0.0; + v_step = 0.0; + } + + // Convert to uint32_t: + uv_pos = (uint32_t)(v * 0x100000000LL); + uv_step = (uint32_t)(v_step * 0x100000000LL); + uv_max = 0; + + // Texture mipmap and filter selection: + double ymagnitude = fabs(uv_stepd); + double magnitude = MAX(ymagnitude, xmagnitude); + double min_lod = -1000.0; + double lod = MAX(log2(magnitude) + r_lod_bias, min_lod); + bool magnifying = lod < 0.0f; + + int mipmap_offset = 0; + int mip_width = texture->GetWidth(); + int mip_height = texture->GetHeight(); + if (r_mipmap && texture->Mipmapped() && mip_width > 1 && mip_height > 1) + { + uint32_t xpos = (uint32_t)((((uint64_t)xoffset) << FRACBITS) / mip_width); + + int level = (int)lod; + while (level > 0 && mip_width > 1 && mip_height > 1) + { + mipmap_offset += mip_width * mip_height; + level--; + mip_width = MAX(mip_width >> 1, 1); + mip_height = MAX(mip_height >> 1, 1); + } + xoffset = (xpos >> FRACBITS) * mip_width; + } + + const uint32_t *pixels = texture->GetPixelsBgra() + mipmap_offset; + + bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter); + if (filter_nearest) + { + int tx = (xoffset >> FRACBITS) % mip_width; + if (tx < 0) + tx += mip_width; + source = (uint8_t*)(pixels + tx * mip_height); + source2 = nullptr; + height = mip_height; + texturefracx = 0; + } + else + { + xoffset -= FRACUNIT / 2; + int tx0 = (xoffset >> FRACBITS) % mip_width; + if (tx0 < 0) + tx0 += mip_width; + int tx1 = (tx0 + 1) % mip_width; + source = (uint8_t*)(pixels + tx0 * mip_height); + source2 = (uint8_t*)(pixels + tx1 * mip_height); + height = mip_height; + texturefracx = (xoffset >> (FRACBITS - 4)) & 15; + } + } + } + + // Draw a column with support for non-power-of-two ranges + void RenderWallPart::Draw1Column(int x, int y1, int y2, WallSampler &sampler) + { + if (r_dynlights && light_list) + { + auto viewport = Thread->Viewport.get(); + + // Find column position in view space + float w1 = 1.0f / WallC.sz1; + float w2 = 1.0f / WallC.sz2; + float t = (x - WallC.sx1 + 0.5f) / (WallC.sx2 - WallC.sx1); + float wcol = w1 * (1.0f - t) + w2 * t; + float zcol = 1.0f / wcol; + drawerargs.dc_viewpos.X = (float)((x + 0.5 - viewport->CenterX) / viewport->CenterX * zcol); + drawerargs.dc_viewpos.Y = zcol; + drawerargs.dc_viewpos.Z = (float)((viewport->CenterY - y1 - 0.5) / viewport->InvZtoScale * zcol); + drawerargs.dc_viewpos_step.Z = (float)(-zcol / viewport->InvZtoScale); + + // Calculate max lights that can touch column so we can allocate memory for the list + int max_lights = 0; + FLightNode *cur_node = light_list; + while (cur_node) + { + if (!(cur_node->lightsource->flags2&MF2_DORMANT)) + max_lights++; + cur_node = cur_node->nextLight; + } + + drawerargs.dc_num_lights = 0; + drawerargs.dc_lights = Thread->FrameMemory->AllocMemory(max_lights); + + // Setup lights for column + cur_node = light_list; + while (cur_node) + { + if (!(cur_node->lightsource->flags2&MF2_DORMANT)) + { + double lightX = cur_node->lightsource->X() - Thread->Viewport->viewpoint.Pos.X; + double lightY = cur_node->lightsource->Y() - Thread->Viewport->viewpoint.Pos.Y; + double lightZ = cur_node->lightsource->Z() - Thread->Viewport->viewpoint.Pos.Z; + + float lx = (float)(lightX * Thread->Viewport->viewpoint.Sin - lightY * Thread->Viewport->viewpoint.Cos) - drawerargs.dc_viewpos.X; + float ly = (float)(lightX * Thread->Viewport->viewpoint.TanCos + lightY * Thread->Viewport->viewpoint.TanSin) - drawerargs.dc_viewpos.Y; + float lz = (float)lightZ; + + // Precalculate the constant part of the dot here so the drawer doesn't have to. + bool is_point_light = (cur_node->lightsource->flags4 & MF4_ATTENUATE) != 0; + float lconstant = lx * lx + ly * ly; + float nlconstant = is_point_light ? lx * drawerargs.dc_normal.X + ly * drawerargs.dc_normal.Y : 0.0f; + + // Include light only if it touches this column + float radius = cur_node->lightsource->GetRadius(); + if (radius * radius >= lconstant && nlconstant >= 0.0f) + { + uint32_t red = cur_node->lightsource->GetRed(); + uint32_t green = cur_node->lightsource->GetGreen(); + uint32_t blue = cur_node->lightsource->GetBlue(); + + auto &light = drawerargs.dc_lights[drawerargs.dc_num_lights++]; + light.x = lconstant; + light.y = nlconstant; + light.z = lz; + light.radius = 256.0f / cur_node->lightsource->GetRadius(); + light.color = (red << 16) | (green << 8) | blue; + } + } + + cur_node = cur_node->nextLight; + } + } + else + { + drawerargs.dc_num_lights = 0; + } + + if (Thread->Viewport->RenderTarget->IsBgra()) + { + int count = y2 - y1; + + drawerargs.SetTexture(sampler.source, sampler.source2, sampler.height); + drawerargs.SetTextureUPos(sampler.texturefracx); + drawerargs.SetDest(Thread->Viewport.get(), x, y1); + drawerargs.SetCount(count); + drawerargs.SetTextureVStep(sampler.uv_step); + drawerargs.SetTextureVPos(sampler.uv_pos); + drawerargs.DrawColumn(Thread); + + uint64_t step64 = sampler.uv_step; + uint64_t pos64 = sampler.uv_pos; + sampler.uv_pos = (uint32_t)(pos64 + step64 * count); + } + else + { + if (sampler.uv_max == 0 || sampler.uv_step == 0) // power of two + { + int count = y2 - y1; + + drawerargs.SetTexture(sampler.source, sampler.source2, sampler.height); + drawerargs.SetTextureUPos(sampler.texturefracx); + drawerargs.SetDest(Thread->Viewport.get(), x, y1); + drawerargs.SetCount(count); + drawerargs.SetTextureVStep(sampler.uv_step); + drawerargs.SetTextureVPos(sampler.uv_pos); + drawerargs.DrawColumn(Thread); + + uint64_t step64 = sampler.uv_step; + uint64_t pos64 = sampler.uv_pos; + sampler.uv_pos = (uint32_t)(pos64 + step64 * count); + } + else + { + uint32_t uv_pos = sampler.uv_pos; + + uint32_t left = y2 - y1; + while (left > 0) + { + uint32_t available = sampler.uv_max - uv_pos; + uint32_t next_uv_wrap = available / sampler.uv_step; + if (available % sampler.uv_step != 0) + next_uv_wrap++; + uint32_t count = MIN(left, next_uv_wrap); + + drawerargs.SetTexture(sampler.source, sampler.source2, sampler.height); + drawerargs.SetTextureUPos(sampler.texturefracx); + drawerargs.SetDest(Thread->Viewport.get(), x, y1); + drawerargs.SetCount(count); + drawerargs.SetTextureVStep(sampler.uv_step); + drawerargs.SetTextureVPos(uv_pos); + drawerargs.DrawColumn(Thread); + + left -= count; + uv_pos += sampler.uv_step * count; + if (uv_pos >= sampler.uv_max) + uv_pos -= sampler.uv_max; + } + + sampler.uv_pos = uv_pos; + } + } + } + + void RenderWallPart::ProcessWallWorker(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal) + { + if (rw_pic->UseType == FTexture::TEX_Null) + return; + + rw_pic->GetHeight(); // To ensure that rw_pic->HeightBits has been set + int fracbits = 32 - rw_pic->HeightBits; + if (fracbits == 32) + { // Hack for one pixel tall textures + fracbits = 0; + yrepeat = 0; + texturemid = 0; + } + + drawerargs.SetTextureFracBits(Thread->Viewport->RenderTarget->IsBgra() ? FRACBITS : fracbits); + + CameraLight *cameraLight = CameraLight::Instance(); + bool fixed = (cameraLight->FixedColormap() != NULL || cameraLight->FixedLightLevel() >= 0); + + if (cameraLight->FixedColormap()) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + else + drawerargs.SetLight(basecolormap, 0, 0); + + float dx = WallC.tright.X - WallC.tleft.X; + float dy = WallC.tright.Y - WallC.tleft.Y; + float length = sqrt(dx * dx + dy * dy); + drawerargs.dc_normal.X = dy / length; + drawerargs.dc_normal.Y = -dx / length; + drawerargs.dc_normal.Z = 0.0f; + + double xmagnitude = 1.0; + + float curlight = light; + for (int x = x1; x < x2; x++, curlight += lightstep) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + if (!fixed) + drawerargs.SetLight(basecolormap, curlight, wallshade); + + if (x + 1 < x2) xmagnitude = fabs(FIXED2DBL(lwal[x + 1]) - FIXED2DBL(lwal[x])); + + WallSampler sampler(Thread->Viewport.get(), y1, texturemid, swal[x], yrepeat, lwal[x] + xoffset, xmagnitude, rw_pic); + Draw1Column(x, y1, y2, sampler); + } + + if (Thread->MainThread) + NetUpdate(); + } + + void RenderWallPart::ProcessNormalWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal) + { + ProcessWallWorker(uwal, dwal, texturemid, swal, lwal); + } + + void RenderWallPart::ProcessStripedWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal) + { + ProjectedWallLine most1, most2, most3; + const short *up; + short *down; + + up = uwal; + down = most1.ScreenY; + + assert(WallC.sx1 <= x1); + assert(WallC.sx2 >= x2); + + RenderPortal *renderportal = Thread->Portal.get(); + + // kg3D - fake floors instead of zdoom light list + for (unsigned int i = 0; i < frontsector->e->XFloor.lightlist.Size(); i++) + { + ProjectedWallCull j = most3.Project(Thread->Viewport.get(), frontsector->e->XFloor.lightlist[i].plane, &WallC, curline, renderportal->MirrorFlags & RF_XFLIP); + if (j != ProjectedWallCull::OutsideAbove) + { + for (int j = x1; j < x2; ++j) + { + down[j] = clamp(most3.ScreenY[j], up[j], dwal[j]); + } + ProcessNormalWall(up, down, texturemid, swal, lwal); + up = down; + down = (down == most1.ScreenY) ? most2.ScreenY : most1.ScreenY; + } + + lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; + basecolormap = lit->extra_colormap; + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), foggy); + } + + ProcessNormalWall(up, dwal, texturemid, swal, lwal); + } + + void RenderWallPart::ProcessWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal) + { + // Textures that aren't masked can use the faster ProcessNormalWall. + if (!rw_pic->bMasked && drawerargs.IsMaskedDrawer()) + { + drawerargs.SetStyle(true, false, OPAQUE); + } + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap() != NULL || cameraLight->FixedLightLevel() >= 0 || !(frontsector->e && frontsector->e->XFloor.lightlist.Size())) + { + ProcessNormalWall(uwal, dwal, texturemid, swal, lwal); + } + else + { + ProcessStripedWall(uwal, dwal, texturemid, swal, lwal); + } + } + + //============================================================================= + // + // ProcessWallNP2 + // + // This is a wrapper around ProcessWall that helps it tile textures whose heights + // are not powers of 2. It divides the wall into texture-sized strips and calls + // ProcessNormalWall for each of those. Since only one repetition of the texture fits + // in each strip, ProcessWall will not tile. + // + //============================================================================= + + void RenderWallPart::ProcessWallNP2(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal, double top, double bot) + { + ProjectedWallLine most1, most2, most3; + double texheight = rw_pic->GetHeight(); + double partition; + double scaledtexheight = texheight / yrepeat; + + if (yrepeat >= 0) + { // normal orientation: draw strips from top to bottom + partition = top - fmod(top - texturemid / yrepeat - Thread->Viewport->viewpoint.Pos.Z, scaledtexheight); + if (partition == top) + { + partition -= scaledtexheight; + } + const short *up = uwal; + short *down = most1.ScreenY; + texturemid = (partition - Thread->Viewport->viewpoint.Pos.Z) * yrepeat + texheight; + while (partition > bot) + { + ProjectedWallCull j = most3.Project(Thread->Viewport.get(), partition - Thread->Viewport->viewpoint.Pos.Z, &WallC); + if (j != ProjectedWallCull::OutsideAbove) + { + for (int j = x1; j < x2; ++j) + { + down[j] = clamp(most3.ScreenY[j], up[j], dwal[j]); + } + ProcessWall(up, down, texturemid, swal, lwal); + up = down; + down = (down == most1.ScreenY) ? most2.ScreenY : most1.ScreenY; + } + partition -= scaledtexheight; + texturemid -= texheight; + } + ProcessWall(up, dwal, texturemid, swal, lwal); + } + else + { // upside down: draw strips from bottom to top + partition = bot - fmod(bot - texturemid / yrepeat - Thread->Viewport->viewpoint.Pos.Z, scaledtexheight); + short *up = most1.ScreenY; + const short *down = dwal; + texturemid = (partition - Thread->Viewport->viewpoint.Pos.Z) * yrepeat + texheight; + while (partition < top) + { + ProjectedWallCull j = most3.Project(Thread->Viewport.get(), partition - Thread->Viewport->viewpoint.Pos.Z, &WallC); + if (j != ProjectedWallCull::OutsideBelow) + { + for (int j = x1; j < x2; ++j) + { + up[j] = clamp(most3.ScreenY[j], uwal[j], down[j]); + } + ProcessWall(up, down, texturemid, swal, lwal); + down = up; + up = (up == most1.ScreenY) ? most2.ScreenY : most1.ScreenY; + } + partition -= scaledtexheight; + texturemid -= texheight; + } + ProcessWall(uwal, down, texturemid, swal, lwal); + } + } + + void RenderWallPart::Render(const WallDrawerArgs &drawerargs, sector_t *frontsector, seg_t *curline, const FWallCoords &WallC, FTexture *pic, int x1, int x2, const short *walltop, const short *wallbottom, double texturemid, float *swall, fixed_t *lwall, double yscale, double top, double bottom, bool mask, int wallshade, fixed_t xoffset, float light, float lightstep, FLightNode *light_list, bool foggy, FDynamicColormap *basecolormap) + { + this->drawerargs = drawerargs; + this->x1 = x1; + this->x2 = x2; + this->frontsector = frontsector; + this->curline = curline; + this->WallC = WallC; + this->yrepeat = yscale; + this->wallshade = wallshade; + this->xoffset = xoffset; + this->light = light; + this->lightstep = lightstep; + this->foggy = foggy; + this->basecolormap = basecolormap; + this->light_list = light_list; + this->rw_pic = pic; + this->mask = mask; + + if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) + { + ProcessWallNP2(walltop, wallbottom, texturemid, swall, lwall, top, bottom); + } + else + { + ProcessWall(walltop, wallbottom, texturemid, swall, lwall); + } + } + + RenderWallPart::RenderWallPart(RenderThread *thread) + { + Thread = thread; + } +} diff --git a/src/swrenderer/line/r_walldraw.h b/src/swrenderer/line/r_walldraw.h new file mode 100644 index 0000000000..d662092d23 --- /dev/null +++ b/src/swrenderer/line/r_walldraw.h @@ -0,0 +1,108 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/viewport/r_walldrawer.h" +#include "r_line.h" + +class FTexture; +struct FLightNode; +struct seg_t; +struct FLightNode; +struct FDynamicColormap; + +namespace swrenderer +{ + class RenderThread; + struct DrawSegment; + struct FWallCoords; + class ProjectedWallLine; + class ProjectedWallTexcoords; + struct WallSampler; + + class RenderWallPart + { + public: + RenderWallPart(RenderThread *thread); + + void Render( + const WallDrawerArgs &drawerargs, + sector_t *frontsector, + seg_t *curline, + const FWallCoords &WallC, + FTexture *rw_pic, + int x1, + int x2, + const short *walltop, + const short *wallbottom, + double texturemid, + float *swall, + fixed_t *lwall, + double yscale, + double top, + double bottom, + bool mask, + int wallshade, + fixed_t xoffset, + float light, + float lightstep, + FLightNode *light_list, + bool foggy, + FDynamicColormap *basecolormap); + + RenderThread *Thread = nullptr; + + private: + void ProcessWallNP2(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal, double top, double bot); + void ProcessWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal); + void ProcessStripedWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal); + void ProcessNormalWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal); + void ProcessWallWorker(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal); + void Draw1Column(int x, int y1, int y2, WallSampler &sampler); + + int x1 = 0; + int x2 = 0; + FTexture *rw_pic = nullptr; + sector_t *frontsector = nullptr; + seg_t *curline = nullptr; + FWallCoords WallC; + + double yrepeat = 0.0; + int wallshade = 0; + fixed_t xoffset = 0; + float light = 0.0f; + float lightstep = 0.0f; + bool foggy = false; + FDynamicColormap *basecolormap = nullptr; + FLightNode *light_list = nullptr; + bool mask = false; + + WallDrawerArgs drawerargs; + }; + + struct WallSampler + { + WallSampler() { } + WallSampler(RenderViewport *viewport, int y1, double texturemid, float swal, double yrepeat, fixed_t xoffset, double xmagnitude, FTexture *texture); + + uint32_t uv_pos; + uint32_t uv_step; + uint32_t uv_max; + + const uint8_t *source; + const uint8_t *source2; + uint32_t texturefracx; + uint32_t height; + }; +} diff --git a/src/swrenderer/line/r_wallsetup.cpp b/src/swrenderer/line/r_wallsetup.cpp new file mode 100644 index 0000000000..dbae303d5f --- /dev/null +++ b/src/swrenderer/line/r_wallsetup.cpp @@ -0,0 +1,223 @@ + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" +#include "r_sky.h" +#include "v_video.h" +#include "m_swap.h" +#include "w_wad.h" +#include "stats.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "r_wallsetup.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_walldraw.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/line/r_line.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" + +namespace swrenderer +{ + ProjectedWallCull ProjectedWallLine::Project(RenderViewport *viewport, double z, const FWallCoords *wallc) + { + return Project(viewport, z, z, wallc); + } + + ProjectedWallCull ProjectedWallLine::Project(RenderViewport *viewport, double z1, double z2, const FWallCoords *wallc) + { + float y1 = (float)(viewport->CenterY - z1 * viewport->InvZtoScale / wallc->sz1); + float y2 = (float)(viewport->CenterY - z2 * viewport->InvZtoScale / wallc->sz2); + + if (y1 < 0 && y2 < 0) // entire line is above screen + { + memset(&ScreenY[wallc->sx1], 0, (wallc->sx2 - wallc->sx1) * sizeof(ScreenY[0])); + return ProjectedWallCull::OutsideAbove; + } + else if (y1 > viewheight && y2 > viewheight) // entire line is below screen + { + fillshort(&ScreenY[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); + return ProjectedWallCull::OutsideBelow; + } + + if (wallc->sx2 <= wallc->sx1) + return ProjectedWallCull::Visible; + + float rcp_delta = 1.0f / (wallc->sx2 - wallc->sx1); + if (y1 >= 0.0f && y2 >= 0.0f && xs_RoundToInt(y1) <= viewheight && xs_RoundToInt(y2) <= viewheight) + { + for (int x = wallc->sx1; x < wallc->sx2; x++) + { + float t = (x - wallc->sx1) * rcp_delta; + float y = y1 * (1.0f - t) + y2 * t; + ScreenY[x] = (short)xs_RoundToInt(y); + } + } + else + { + for (int x = wallc->sx1; x < wallc->sx2; x++) + { + float t = (x - wallc->sx1) * rcp_delta; + float y = y1 * (1.0f - t) + y2 * t; + ScreenY[x] = (short)clamp(xs_RoundToInt(y), 0, viewheight); + } + } + + return ProjectedWallCull::Visible; + } + + ProjectedWallCull ProjectedWallLine::Project(RenderViewport *viewport, const secplane_t &plane, const FWallCoords *wallc, seg_t *curline, bool xflip) + { + if (!plane.isSlope()) + { + return Project(viewport, plane.Zat0() - viewport->viewpoint.Pos.Z, wallc); + } + else + { + // Get Z coordinates at both ends of the line + double x, y, den, z1, z2; + if (xflip) + { + x = curline->v2->fX(); + y = curline->v2->fY(); + if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) + { + double frac = (wallc->tleft.Y + wallc->tleft.X) / den; + x -= frac * (x - curline->v1->fX()); + y -= frac * (y - curline->v1->fY()); + } + z1 = plane.ZatPoint(x, y) - viewport->viewpoint.Pos.Z; + + if (wallc->sx2 > wallc->sx1 + 1) + { + x = curline->v1->fX(); + y = curline->v1->fY(); + if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) + { + double frac = (wallc->tright.Y - wallc->tright.X) / den; + x += frac * (curline->v2->fX() - x); + y += frac * (curline->v2->fY() - y); + } + z2 = plane.ZatPoint(x, y) - viewport->viewpoint.Pos.Z; + } + else + { + z2 = z1; + } + } + else + { + x = curline->v1->fX(); + y = curline->v1->fY(); + if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) + { + double frac = (wallc->tleft.Y + wallc->tleft.X) / den; + x += frac * (curline->v2->fX() - x); + y += frac * (curline->v2->fY() - y); + } + z1 = plane.ZatPoint(x, y) - viewport->viewpoint.Pos.Z; + + if (wallc->sx2 > wallc->sx1 + 1) + { + x = curline->v2->fX(); + y = curline->v2->fY(); + if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) + { + double frac = (wallc->tright.Y - wallc->tright.X) / den; + x -= frac * (x - curline->v1->fX()); + y -= frac * (y - curline->v1->fY()); + } + z2 = plane.ZatPoint(x, y) - viewport->viewpoint.Pos.Z; + } + else + { + z2 = z1; + } + } + + return Project(viewport, z1, z2, wallc); + } + } + + ///////////////////////////////////////////////////////////////////////// + + void ProjectedWallTexcoords::Project(RenderViewport *viewport, double walxrepeat, int x1, int x2, const FWallTmapVals &WallT) + { + float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - viewport->CenterX); + float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - viewport->CenterX); + float uGradient = WallT.UoverZstep; + float zGradient = WallT.InvZstep; + float xrepeat = (float)fabs(walxrepeat); + float depthScale = (float)(WallT.InvZstep * viewport->WallTMapScale2); + float depthOrg = (float)(-WallT.UoverZstep * viewport->WallTMapScale2); + + if (walxrepeat < 0.0) + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ; + + UPos[x] = (fixed_t)((xrepeat - u * xrepeat) * FRACUNIT); + VStep[x] = depthOrg + u * depthScale; + + uOverZ += uGradient; + invZ += zGradient; + } + } + else + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ; + + UPos[x] = (fixed_t)(u * xrepeat * FRACUNIT); + VStep[x] = depthOrg + u * depthScale; + + uOverZ += uGradient; + invZ += zGradient; + } + } + } + + void ProjectedWallTexcoords::ProjectPos(RenderViewport *viewport, double walxrepeat, int x1, int x2, const FWallTmapVals &WallT) + { + float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - viewport->CenterX); + float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - viewport->CenterX); + float uGradient = WallT.UoverZstep; + float zGradient = WallT.InvZstep; + float xrepeat = (float)fabs(walxrepeat); + + if (walxrepeat < 0.0f) + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ * xrepeat - xrepeat; + + UPos[x] = (fixed_t)(u * FRACUNIT); + + uOverZ += uGradient; + invZ += zGradient; + } + } + else + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ * xrepeat; + + UPos[x] = (fixed_t)(u * FRACUNIT); + + uOverZ += uGradient; + invZ += zGradient; + } + } + } +} diff --git a/src/swrenderer/line/r_wallsetup.h b/src/swrenderer/line/r_wallsetup.h new file mode 100644 index 0000000000..d7d32dd5b8 --- /dev/null +++ b/src/swrenderer/line/r_wallsetup.h @@ -0,0 +1,37 @@ + +#pragma once + +#include "r_defs.h" + +namespace swrenderer +{ + struct FWallCoords; + struct FWallTmapVals; + + enum class ProjectedWallCull + { + Visible, + OutsideAbove, + OutsideBelow + }; + + class ProjectedWallLine + { + public: + short ScreenY[MAXWIDTH]; + + ProjectedWallCull Project(RenderViewport *viewport, double z1, double z2, const FWallCoords *wallc); + ProjectedWallCull Project(RenderViewport *viewport, const secplane_t &plane, const FWallCoords *wallc, seg_t *line, bool xflip); + ProjectedWallCull Project(RenderViewport *viewport, double z, const FWallCoords *wallc); + }; + + class ProjectedWallTexcoords + { + public: + float VStep[MAXWIDTH]; // swall + fixed_t UPos[MAXWIDTH]; // lwall + + void Project(RenderViewport *viewport, double walxrepeat, int x1, int x2, const FWallTmapVals &WallT); + void ProjectPos(RenderViewport *viewport, double walxrepeat, int x1, int x2, const FWallTmapVals &WallT); + }; +} diff --git a/src/swrenderer/plane/r_flatplane.cpp b/src/swrenderer/plane/r_flatplane.cpp new file mode 100644 index 0000000000..25d6a5ff62 --- /dev/null +++ b/src/swrenderer/plane/r_flatplane.cpp @@ -0,0 +1,294 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "g_levellocals.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "r_flatplane.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + RenderFlatPlane::RenderFlatPlane(RenderThread *thread) + { + Thread = thread; + } + + void RenderFlatPlane::Render(VisiblePlane *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked, FDynamicColormap *colormap, FTexture *texture) + { + if (alpha <= 0) + { + return; + } + + drawerargs.SetSolidColor(3); + drawerargs.SetTexture(Thread->Viewport.get(), texture); + + double planeang = (pl->xform.Angle + pl->xform.baseAngle).Radians(); + double xstep, ystep, leftxfrac, leftyfrac, rightxfrac, rightyfrac; + double x; + + if (planeang != 0) + { + double cosine = cos(planeang), sine = sin(planeang); + pviewx = pl->xform.xOffs + Thread->Viewport->viewpoint.Pos.X * cosine - Thread->Viewport->viewpoint.Pos.Y * sine; + pviewy = pl->xform.yOffs + pl->xform.baseyOffs - Thread->Viewport->viewpoint.Pos.X * sine - Thread->Viewport->viewpoint.Pos.Y * cosine; + } + else + { + pviewx = pl->xform.xOffs + Thread->Viewport->viewpoint.Pos.X; + pviewy = pl->xform.yOffs - Thread->Viewport->viewpoint.Pos.Y; + } + + pviewx = _xscale * pviewx; + pviewy = _yscale * pviewy; + + // left to right mapping + planeang += (Thread->Viewport->viewpoint.Angles.Yaw - 90).Radians(); + + auto viewport = Thread->Viewport.get(); + + // Scale will be unit scale at FocalLengthX (normally SCREENWIDTH/2) distance + xstep = cos(planeang) / viewport->FocalLengthX; + ystep = -sin(planeang) / viewport->FocalLengthX; + + // [RH] flip for mirrors + RenderPortal *renderportal = Thread->Portal.get(); + if (renderportal->MirrorFlags & RF_XFLIP) + { + xstep = -xstep; + ystep = -ystep; + } + + planeang += M_PI / 2; + double cosine = cos(planeang), sine = -sin(planeang); + x = pl->right - viewport->CenterX - 0.5; + rightxfrac = _xscale * (cosine + x * xstep); + rightyfrac = _yscale * (sine + x * ystep); + x = pl->left - viewport->CenterX + 0.5; + leftxfrac = _xscale * (cosine + x * xstep); + leftyfrac = _yscale * (sine + x * ystep); + + basexfrac = leftxfrac; + baseyfrac = leftyfrac; + xstepscale = (rightxfrac - leftxfrac) / (pl->right - pl->left + 1); + ystepscale = (rightyfrac - leftyfrac) / (pl->right - pl->left + 1); + + minx = pl->left; + + planeheight = fabs(pl->height.Zat0() - Thread->Viewport->viewpoint.Pos.Z); + + basecolormap = colormap; + + // [RH] set foggy flag + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); + + GlobVis = LightVisibility::Instance()->FlatPlaneGlobVis(foggy) / planeheight; + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + { + drawerargs.SetLight(basecolormap, 0, cameraLight->FixedLightLevelShade()); + plane_shade = false; + } + else if (cameraLight->FixedColormap()) + { + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + plane_shade = false; + } + else + { + plane_shade = true; + planeshade = LightVisibility::LightLevelToShade(pl->lightlevel, foggy); + } + + drawerargs.SetStyle(masked, additive, alpha); + + light_list = pl->lights; + + RenderLines(pl); + } + + void RenderFlatPlane::RenderLine(int y, int x1, int x2) + { +#ifdef RANGECHECK + if (x2 < x1 || x1<0 || x2 >= viewwidth || (unsigned)y >= (unsigned)viewheight) + { + I_FatalError("R_MapPlane: %i, %i at %i", x1, x2, y); + } +#endif + + auto viewport = Thread->Viewport.get(); + + double curxfrac = basexfrac + xstepscale * (x1 + 0.5 - minx); + double curyfrac = baseyfrac + ystepscale * (x1 + 0.5 - minx); + + double distance = viewport->PlaneDepth(y, planeheight); + + if (drawerargs.TextureWidthBits() != 0) + { + drawerargs.SetTextureUStep(xs_ToFixed(32 - drawerargs.TextureWidthBits(), distance * xstepscale)); + drawerargs.SetTextureUPos(xs_ToFixed(32 - drawerargs.TextureWidthBits(), distance * curxfrac + pviewx)); + } + else + { + drawerargs.SetTextureUStep(0); + drawerargs.SetTextureUPos(0); + } + + if (drawerargs.TextureHeightBits() != 0) + { + drawerargs.SetTextureVStep(xs_ToFixed(32 - drawerargs.TextureHeightBits(), distance * ystepscale)); + drawerargs.SetTextureVPos(xs_ToFixed(32 - drawerargs.TextureHeightBits(), distance * curyfrac + pviewy)); + } + else + { + drawerargs.SetTextureVStep(0); + drawerargs.SetTextureVPos(0); + } + + if (viewport->RenderTarget->IsBgra()) + { + double distance2 = viewport->PlaneDepth(y + 1, planeheight); + double xmagnitude = fabs(ystepscale * (distance2 - distance) * viewport->FocalLengthX); + double ymagnitude = fabs(xstepscale * (distance2 - distance) * viewport->FocalLengthX); + double magnitude = MAX(ymagnitude, xmagnitude); + double min_lod = -1000.0; + drawerargs.SetTextureLOD(MAX(log2(magnitude) + r_lod_bias, min_lod)); + } + + if (plane_shade) + { + // Determine lighting based on the span's distance from the viewer. + drawerargs.SetLight(basecolormap, (float)(GlobVis * fabs(viewport->CenterY - y)), planeshade); + } + + if (r_dynlights) + { + // Find row position in view space + float zspan = (float)(planeheight / (fabs(y + 0.5 - viewport->CenterY) / viewport->InvZtoScale)); + drawerargs.dc_viewpos.X = (float)((x1 + 0.5 - viewport->CenterX) / viewport->CenterX * zspan); + drawerargs.dc_viewpos.Y = zspan; + drawerargs.dc_viewpos.Z = (float)((viewport->CenterY - y - 0.5) / viewport->InvZtoScale * zspan); + drawerargs.dc_viewpos_step.X = (float)(zspan / viewport->CenterX); + + // Plane normal + drawerargs.dc_normal.X = 0.0f; + drawerargs.dc_normal.Y = 0.0f; + drawerargs.dc_normal.Z = (y >= viewport->CenterY) ? 1.0f : -1.0f; + + // Calculate max lights that can touch the row so we can allocate memory for the list + int max_lights = 0; + VisiblePlaneLight *cur_node = light_list; + while (cur_node) + { + if (!(cur_node->lightsource->flags2&MF2_DORMANT)) + max_lights++; + cur_node = cur_node->next; + } + + drawerargs.dc_num_lights = 0; + drawerargs.dc_lights = Thread->FrameMemory->AllocMemory(max_lights); + + // Setup lights for row + cur_node = light_list; + while (cur_node) + { + double lightX = cur_node->lightsource->X() - Thread->Viewport->viewpoint.Pos.X; + double lightY = cur_node->lightsource->Y() - Thread->Viewport->viewpoint.Pos.Y; + double lightZ = cur_node->lightsource->Z() - Thread->Viewport->viewpoint.Pos.Z; + + float lx = (float)(lightX * Thread->Viewport->viewpoint.Sin - lightY * Thread->Viewport->viewpoint.Cos); + float ly = (float)(lightX * Thread->Viewport->viewpoint.TanCos + lightY * Thread->Viewport->viewpoint.TanSin) - drawerargs.dc_viewpos.Y; + float lz = (float)lightZ - drawerargs.dc_viewpos.Z; + + // Precalculate the constant part of the dot here so the drawer doesn't have to. + bool is_point_light = (cur_node->lightsource->flags4 & MF4_ATTENUATE) != 0; + float lconstant = ly * ly + lz * lz; + float nlconstant = is_point_light ? lz * drawerargs.dc_normal.Z : 0.0f; + + // Include light only if it touches this row + float radius = cur_node->lightsource->GetRadius(); + if (radius * radius >= lconstant && nlconstant >= 0.0f) + { + uint32_t red = cur_node->lightsource->GetRed(); + uint32_t green = cur_node->lightsource->GetGreen(); + uint32_t blue = cur_node->lightsource->GetBlue(); + + auto &light = drawerargs.dc_lights[drawerargs.dc_num_lights++]; + light.x = lx; + light.y = lconstant; + light.z = nlconstant; + light.radius = 256.0f / radius; + light.color = (red << 16) | (green << 8) | blue; + } + + cur_node = cur_node->next; + } + } + else + { + drawerargs.dc_num_lights = 0; + } + + drawerargs.SetDestY(viewport, y); + drawerargs.SetDestX1(x1); + drawerargs.SetDestX2(x2); + + drawerargs.DrawSpan(Thread); + } + + ///////////////////////////////////////////////////////////////////////// + + RenderColoredPlane::RenderColoredPlane(RenderThread *thread) + { + Thread = thread; + } + + void RenderColoredPlane::Render(VisiblePlane *pl) + { + RenderLines(pl); + } + + void RenderColoredPlane::RenderLine(int y, int x1, int x2) + { + drawerargs.DrawColoredSpan(Thread, y, x1, x2); + } +} diff --git a/src/swrenderer/plane/r_flatplane.h b/src/swrenderer/plane/r_flatplane.h new file mode 100644 index 0000000000..1d62c09f78 --- /dev/null +++ b/src/swrenderer/plane/r_flatplane.h @@ -0,0 +1,62 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_planerenderer.h" +#include "swrenderer/viewport/r_spandrawer.h" + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlaneLight; + + class RenderFlatPlane : PlaneRenderer + { + public: + RenderFlatPlane(RenderThread *thread); + void Render(VisiblePlane *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked, FDynamicColormap *basecolormap, FTexture *texture); + + RenderThread *Thread = nullptr; + + private: + void RenderLine(int y, int x1, int x2) override; + + int minx; + double planeheight; + bool plane_shade; + int planeshade; + double GlobVis; + FDynamicColormap *basecolormap; + double pviewx, pviewy; + double xstepscale, ystepscale; + double basexfrac, baseyfrac; + VisiblePlaneLight *light_list; + + SpanDrawerArgs drawerargs; + }; + + class RenderColoredPlane : PlaneRenderer + { + public: + RenderColoredPlane(RenderThread *thread); + void Render(VisiblePlane *pl); + + RenderThread *Thread = nullptr; + + private: + void RenderLine(int y, int x1, int x2) override; + + SpanDrawerArgs drawerargs; + }; +} diff --git a/src/swrenderer/plane/r_planerenderer.cpp b/src/swrenderer/plane/r_planerenderer.cpp new file mode 100644 index 0000000000..6bc023738c --- /dev/null +++ b/src/swrenderer/plane/r_planerenderer.cpp @@ -0,0 +1,97 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_planerenderer.h" + +namespace swrenderer +{ + void PlaneRenderer::RenderLines(VisiblePlane *pl) + { + // t1/b1 are at x + // t2/b2 are at x+1 + // spanend[y] is at the right edge + + int x = pl->right - 1; + int t2 = pl->top[x]; + int b2 = pl->bottom[x]; + + if (b2 > t2) + { + fillshort(spanend + t2, b2 - t2, x); + } + + for (--x; x >= pl->left; --x) + { + int t1 = pl->top[x]; + int b1 = pl->bottom[x]; + const int xr = x + 1; + int stop; + + // Draw any spans that have just closed + stop = MIN(t1, b2); + while (t2 < stop) + { + int y = t2++; + int x2 = spanend[y]; + RenderLine(y, xr, x2); + } + stop = MAX(b1, t2); + while (b2 > stop) + { + int y = --b2; + int x2 = spanend[y]; + RenderLine(y, xr, x2); + } + + // Mark any spans that have just opened + stop = MIN(t2, b1); + while (t1 < stop) + { + spanend[t1++] = x; + } + stop = MAX(b2, t2); + while (b1 > stop) + { + spanend[--b1] = x; + } + + t2 = pl->top[x]; + b2 = pl->bottom[x]; + } + // Draw any spans that are still open + while (t2 < b2) + { + int y = --b2; + int x2 = spanend[y]; + RenderLine(y, pl->left, x2); + } + } +} diff --git a/src/swrenderer/plane/r_planerenderer.h b/src/swrenderer/plane/r_planerenderer.h new file mode 100644 index 0000000000..f7992cd66b --- /dev/null +++ b/src/swrenderer/plane/r_planerenderer.h @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include "r_defs.h" + +namespace swrenderer +{ + struct VisiblePlane; + + class PlaneRenderer + { + public: + void RenderLines(VisiblePlane *pl); + + virtual void RenderLine(int y, int x1, int x2) = 0; + + private: + short spanend[MAXHEIGHT]; + }; +} diff --git a/src/swrenderer/plane/r_skyplane.cpp b/src/swrenderer/plane/r_skyplane.cpp new file mode 100644 index 0000000000..534a46890e --- /dev/null +++ b/src/swrenderer/plane/r_skyplane.cpp @@ -0,0 +1,255 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "r_skyplane.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "g_levellocals.h" + +CVAR(Bool, r_linearsky, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +EXTERN_CVAR(Int, r_skymode) + +namespace swrenderer +{ + RenderSkyPlane::RenderSkyPlane(RenderThread *thread) + { + Thread = thread; + } + + void RenderSkyPlane::Render(VisiblePlane *pl) + { + FTextureID sky1tex, sky2tex; + double frontdpos = 0, backdpos = 0; + + if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) + { + sky1tex = sky2texture; + } + else + { + sky1tex = sky1texture; + } + sky2tex = sky2texture; + skymid = skytexturemid; + skyangle = Thread->Viewport->viewpoint.Angles.Yaw.BAMs(); + + if (pl->picnum == skyflatnum) + { + if (!(pl->sky & PL_SKYFLAT)) + { // use sky1 + sky1: + frontskytex = TexMan(sky1tex, true); + if (level.flags & LEVEL_DOUBLESKY) + backskytex = TexMan(sky2tex, true); + else + backskytex = NULL; + skyflip = 0; + frontdpos = sky1pos; + backdpos = sky2pos; + frontcyl = sky1cyl; + backcyl = sky2cyl; + } + else if (pl->sky == PL_SKYFLAT) + { // use sky2 + frontskytex = TexMan(sky2tex, true); + backskytex = NULL; + frontcyl = sky2cyl; + skyflip = 0; + frontdpos = sky2pos; + } + else + { // MBF's linedef-controlled skies + // Sky Linedef + const line_t *l = &level.lines[(pl->sky & ~PL_SKYFLAT) - 1]; + + // Sky transferred from first sidedef + const side_t *s = l->sidedef[0]; + int pos; + + // Texture comes from upper texture of reference sidedef + // [RH] If swapping skies, then use the lower sidedef + if (level.flags & LEVEL_SWAPSKIES && s->GetTexture(side_t::bottom).isValid()) + { + pos = side_t::bottom; + } + else + { + pos = side_t::top; + } + + frontskytex = TexMan(s->GetTexture(pos), true); + if (frontskytex == NULL || frontskytex->UseType == FTexture::TEX_Null) + { // [RH] The blank texture: Use normal sky instead. + goto sky1; + } + backskytex = NULL; + + // Horizontal offset is turned into an angle offset, + // to allow sky rotation as well as careful positioning. + // However, the offset is scaled very small, so that it + // allows a long-period of sky rotation. + skyangle += FLOAT2FIXED(s->GetTextureXOffset(pos)); + + // Vertical offset allows careful sky positioning. + skymid = s->GetTextureYOffset(pos); + + // We sometimes flip the picture horizontally. + // + // Doom always flipped the picture, so we make it optional, + // to make it easier to use the new feature, while to still + // allow old sky textures to be used. + skyflip = l->args[2] ? 0u : ~0u; + + int frontxscale = int(frontskytex->Scale.X * 1024); + frontcyl = MAX(frontskytex->GetWidth(), frontxscale); + if (skystretch) + { + skymid = skymid * frontskytex->GetScaledHeightDouble() / SKYSTRETCH_HEIGHT; + } + } + } + frontpos = int(fmod(frontdpos, sky1cyl * 65536.0)); + if (backskytex != NULL) + { + backpos = int(fmod(backdpos, sky2cyl * 65536.0)); + } + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap()) + { + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + } + else + { + drawerargs.SetLight(&NormalLight, 0, 0); + } + + DrawSky(pl); + } + + void RenderSkyPlane::DrawSkyColumnStripe(int start_x, int y1, int y2, double scale, double texturemid, double yrepeat) + { + RenderPortal *renderportal = Thread->Portal.get(); + auto viewport = Thread->Viewport.get(); + + uint32_t height = frontskytex->GetHeight(); + + double uv_stepd = skyiscale * yrepeat; + double v = (texturemid + uv_stepd * (y1 - viewport->CenterY + 0.5)) / height; + double v_step = uv_stepd / height; + + uint32_t uv_pos = (uint32_t)(v * 0x01000000); + uint32_t uv_step = (uint32_t)(v_step * 0x01000000); + + int x = start_x; + if (renderportal->MirrorFlags & RF_XFLIP) + x = (viewwidth - x); + + uint32_t ang, angle1, angle2; + + if (r_linearsky) + { + angle_t xangle = (angle_t)((0.5 - x / (double)viewwidth) * viewport->viewwindow.FocalTangent * ANGLE_90); + ang = (skyangle + xangle) ^ skyflip; + } + else + { + ang = (skyangle + viewport->xtoviewangle[x]) ^ skyflip; + } + angle1 = (uint32_t)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); + angle2 = (uint32_t)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); + + drawerargs.SetFrontTexture(viewport, frontskytex, angle1); + drawerargs.SetBackTexture(viewport, backskytex, angle2); + drawerargs.SetTextureVStep(uv_step); + drawerargs.SetTextureVPos(uv_pos); + drawerargs.SetDest(viewport, start_x, y1); + drawerargs.SetCount(y2 - y1); + drawerargs.SetFadeSky(r_skymode == 2 && !(level.flags & LEVEL_FORCETILEDSKY)); + drawerargs.SetSolidTop(frontskytex->GetSkyCapColor(false)); + drawerargs.SetSolidBottom(frontskytex->GetSkyCapColor(true)); + + if (!backskytex) + drawerargs.DrawSingleSkyColumn(Thread); + else + drawerargs.DrawDoubleSkyColumn(Thread); + } + + void RenderSkyPlane::DrawSkyColumn(int start_x, int y1, int y2) + { + if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) + { + double texturemid = skymid * frontskytex->Scale.Y + frontskytex->GetHeight(); + DrawSkyColumnStripe(start_x, y1, y2, frontskytex->Scale.Y, texturemid, frontskytex->Scale.Y); + } + else + { + auto viewport = Thread->Viewport.get(); + double yrepeat = frontskytex->Scale.Y; + double scale = frontskytex->Scale.Y * skyscale; + double iscale = 1 / scale; + short drawheight = short(frontskytex->GetHeight() * scale); + double topfrac = fmod(skymid + iscale * (1 - viewport->CenterY), frontskytex->GetHeight()); + if (topfrac < 0) topfrac += frontskytex->GetHeight(); + double texturemid = topfrac - iscale * (1 - viewport->CenterY); + DrawSkyColumnStripe(start_x, y1, y2, scale, texturemid, yrepeat); + } + } + + void RenderSkyPlane::DrawSky(VisiblePlane *pl) + { + int x1 = pl->left; + int x2 = pl->right; + short *uwal = (short *)pl->top; + short *dwal = (short *)pl->bottom; + + for (int x = x1; x < x2; x++) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + DrawSkyColumn(x, y1, y2); + } + } +} diff --git a/src/swrenderer/plane/r_skyplane.h b/src/swrenderer/plane/r_skyplane.h new file mode 100644 index 0000000000..9a79b1e96b --- /dev/null +++ b/src/swrenderer/plane/r_skyplane.h @@ -0,0 +1,47 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visibleplane.h" +#include "swrenderer/viewport/r_skydrawer.h" + +namespace swrenderer +{ + class RenderSkyPlane + { + public: + RenderSkyPlane(RenderThread *thread); + + void Render(VisiblePlane *pl); + + RenderThread *Thread = nullptr; + + private: + void DrawSky(VisiblePlane *pl); + void DrawSkyColumnStripe(int start_x, int y1, int y2, double scale, double texturemid, double yrepeat); + void DrawSkyColumn(int start_x, int y1, int y2); + + FTexture *frontskytex = nullptr; + FTexture *backskytex = nullptr; + angle_t skyflip = 0; + int frontpos = 0; + int backpos = 0; + fixed_t frontcyl = 0; + fixed_t backcyl = 0; + double skymid = 0.0; + angle_t skyangle = 0; + + SkyDrawerArgs drawerargs; + }; +} diff --git a/src/swrenderer/plane/r_slopeplane.cpp b/src/swrenderer/plane/r_slopeplane.cpp new file mode 100644 index 0000000000..16e73f0b86 --- /dev/null +++ b/src/swrenderer/plane/r_slopeplane.cpp @@ -0,0 +1,201 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "g_levellocals.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "r_slopeplane.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +namespace swrenderer +{ + RenderSlopePlane::RenderSlopePlane(RenderThread *thread) + { + Thread = thread; + } + + void RenderSlopePlane::Render(VisiblePlane *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked, FDynamicColormap *colormap, FTexture *texture) + { + static const float ifloatpow2[16] = + { + // ifloatpow2[i] = 1 / (1 << i) + 64.f, 32.f, 16.f, 8.f, 4.f, 2.f, 1.f, 0.5f, + 0.25f, 0.125f, 0.0625f, 0.03125f, 0.015625f, 0.0078125f, + 0.00390625f, 0.001953125f + /*, 0.0009765625f, 0.00048828125f, 0.000244140625f, + 1.220703125e-4f, 6.103515625e-5, 3.0517578125e-5*/ + }; + double lxscale, lyscale; + double xscale, yscale; + FVector3 p, m, n; + double ang, planeang, cosine, sine; + double zeroheight; + + if (alpha <= 0) + { + return; + } + + auto viewport = Thread->Viewport.get(); + + drawerargs.SetSolidColor(3); + drawerargs.SetTexture(Thread->Viewport.get(), texture); + + lxscale = _xscale * ifloatpow2[drawerargs.TextureWidthBits()]; + lyscale = _yscale * ifloatpow2[drawerargs.TextureHeightBits()]; + xscale = 64.f / lxscale; + yscale = 64.f / lyscale; + zeroheight = pl->height.ZatPoint(Thread->Viewport->viewpoint.Pos); + + pviewx = xs_ToFixed(32 - drawerargs.TextureWidthBits(), pl->xform.xOffs * pl->xform.xScale); + pviewy = xs_ToFixed(32 - drawerargs.TextureHeightBits(), pl->xform.yOffs * pl->xform.yScale); + planeang = (pl->xform.Angle + pl->xform.baseAngle).Radians(); + + // p is the texture origin in view space + // Don't add in the offsets at this stage, because doing so can result in + // errors if the flat is rotated. + ang = M_PI * 3 / 2 - Thread->Viewport->viewpoint.Angles.Yaw.Radians(); + cosine = cos(ang), sine = sin(ang); + p[0] = Thread->Viewport->viewpoint.Pos.X * cosine - Thread->Viewport->viewpoint.Pos.Y * sine; + p[2] = Thread->Viewport->viewpoint.Pos.X * sine + Thread->Viewport->viewpoint.Pos.Y * cosine; + p[1] = pl->height.ZatPoint(0.0, 0.0) - Thread->Viewport->viewpoint.Pos.Z; + + // m is the v direction vector in view space + ang = ang - M_PI / 2 - planeang; + cosine = cos(ang), sine = sin(ang); + m[0] = yscale * cosine; + m[2] = yscale * sine; + // m[1] = pl->height.ZatPointF (0, iyscale) - pl->height.ZatPointF (0,0)); + // VectorScale2 (m, 64.f/VectorLength(m)); + + // n is the u direction vector in view space +#if 0 + //let's use the sin/cosine we already know instead of computing new ones + ang += M_PI / 2 + n[0] = -xscale * cos(ang); + n[2] = -xscale * sin(ang); +#else + n[0] = xscale * sine; + n[2] = -xscale * cosine; +#endif + // n[1] = pl->height.ZatPointF (ixscale, 0) - pl->height.ZatPointF (0,0)); + // VectorScale2 (n, 64.f/VectorLength(n)); + + // This code keeps the texture coordinates constant across the x,y plane no matter + // how much you slope the surface. Use the commented-out code above instead to keep + // the textures a constant size across the surface's plane instead. + cosine = cos(planeang), sine = sin(planeang); + m[1] = pl->height.ZatPoint(Thread->Viewport->viewpoint.Pos.X + yscale * sine, Thread->Viewport->viewpoint.Pos.Y + yscale * cosine) - zeroheight; + n[1] = -(pl->height.ZatPoint(Thread->Viewport->viewpoint.Pos.X - xscale * cosine, Thread->Viewport->viewpoint.Pos.Y + xscale * sine) - zeroheight); + + plane_su = p ^ m; + plane_sv = p ^ n; + plane_sz = m ^ n; + + plane_su.Z *= viewport->FocalLengthX; + plane_sv.Z *= viewport->FocalLengthX; + plane_sz.Z *= viewport->FocalLengthX; + + plane_su.Y *= viewport->IYaspectMul; + plane_sv.Y *= viewport->IYaspectMul; + plane_sz.Y *= viewport->IYaspectMul; + + // Premultiply the texture vectors with the scale factors + plane_su *= 4294967296.f; + plane_sv *= 4294967296.f; + + RenderPortal *renderportal = Thread->Portal.get(); + if (renderportal->MirrorFlags & RF_XFLIP) + { + plane_su[0] = -plane_su[0]; + plane_sv[0] = -plane_sv[0]; + plane_sz[0] = -plane_sz[0]; + } + + // [RH] set foggy flag + basecolormap = colormap; + bool foggy = level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE);; + + planelightfloat = (LightVisibility::Instance()->SlopePlaneGlobVis(foggy) * lxscale * lyscale) / (fabs(pl->height.ZatPoint(Thread->Viewport->viewpoint.Pos) - Thread->Viewport->viewpoint.Pos.Z)) / 65536.f; + + if (pl->height.fC() > 0) + planelightfloat = -planelightfloat; + + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + { + drawerargs.SetLight(basecolormap, 0, cameraLight->FixedLightLevelShade()); + plane_shade = false; + } + else if (cameraLight->FixedColormap()) + { + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + plane_shade = false; + } + else + { + drawerargs.SetLight(basecolormap, 0, 0); + plane_shade = true; + planeshade = LightVisibility::LightLevelToShade(pl->lightlevel, foggy); + } + + // Hack in support for 1 x Z and Z x 1 texture sizes + if (drawerargs.TextureHeightBits() == 0) + { + plane_sv[2] = plane_sv[1] = plane_sv[0] = 0; + } + if (drawerargs.TextureWidthBits() == 0) + { + plane_su[2] = plane_su[1] = plane_su[0] = 0; + } + + RenderLines(pl); + } + + void RenderSlopePlane::RenderLine(int y, int x1, int x2) + { + drawerargs.DrawTiltedSpan(Thread, y, x1, x2, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy, basecolormap); + } +} diff --git a/src/swrenderer/plane/r_slopeplane.h b/src/swrenderer/plane/r_slopeplane.h new file mode 100644 index 0000000000..b707a3f7c4 --- /dev/null +++ b/src/swrenderer/plane/r_slopeplane.h @@ -0,0 +1,43 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_planerenderer.h" +#include "swrenderer/viewport/r_spandrawer.h" + +namespace swrenderer +{ + class RenderThread; + + class RenderSlopePlane : PlaneRenderer + { + public: + RenderSlopePlane(RenderThread *thread); + void Render(VisiblePlane *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked, FDynamicColormap *basecolormap, FTexture *texture); + + RenderThread *Thread = nullptr; + + private: + void RenderLine(int y, int x1, int x2) override; + + FVector3 plane_sz, plane_su, plane_sv; + float planelightfloat; + bool plane_shade; + int planeshade; + fixed_t pviewx, pviewy; + fixed_t xscale, yscale; + FDynamicColormap *basecolormap; + SpanDrawerArgs drawerargs; + }; +} diff --git a/src/swrenderer/plane/r_visibleplane.cpp b/src/swrenderer/plane/r_visibleplane.cpp new file mode 100644 index 0000000000..b32e092b48 --- /dev/null +++ b/src/swrenderer/plane/r_visibleplane.cpp @@ -0,0 +1,139 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/plane/r_flatplane.h" +#include "swrenderer/plane/r_slopeplane.h" +#include "swrenderer/plane/r_skyplane.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/drawers/r_draw.h" + +CVAR(Bool, tilt, false, 0); + +namespace swrenderer +{ + VisiblePlane::VisiblePlane(RenderThread *thread) + { + picnum.SetNull(); + height.set(0.0, 0.0, 1.0, 0.0); + + bottom = thread->FrameMemory->AllocMemory(viewwidth); + top = thread->FrameMemory->AllocMemory(viewwidth); + + fillshort(bottom, viewwidth, 0); + fillshort(top, viewwidth, 0x7fff); + } + + void VisiblePlane::AddLights(RenderThread *thread, FLightNode *node) + { + if (!r_dynlights) + return; + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap() != NULL || cameraLight->FixedLightLevel() >= 0) + return; // [SP] no dynlights if invul or lightamp + + while (node) + { + if (!(node->lightsource->flags2&MF2_DORMANT)) + { + bool found = false; + VisiblePlaneLight *light_node = lights; + while (light_node) + { + if (light_node->lightsource == node->lightsource) + { + found = true; + break; + } + light_node = light_node->next; + } + if (!found) + { + VisiblePlaneLight *newlight = thread->FrameMemory->NewObject(); + newlight->next = lights; + newlight->lightsource = node->lightsource; + lights = newlight; + } + } + node = node->nextLight; + } + } + + void VisiblePlane::Render(RenderThread *thread, fixed_t alpha, bool additive, bool masked) + { + if (left >= right) + return; + + if (picnum == skyflatnum) // sky flat + { + RenderSkyPlane renderer(thread); + renderer.Render(this); + } + else // regular flat + { + FTexture *tex = TexMan(picnum, true); + + if (tex->UseType == FTexture::TEX_Null) + { + return; + } + + if (!masked && !additive) + { // If we're not supposed to see through this plane, draw it opaque. + alpha = OPAQUE; + } + else if (!tex->bMasked) + { // Don't waste time on a masked texture if it isn't really masked. + masked = false; + } + double xscale = xform.xScale * tex->Scale.X; + double yscale = xform.yScale * tex->Scale.Y; + + if (!height.isSlope() && !tilt) + { + RenderFlatPlane renderer(thread); + renderer.Render(this, xscale, yscale, alpha, additive, masked, colormap, tex); + } + else + { + RenderSlopePlane renderer(thread); + renderer.Render(this, xscale, yscale, alpha, additive, masked, colormap, tex); + } + } + if (thread->MainThread) + NetUpdate(); + } +} diff --git a/src/swrenderer/plane/r_visibleplane.h b/src/swrenderer/plane/r_visibleplane.h new file mode 100644 index 0000000000..970eee6326 --- /dev/null +++ b/src/swrenderer/plane/r_visibleplane.h @@ -0,0 +1,76 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include "r_defs.h" +#include "r_state.h" +#include "swrenderer/r_memory.h" + +class ADynamicLight; +struct FLightNode; +struct FDynamicColormap; +struct FSectorPortal; + +namespace swrenderer +{ + class RenderThread; + + struct VisiblePlaneLight + { + ADynamicLight *lightsource; + VisiblePlaneLight *next; + }; + + struct VisiblePlane + { + VisiblePlane(RenderThread *thread); + + void AddLights(RenderThread *thread, FLightNode *node); + void Render(RenderThread *thread, fixed_t alpha, bool additive, bool masked); + + VisiblePlane *next = nullptr; // Next visplane in hash chain -- killough + + FDynamicColormap *colormap = nullptr; // [RH] Support multiple colormaps + FSectorPortal *portal = nullptr; // [RH] Support sky boxes + VisiblePlaneLight *lights = nullptr; + + FTransform xform; + secplane_t height; + FTextureID picnum; + int lightlevel = 0; + int left = viewwidth; + int right = 0; + int sky = 0; + + // [RH] This set of variables copies information from the time when the + // visplane is created. They are only used by stacks so that you can + // have stacked sectors inside a skybox. If the visplane is not for a + // stack, then they are unused. + int extralight = 0; + double visibility = 0.0; + DVector3 viewpos = { 0.0, 0.0, 0.0 }; + DAngle viewangle = { 0.0 }; + fixed_t Alpha = 0; + bool Additive = false; + + // kg3D - keep track of mirror and skybox owner + int CurrentSkybox = 0; + int CurrentPortalUniq = 0; // mirror counter, counts all of them + int MirrorFlags = 0; // this is not related to CurrentMirror + + uint16_t *bottom = nullptr; + uint16_t *top = nullptr; + }; +} diff --git a/src/swrenderer/plane/r_visibleplanelist.cpp b/src/swrenderer/plane/r_visibleplanelist.cpp new file mode 100644 index 0000000000..8cb29d938e --- /dev/null +++ b/src/swrenderer/plane/r_visibleplanelist.cpp @@ -0,0 +1,378 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/plane/r_flatplane.h" +#include "swrenderer/plane/r_slopeplane.h" +#include "swrenderer/plane/r_skyplane.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + VisiblePlaneList::VisiblePlaneList(RenderThread *thread) + { + Thread = thread; + } + + VisiblePlaneList::VisiblePlaneList() + { + for (auto &plane : visplanes) + plane = nullptr; + } + + VisiblePlane *VisiblePlaneList::Add(unsigned hash) + { + VisiblePlane *newplane = Thread->FrameMemory->NewObject(Thread); + newplane->next = visplanes[hash]; + visplanes[hash] = newplane; + return newplane; + } + + void VisiblePlaneList::Clear() + { + for (int i = 0; i <= MAXVISPLANES; i++) + visplanes[i] = nullptr; + } + + void VisiblePlaneList::ClearKeepFakePlanes() + { + for (int i = 0; i <= MAXVISPLANES - 1; i++) + { + for (VisiblePlane **probe = &visplanes[i]; *probe != nullptr; ) + { + if ((*probe)->sky < 0) + { // fake: move past it + probe = &(*probe)->next; + } + else + { // not fake: move from list + VisiblePlane *vis = *probe; + *probe = vis->next; + vis->next = nullptr; + } + } + } + } + + VisiblePlane *VisiblePlaneList::FindPlane(const secplane_t &height, FTextureID picnum, int lightlevel, double Alpha, bool additive, const FTransform &xxform, int sky, FSectorPortal *portal, FDynamicColormap *basecolormap) + { + secplane_t plane; + VisiblePlane *check; + unsigned hash; // killough + bool isskybox; + const FTransform *xform = &xxform; + fixed_t alpha = FLOAT2FIXED(Alpha); + //angle_t angle = (xform.Angle + xform.baseAngle).BAMs(); + + FTransform nulltransform; + + RenderPortal *renderportal = Thread->Portal.get(); + + if (picnum == skyflatnum) // killough 10/98 + { // most skies map together + lightlevel = 0; + xform = &nulltransform; + nulltransform.xOffs = nulltransform.yOffs = nulltransform.baseyOffs = 0; + nulltransform.xScale = nulltransform.yScale = 1; + nulltransform.Angle = nulltransform.baseAngle = 0.0; + additive = false; + // [RH] Map floor skies and ceiling skies to separate visplanes. This isn't + // always necessary, but it is needed if a floor and ceiling sky are in the + // same column but separated by a wall. If they both try to reside in the + // same visplane, then only the floor sky will be drawn. + plane.set(0., 0., height.fC(), 0.); + isskybox = portal != nullptr && !renderportal->InSkyBox(portal); + } + else if (portal != nullptr && !renderportal->InSkyBox(portal)) + { + plane = height; + isskybox = true; + } + else + { + plane = height; + isskybox = false; + // kg3D - hack, store alpha in sky + // i know there is ->alpha, but this also allows to identify fake plane + // and ->alpha is for stacked sectors + Clip3DFloors *clip3d = Thread->Clip3D.get(); + if (clip3d->fake3D & (FAKE3D_FAKEFLOOR | FAKE3D_FAKECEILING)) sky = 0x80000000 | clip3d->fakeAlpha; + else sky = 0; // not skyflatnum so it can't be a sky + portal = nullptr; + alpha = OPAQUE; + } + + // New visplane algorithm uses hash table -- killough + hash = isskybox ? ((unsigned)MAXVISPLANES) : CalcHash(picnum.GetIndex(), lightlevel, height); + + for (check = visplanes[hash]; check; check = check->next) // killough + { + if (isskybox) + { + if (portal == check->portal && plane == check->height) + { + if (portal->mType != PORTS_SKYVIEWPOINT) + { // This skybox is really a stacked sector, so we need to + // check even more. + if (check->extralight == renderportal->stacked_extralight && + check->visibility == renderportal->stacked_visibility && + check->viewpos == renderportal->stacked_viewpos && + ( + // headache inducing logic... :( + (portal->mType != PORTS_STACKEDSECTORTHING) || + ( + check->Alpha == alpha && + check->Additive == additive && + (alpha == 0 || // if alpha is > 0 everything needs to be checked + (plane == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + basecolormap == check->colormap && // [RH] Add more checks + *xform == check->xform + ) + ) && + check->viewangle == renderportal->stacked_angle.Yaw + ) + ) + ) + { + return check; + } + } + else + { + return check; + } + } + } + else + if (plane == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + basecolormap == check->colormap && // [RH] Add more checks + *xform == check->xform && + sky == check->sky && + renderportal->CurrentPortalUniq == check->CurrentPortalUniq && + renderportal->MirrorFlags == check->MirrorFlags && + Thread->Clip3D->CurrentSkybox == check->CurrentSkybox && + Thread->Viewport->viewpoint.Pos == check->viewpos + ) + { + return check; + } + } + + check = Add(hash); // killough + + check->height = plane; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->xform = *xform; + check->colormap = basecolormap; // [RH] Save colormap + check->sky = sky; + check->portal = portal; + check->extralight = renderportal->stacked_extralight; + check->visibility = renderportal->stacked_visibility; + check->viewpos = renderportal->stacked_viewpos; + check->viewangle = renderportal->stacked_angle.Yaw; + check->Alpha = alpha; + check->Additive = additive; + check->CurrentPortalUniq = renderportal->CurrentPortalUniq; + check->MirrorFlags = renderportal->MirrorFlags; + check->CurrentSkybox = Thread->Clip3D->CurrentSkybox; + + return check; + } + + VisiblePlane *VisiblePlaneList::GetRange(VisiblePlane *pl, int start, int stop) + { + int intrl, intrh; + int unionl, unionh; + int x; + + assert(start >= 0 && start < viewwidth); + assert(stop > start && stop <= viewwidth); + + if (start < pl->left) + { + intrl = pl->left; + unionl = start; + } + else + { + unionl = pl->left; + intrl = start; + } + + if (stop > pl->right) + { + intrh = pl->right; + unionh = stop; + } + else + { + unionh = pl->right; + intrh = stop; + } + + x = intrl; + while (x < intrh && pl->top[x] == 0x7fff) x++; + + if (x >= intrh) + { + // use the same visplane + pl->left = unionl; + pl->right = unionh; + } + else + { + // make a new visplane + unsigned hash; + + if (pl->portal != nullptr && !Thread->Portal->InSkyBox(pl->portal) && viewactive) + { + hash = MAXVISPLANES; + } + else + { + hash = CalcHash(pl->picnum.GetIndex(), pl->lightlevel, pl->height); + } + VisiblePlane *new_pl = Add(hash); + + new_pl->height = pl->height; + new_pl->picnum = pl->picnum; + new_pl->lightlevel = pl->lightlevel; + new_pl->xform = pl->xform; + new_pl->colormap = pl->colormap; + new_pl->portal = pl->portal; + new_pl->extralight = pl->extralight; + new_pl->visibility = pl->visibility; + new_pl->viewpos = pl->viewpos; + new_pl->viewangle = pl->viewangle; + new_pl->sky = pl->sky; + new_pl->Alpha = pl->Alpha; + new_pl->Additive = pl->Additive; + new_pl->CurrentPortalUniq = pl->CurrentPortalUniq; + new_pl->MirrorFlags = pl->MirrorFlags; + new_pl->CurrentSkybox = pl->CurrentSkybox; + new_pl->lights = pl->lights; + pl = new_pl; + pl->left = start; + pl->right = stop; + } + return pl; + } + + bool VisiblePlaneList::HasPortalPlanes() const + { + return visplanes[MAXVISPLANES] != nullptr; + } + + VisiblePlane *VisiblePlaneList::PopFirstPortalPlane() + { + VisiblePlane *pl = visplanes[VisiblePlaneList::MAXVISPLANES]; + if (pl) + { + visplanes[VisiblePlaneList::MAXVISPLANES] = pl->next; + pl->next = nullptr; + } + return pl; + } + + void VisiblePlaneList::ClearPortalPlanes() + { + visplanes[VisiblePlaneList::MAXVISPLANES] = nullptr; + } + + int VisiblePlaneList::Render() + { + VisiblePlane *pl; + int i; + int vpcount = 0; + + RenderPortal *renderportal = Thread->Portal.get(); + + for (i = 0; i < MAXVISPLANES; i++) + { + for (pl = visplanes[i]; pl; pl = pl->next) + { + // kg3D - draw only correct planes + if (pl->CurrentPortalUniq != renderportal->CurrentPortalUniq || pl->CurrentSkybox != Thread->Clip3D->CurrentSkybox) + continue; + // kg3D - draw only real planes now + if (pl->sky >= 0) { + vpcount++; + pl->Render(Thread, OPAQUE, false, false); + } + } + } + return vpcount; + } + + void VisiblePlaneList::RenderHeight(double height) + { + VisiblePlane *pl; + int i; + + DVector3 oViewPos = Thread->Viewport->viewpoint.Pos; + DAngle oViewAngle = Thread->Viewport->viewpoint.Angles.Yaw; + + RenderPortal *renderportal = Thread->Portal.get(); + + for (i = 0; i < MAXVISPLANES; i++) + { + for (pl = visplanes[i]; pl; pl = pl->next) + { + if (pl->CurrentSkybox != Thread->Clip3D->CurrentSkybox || pl->CurrentPortalUniq != renderportal->CurrentPortalUniq) + continue; + + if (pl->sky < 0 && pl->height.Zat0() == height) + { + Thread->Viewport->viewpoint.Pos = pl->viewpos; + Thread->Viewport->viewpoint.Angles.Yaw = pl->viewangle; + renderportal->MirrorFlags = pl->MirrorFlags; + + pl->Render(Thread, pl->sky & 0x7FFFFFFF, pl->Additive, true); + } + } + } + Thread->Viewport->viewpoint.Pos = oViewPos; + Thread->Viewport->viewpoint.Angles.Yaw = oViewAngle; + } +} diff --git a/src/swrenderer/plane/r_visibleplanelist.h b/src/swrenderer/plane/r_visibleplanelist.h new file mode 100644 index 0000000000..e07a086fd9 --- /dev/null +++ b/src/swrenderer/plane/r_visibleplanelist.h @@ -0,0 +1,55 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include "r_defs.h" + +struct FSectorPortal; + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlane; + + class VisiblePlaneList + { + public: + VisiblePlaneList(RenderThread *thread); + + void Clear(); + void ClearKeepFakePlanes(); + + VisiblePlane *FindPlane(const secplane_t &height, FTextureID picnum, int lightlevel, double Alpha, bool additive, const FTransform &xxform, int sky, FSectorPortal *portal, FDynamicColormap *basecolormap); + VisiblePlane *GetRange(VisiblePlane *pl, int start, int stop); + + bool HasPortalPlanes() const; + VisiblePlane *PopFirstPortalPlane(); + void ClearPortalPlanes(); + + int Render(); + void RenderHeight(double height); + + RenderThread *Thread = nullptr; + + private: + VisiblePlaneList(); + VisiblePlane *Add(unsigned hash); + + enum { MAXVISPLANES = 128 }; // must be a power of 2 + VisiblePlane *visplanes[MAXVISPLANES + 1]; + + static unsigned CalcHash(int picnum, int lightlevel, const secplane_t &height) { return (unsigned)((picnum) * 3 + (lightlevel)+(FLOAT2FIXED((height).fD())) * 7) & (MAXVISPLANES - 1); } + }; +} diff --git a/src/swrenderer/r_all.cpp b/src/swrenderer/r_all.cpp new file mode 100644 index 0000000000..d2e4e97d4f --- /dev/null +++ b/src/swrenderer/r_all.cpp @@ -0,0 +1,42 @@ +#include "r_memory.cpp" +#include "r_renderthread.cpp" +#include "r_swcanvas.cpp" +#include "r_swrenderer.cpp" +#include "drawers/r_draw.cpp" +#include "drawers/r_draw_pal.cpp" +#include "drawers/r_draw_rgba.cpp" +#include "drawers/r_thread.cpp" +#include "line/r_fogboundary.cpp" +#include "line/r_line.cpp" +#include "line/r_renderdrawsegment.cpp" +#include "line/r_walldraw.cpp" +#include "line/r_wallsetup.cpp" +#include "plane/r_flatplane.cpp" +#include "plane/r_planerenderer.cpp" +#include "plane/r_skyplane.cpp" +#include "plane/r_slopeplane.cpp" +#include "plane/r_visibleplane.cpp" +#include "plane/r_visibleplanelist.cpp" +#include "scene/r_3dfloors.cpp" +#include "scene/r_light.cpp" +#include "scene/r_opaque_pass.cpp" +#include "scene/r_portal.cpp" +#include "scene/r_scene.cpp" +#include "scene/r_translucent_pass.cpp" +#include "segments/r_clipsegment.cpp" +#include "segments/r_drawsegment.cpp" +#include "segments/r_portalsegment.cpp" +#include "things/r_decal.cpp" +#include "things/r_particle.cpp" +#include "things/r_playersprite.cpp" +#include "things/r_sprite.cpp" +#include "things/r_visiblesprite.cpp" +#include "things/r_visiblespritelist.cpp" +#include "things/r_voxel.cpp" +#include "things/r_wallsprite.cpp" +#include "viewport/r_drawerargs.cpp" +#include "viewport/r_skydrawer.cpp" +#include "viewport/r_spandrawer.cpp" +#include "viewport/r_spritedrawer.cpp" +#include "viewport/r_viewport.cpp" +#include "viewport/r_walldrawer.cpp" diff --git a/src/swrenderer/r_memory.cpp b/src/swrenderer/r_memory.cpp new file mode 100644 index 0000000000..11c5b0b3b4 --- /dev/null +++ b/src/swrenderer/r_memory.cpp @@ -0,0 +1,71 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "r_memory.h" + +namespace swrenderer +{ + void *RenderMemory::AllocBytes(int size) + { + size = (size + 15) / 16 * 16; // 16-byte align + + if (UsedBlocks.empty() || UsedBlocks.back()->Position + size > BlockSize) + { + if (!FreeBlocks.empty()) + { + auto block = std::move(FreeBlocks.back()); + block->Position = 0; + FreeBlocks.pop_back(); + UsedBlocks.push_back(std::move(block)); + } + else + { + UsedBlocks.push_back(std::make_unique()); + } + } + + auto &block = UsedBlocks.back(); + void *data = block->Data + block->Position; + block->Position += size; + + return data; + } + + void RenderMemory::Clear() + { + while (!UsedBlocks.empty()) + { + auto block = std::move(UsedBlocks.back()); + UsedBlocks.pop_back(); + FreeBlocks.push_back(std::move(block)); + } + } +} diff --git a/src/swrenderer/r_memory.h b/src/swrenderer/r_memory.h new file mode 100644 index 0000000000..2c7a04874b --- /dev/null +++ b/src/swrenderer/r_memory.h @@ -0,0 +1,59 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include + +namespace swrenderer +{ + // Memory needed for the duration of a frame rendering + class RenderMemory + { + public: + void Clear(); + + template + T *AllocMemory(int size = 1) + { + return (T*)AllocBytes(sizeof(T) * size); + } + + template + T *NewObject(Types &&... args) + { + void *ptr = AllocBytes(sizeof(T)); + return new (ptr)T(std::forward(args)...); + } + + private: + void *AllocBytes(int size); + + enum { BlockSize = 1024 * 1024 }; + + struct MemoryBlock + { + MemoryBlock() : Data(new uint8_t[BlockSize]), Position(0) { } + ~MemoryBlock() { delete[] Data; } + + MemoryBlock(const MemoryBlock &) = delete; + MemoryBlock &operator=(const MemoryBlock &) = delete; + + uint8_t *Data; + uint32_t Position; + }; + std::vector> UsedBlocks; + std::vector> FreeBlocks; + }; +} diff --git a/src/swrenderer/r_renderthread.cpp b/src/swrenderer/r_renderthread.cpp new file mode 100644 index 0000000000..559d57ec1d --- /dev/null +++ b/src/swrenderer/r_renderthread.cpp @@ -0,0 +1,90 @@ +/* +** Renderer multithreading framework +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "r_renderthread.h" +#include "swrenderer/things/r_visiblespritelist.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/things/r_playersprite.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/drawers/r_thread.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "swrenderer/viewport/r_viewport.h" +#include "r_memory.h" + +namespace swrenderer +{ + RenderThread::RenderThread(RenderScene *scene, bool mainThread) + { + Scene = scene; + MainThread = mainThread; + FrameMemory = std::make_unique(); + Viewport = std::make_unique(); + DrawQueue = std::make_shared(this); + OpaquePass = std::make_unique(this); + TranslucentPass = std::make_unique(this); + SpriteList = std::make_unique(); + Portal = std::make_unique(this); + Clip3D = std::make_unique(this); + PlayerSprites = std::make_unique(this); + PlaneList = std::make_unique(this); + DrawSegments = std::make_unique(this); + ClipSegments = std::make_unique(); + tc_drawers = std::make_unique(DrawQueue); + pal_drawers = std::make_unique(DrawQueue); + } + + RenderThread::~RenderThread() + { + } + + SWPixelFormatDrawers *RenderThread::Drawers(RenderViewport *viewport) + { + if (viewport->RenderTarget->IsBgra()) + return tc_drawers.get(); + else + return pal_drawers.get(); + } +} diff --git a/src/swrenderer/r_renderthread.h b/src/swrenderer/r_renderthread.h new file mode 100644 index 0000000000..70dfec3422 --- /dev/null +++ b/src/swrenderer/r_renderthread.h @@ -0,0 +1,86 @@ +/* +** Renderer multithreading framework +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include +#include + +class DrawerCommandQueue; +typedef std::shared_ptr DrawerCommandQueuePtr; + +namespace swrenderer +{ + class VisibleSpriteList; + class RenderPortal; + class RenderOpaquePass; + class RenderTranslucentPass; + class RenderPlayerSprites; + class RenderScene; + class RenderViewport; + class Clip3DFloors; + class VisiblePlaneList; + class DrawSegmentList; + class RenderClipSegment; + class RenderMemory; + class RenderViewport; + class SWPixelFormatDrawers; + class SWTruecolorDrawers; + class SWPalDrawers; + + class RenderThread + { + public: + RenderThread(RenderScene *scene, bool mainThread = true); + ~RenderThread(); + + RenderScene *Scene; + int X1 = 0; + int X2 = MAXWIDTH; + bool MainThread = false; + + std::unique_ptr FrameMemory; + std::unique_ptr OpaquePass; + std::unique_ptr TranslucentPass; + std::unique_ptr SpriteList; + std::unique_ptr Portal; + std::unique_ptr Clip3D; + std::unique_ptr PlayerSprites; + std::unique_ptr PlaneList; + std::unique_ptr DrawSegments; + std::unique_ptr ClipSegments; + std::unique_ptr Viewport; + DrawerCommandQueuePtr DrawQueue; + + std::thread thread; + + // VisibleSprite working buffers + short clipbot[MAXWIDTH]; + short cliptop[MAXWIDTH]; + + SWPixelFormatDrawers *Drawers(RenderViewport *viewport); + + private: + std::unique_ptr tc_drawers; + std::unique_ptr pal_drawers; + }; +} diff --git a/src/swrenderer/r_swcanvas.cpp b/src/swrenderer/r_swcanvas.cpp new file mode 100644 index 0000000000..df17ba5abd --- /dev/null +++ b/src/swrenderer/r_swcanvas.cpp @@ -0,0 +1,867 @@ +/* +** r_swcanvas.cpp +** Draw to a canvas using software rendering +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** 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 "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/r_renderthread.h" +#include "v_palette.h" +#include "v_video.h" +#include "m_png.h" +#include "colormatcher.h" +#include "r_swcanvas.h" +#include "textures/textures.h" +#include "r_data/voxels.h" +#include "drawers/r_draw_rgba.h" + +EXTERN_CVAR(Bool, r_blendmethod) + +using namespace swrenderer; + +void SWCanvas::DrawTexture(DCanvas *canvas, FTexture *img, DrawParms &parms) +{ + static short bottomclipper[MAXWIDTH], topclipper[MAXWIDTH]; + + static RenderThread thread(nullptr); + thread.DrawQueue->ThreadedRender = false; + + auto viewport = thread.Viewport.get(); + viewport->RenderTarget = canvas; + viewport->RenderTarget->Lock(true); + + lighttable_t *translation = nullptr; + FDynamicColormap *basecolormap = &identitycolormap; + int shade = 0; + + if (APART(parms.colorOverlay) != 0) + { + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + INTBOOL invertoverlay = (parms.style.Flags & STYLEF_InvertOverlay); + + if (parms.style.Flags & STYLEF_InvertSource) + { + invertoverlay = !invertoverlay; + } + if (invertoverlay) + { + parms.colorOverlay = PalEntry(parms.colorOverlay).InverseColor(); + } + // Note that this overrides the translation in software, but not in hardware. + FDynamicColormap *colormap = GetSpecialLights(MAKERGB(255, 255, 255), parms.colorOverlay & MAKEARGB(0, 255, 255, 255), 0); + + if (viewport->RenderTarget->IsBgra()) + { + basecolormap = colormap; + shade = (APART(parms.colorOverlay)*NUMCOLORMAPS / 255) << FRACBITS; + } + else + { + translation = &colormap->Maps[(APART(parms.colorOverlay)*NUMCOLORMAPS / 255) * 256]; + } + } + else if (parms.remap != NULL) + { + if (viewport->RenderTarget->IsBgra()) + translation = (lighttable_t *)parms.remap->Palette; + else + translation = parms.remap->Remap; + } + + SpriteDrawerArgs drawerargs; + + drawerargs.SetTranslationMap(translation); + drawerargs.SetLight(basecolormap, 0.0f, shade); + bool visible = drawerargs.SetStyle(viewport, parms.style, parms.Alpha, -1, parms.fillcolor, basecolormap); + + double x0 = parms.x - parms.left * parms.destwidth / parms.texwidth; + double y0 = parms.y - parms.top * parms.destheight / parms.texheight; + + if (visible) + { + double centeryback = viewport->CenterY; + viewport->CenterY = 0; + + int oldviewwindowx = 0; + int oldviewwindowy = 0; + oldviewwindowx = viewwindowx; + oldviewwindowy = viewwindowy; + viewwindowx = 0; + viewwindowy = 0; + + // There is not enough precision in the drawing routines to keep the full + // precision for y0. :( + double sprtopscreen; + modf(y0, &sprtopscreen); + + double yscale = parms.destheight / img->GetHeight(); + double iyscale = 1 / yscale; + + double spryscale = yscale; + assert(spryscale > 0); + + bool sprflipvert = false; + fixed_t iscale = FLOAT2FIXED(1 / spryscale); + //dc_texturemid = (CenterY - 1 - sprtopscreen) * iscale / 65536; + fixed_t frac = 0; + double xiscale = img->GetWidth() / parms.destwidth; + double x2 = x0 + parms.destwidth; + + short *mfloorclip; + short *mceilingclip; + + if (bottomclipper[0] != parms.dclip) + { + fillshort(bottomclipper, screen->GetWidth(), (short)parms.dclip); + } + if (parms.uclip != 0) + { + if (topclipper[0] != parms.uclip) + { + fillshort(topclipper, screen->GetWidth(), (short)parms.uclip); + } + mceilingclip = topclipper; + } + else + { + mceilingclip = zeroarray; + } + mfloorclip = bottomclipper; + + if (parms.flipX) + { + frac = (img->GetWidth() << FRACBITS) - 1; + xiscale = -xiscale; + } + + if (parms.windowleft > 0 || parms.windowright < parms.texwidth) + { + double wi = MIN(parms.windowright, parms.texwidth); + double xscale = parms.destwidth / parms.texwidth; + x0 += parms.windowleft * xscale; + frac += FLOAT2FIXED(parms.windowleft); + x2 -= (parms.texwidth - wi) * xscale; + } + if (x0 < parms.lclip) + { + frac += FLOAT2FIXED((parms.lclip - x0) * xiscale); + x0 = parms.lclip; + } + if (x2 > parms.rclip) + { + x2 = parms.rclip; + } + + int x = int(x0); + int x2_i = int(x2); + fixed_t xiscale_i = FLOAT2FIXED(xiscale); + + while (x < x2_i) + { + drawerargs.DrawMaskedColumn(&thread, x, iscale, img, frac, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, !parms.masked); + x++; + frac += xiscale_i; + } + + viewport->CenterY = centeryback; + viewwindowx = oldviewwindowx; + viewwindowy = oldviewwindowy; + } + + viewport->RenderTarget->Unlock(); + viewport->RenderTarget = canvas; +} + +void SWCanvas::FillSimplePoly(DCanvas *canvas, FTexture *tex, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, DAngle rotation, + FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) +{ + // Use an equation similar to player sprites to determine shade + fixed_t shade = LightVisibility::LightLevelToShade(lightlevel, true) - 12 * FRACUNIT; + float topy, boty, leftx, rightx; + int toppt, botpt, pt1, pt2; + int i; + int y1, y2, y; + fixed_t x; + bool dorotate = rotation != 0.; + double cosrot, sinrot; + + if (--npoints < 2) + { // not a polygon or we're not locked + return; + } + + if (bottomclip <= 0) + { + bottomclip = canvas->GetHeight(); + } + + // Find the extents of the polygon, in particular the highest and lowest points. + for (botpt = toppt = 0, boty = topy = points[0].Y, leftx = rightx = points[0].X, i = 1; i <= npoints; ++i) + { + if (points[i].Y < topy) + { + topy = points[i].Y; + toppt = i; + } + if (points[i].Y > boty) + { + boty = points[i].Y; + botpt = i; + } + if (points[i].X < leftx) + { + leftx = points[i].X; + } + if (points[i].X > rightx) + { + rightx = points[i].X; + } + } + if (topy >= bottomclip || // off the bottom of the screen + boty <= 0 || // off the top of the screen + leftx >= canvas->GetWidth() || // off the right of the screen + rightx <= 0) // off the left of the screen + { + return; + } + + static RenderThread thread(nullptr); + thread.DrawQueue->ThreadedRender = false; + + auto viewport = thread.Viewport.get(); + viewport->RenderTarget = canvas; + + viewport->RenderTarget->Lock(true); + + scalex /= tex->Scale.X; + scaley /= tex->Scale.Y; + + // Use the CRT's functions here. + cosrot = cos(rotation.Radians()); + sinrot = sin(rotation.Radians()); + + // Setup constant texture mapping parameters. + SpanDrawerArgs drawerargs; + drawerargs.SetTexture(viewport, tex); + if (colormap) + drawerargs.SetLight(colormap, 0, clamp(shade >> FRACBITS, 0, NUMCOLORMAPS - 1)); + else + drawerargs.SetLight(&identitycolormap, 0, 0); + if (drawerargs.TextureWidthBits() != 0) + { + scalex = double(1u << (32 - drawerargs.TextureWidthBits())) / scalex; + drawerargs.SetTextureUStep(xs_RoundToInt(cosrot * scalex)); + } + else + { // Texture is one pixel wide. + scalex = 0; + drawerargs.SetTextureUStep(0); + } + if (drawerargs.TextureHeightBits() != 0) + { + scaley = double(1u << (32 - drawerargs.TextureHeightBits())) / scaley; + drawerargs.SetTextureVStep(xs_RoundToInt(sinrot * scaley)); + } + else + { // Texture is one pixel tall. + scaley = 0; + drawerargs.SetTextureVStep(0); + } + + int width = canvas->GetWidth(); + + // Travel down the right edge and create an outline of that edge. + static short spanend[MAXHEIGHT]; + pt1 = toppt; + pt2 = toppt + 1; if (pt2 > npoints) pt2 = 0; + y1 = xs_RoundToInt(points[pt1].Y + 0.5f); + do + { + x = FLOAT2FIXED(points[pt1].X + 0.5f); + y2 = xs_RoundToInt(points[pt2].Y + 0.5f); + if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) + { + } + else + { + fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); + int y3 = MIN(y2, bottomclip); + if (y1 < 0) + { + x += xinc * -y1; + y1 = 0; + } + for (y = y1; y < y3; ++y) + { + spanend[y] = clamp(x >> FRACBITS, -1, width); + x += xinc; + } + } + y1 = y2; + pt1 = pt2; + pt2++; if (pt2 > npoints) pt2 = 0; + } while (pt1 != botpt); + + // Travel down the left edge and fill it in. + pt1 = toppt; + pt2 = toppt - 1; if (pt2 < 0) pt2 = npoints; + y1 = xs_RoundToInt(points[pt1].Y + 0.5f); + do + { + x = FLOAT2FIXED(points[pt1].X + 0.5f); + y2 = xs_RoundToInt(points[pt2].Y + 0.5f); + if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) + { + } + else + { + fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); + int y3 = MIN(y2, bottomclip); + if (y1 < 0) + { + x += xinc * -y1; + y1 = 0; + } + for (y = y1; y < y3; ++y) + { + int x1 = x >> FRACBITS; + int x2 = spanend[y]; + if (x2 > x1 && x2 > 0 && x1 < width) + { + x1 = MAX(x1, 0); + x2 = MIN(x2, width); +#if 0 + memset(this->Buffer + y * this->Pitch + x1, (int)tex, x2 - x1); +#else + drawerargs.SetDestY(viewport, y); + drawerargs.SetDestX1(x1); + drawerargs.SetDestX2(x2 - 1); + + DVector2 tex(x1 - originx, y - originy); + if (dorotate) + { + double t = tex.X; + tex.X = t * cosrot - tex.Y * sinrot; + tex.Y = tex.Y * cosrot + t * sinrot; + } + drawerargs.SetTextureUPos(xs_RoundToInt(tex.X * scalex)); + drawerargs.SetTextureVPos(xs_RoundToInt(tex.Y * scaley)); + + drawerargs.DrawSpan(&thread); +#endif + } + x += xinc; + } + } + y1 = y2; + pt1 = pt2; + pt2--; if (pt2 < 0) pt2 = npoints; + } while (pt1 != botpt); + + viewport->RenderTarget->Unlock(); + viewport->RenderTarget = screen; +} + +void SWCanvas::DrawLine(DCanvas *canvas, int x0, int y0, int x1, int y1, int palColor, uint32_t realcolor) +{ + const int WeightingScale = 0; + const int WEIGHTBITS = 6; + const int WEIGHTSHIFT = 16 - WEIGHTBITS; + const int NUMWEIGHTS = (1 << WEIGHTBITS); + const int WEIGHTMASK = (NUMWEIGHTS - 1); + + if (palColor < 0) + { + palColor = PalFromRGB(realcolor); + } + + canvas->Lock(); + int deltaX, deltaY, xDir; + + if (y0 > y1) + { + int temp = y0; y0 = y1; y1 = temp; + temp = x0; x0 = x1; x1 = temp; + } + + PUTTRANSDOT(canvas, x0, y0, palColor, 0); + + if ((deltaX = x1 - x0) >= 0) + { + xDir = 1; + } + else + { + xDir = -1; + deltaX = -deltaX; + } + + if ((deltaY = y1 - y0) == 0) + { // horizontal line + if (x0 > x1) + { + swapvalues(x0, x1); + } + if (canvas->IsBgra()) + { + uint32_t fillColor = GPalette.BaseColors[palColor].d; + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + for (int i = 0; i <= deltaX; i++) + spot[i] = fillColor; + } + else + { + memset(canvas->GetBuffer() + y0*canvas->GetPitch() + x0, palColor, deltaX + 1); + } + } + else if (deltaX == 0) + { // vertical line + if (canvas->IsBgra()) + { + uint32_t fillColor = GPalette.BaseColors[palColor].d; + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + int pitch = canvas->GetPitch(); + do + { + *spot = fillColor; + spot += pitch; + } while (--deltaY != 0); + } + else + { + uint8_t *spot = canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + int pitch = canvas->GetPitch(); + do + { + *spot = palColor; + spot += pitch; + } while (--deltaY != 0); + } + } + else if (deltaX == deltaY) + { // diagonal line. + if (canvas->IsBgra()) + { + uint32_t fillColor = GPalette.BaseColors[palColor].d; + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + int advance = canvas->GetPitch() + xDir; + do + { + *spot = fillColor; + spot += advance; + } while (--deltaY != 0); + } + else + { + uint8_t *spot = canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + int advance = canvas->GetPitch() + xDir; + do + { + *spot = palColor; + spot += advance; + } while (--deltaY != 0); + } + } + else + { + // line is not horizontal, diagonal, or vertical + fixed_t errorAcc = 0; + + if (deltaY > deltaX) + { // y-major line + fixed_t errorAdj = (((unsigned)deltaX << 16) / (unsigned)deltaY) & 0xffff; + if (xDir < 0) + { + if (WeightingScale == 0) + { + while (--deltaY) + { + errorAcc += errorAdj; + y0++; + int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0 - (errorAcc >> 16), y0, palColor, weighting); + PUTTRANSDOT(canvas, x0 - (errorAcc >> 16) - 1, y0, + palColor, WEIGHTMASK - weighting); + } + } + else + { + while (--deltaY) + { + errorAcc += errorAdj; + y0++; + int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT + 8)) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0 - (errorAcc >> 16), y0, palColor, weighting); + PUTTRANSDOT(canvas, x0 - (errorAcc >> 16) - 1, y0, + palColor, WEIGHTMASK - weighting); + } + } + } + else + { + if (WeightingScale == 0) + { + while (--deltaY) + { + errorAcc += errorAdj; + y0++; + int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0 + (errorAcc >> 16), y0, palColor, weighting); + PUTTRANSDOT(canvas, x0 + (errorAcc >> 16) + xDir, y0, + palColor, WEIGHTMASK - weighting); + } + } + else + { + while (--deltaY) + { + errorAcc += errorAdj; + y0++; + int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT + 8)) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0 + (errorAcc >> 16), y0, palColor, weighting); + PUTTRANSDOT(canvas, x0 + (errorAcc >> 16) + xDir, y0, + palColor, WEIGHTMASK - weighting); + } + } + } + } + else + { // x-major line + fixed_t errorAdj = (((uint32_t)deltaY << 16) / (uint32_t)deltaX) & 0xffff; + + if (WeightingScale == 0) + { + while (--deltaX) + { + errorAcc += errorAdj; + x0 += xDir; + int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0, y0 + (errorAcc >> 16), palColor, weighting); + PUTTRANSDOT(canvas, x0, y0 + (errorAcc >> 16) + 1, + palColor, WEIGHTMASK - weighting); + } + } + else + { + while (--deltaX) + { + errorAcc += errorAdj; + x0 += xDir; + int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT + 8)) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0, y0 + (errorAcc >> 16), palColor, weighting); + PUTTRANSDOT(canvas, x0, y0 + (errorAcc >> 16) + 1, + palColor, WEIGHTMASK - weighting); + } + } + } + PUTTRANSDOT(canvas, x1, y1, palColor, 0); + } + canvas->Unlock(); +} + +void SWCanvas::DrawPixel(DCanvas *canvas, int x, int y, int palColor, uint32_t realcolor) +{ + if (palColor < 0) + { + palColor = PalFromRGB(realcolor); + } + + canvas->GetBuffer()[canvas->GetPitch() * y + x] = (uint8_t)palColor; +} + +void SWCanvas::PUTTRANSDOT(DCanvas *canvas, int xx, int yy, int basecolor, int level) +{ + static int oldyy; + static int oldyyshifted; + + if (yy == oldyy + 1) + { + oldyy++; + oldyyshifted += canvas->GetPitch(); + } + else if (yy == oldyy - 1) + { + oldyy--; + oldyyshifted -= canvas->GetPitch(); + } + else if (yy != oldyy) + { + oldyy = yy; + oldyyshifted = yy * canvas->GetPitch(); + } + + if (canvas->IsBgra()) + { + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + oldyyshifted + xx; + + uint32_t fg = swrenderer::LightBgra::shade_pal_index_simple(basecolor, swrenderer::LightBgra::calc_light_multiplier(0)); + uint32_t fg_red = (((fg >> 16) & 0xff) * (63 - level)) >> 6; + uint32_t fg_green = (((fg >> 8) & 0xff) * (63 - level)) >> 6; + uint32_t fg_blue = ((fg & 0xff) * (63 - level)) >> 6; + + uint32_t bg_red = (((*spot >> 16) & 0xff) * level) >> 6; + uint32_t bg_green = (((*spot >> 8) & 0xff) * level) >> 6; + uint32_t bg_blue = (((*spot) & 0xff) * level) >> 6; + + uint32_t red = fg_red + bg_red; + uint32_t green = fg_green + bg_green; + uint32_t blue = fg_blue + bg_blue; + + *spot = 0xff000000 | (red << 16) | (green << 8) | blue; + } + else if (!r_blendmethod) + { + uint8_t *spot = canvas->GetBuffer() + oldyyshifted + xx; + uint32_t *bg2rgb = Col2RGB8[1 + level]; + uint32_t *fg2rgb = Col2RGB8[63 - level]; + uint32_t fg = fg2rgb[basecolor]; + uint32_t bg = bg2rgb[*spot]; + bg = (fg + bg) | 0x1f07c1f; + *spot = RGB32k.All[bg&(bg >> 15)]; + } + else + { + uint8_t *spot = canvas->GetBuffer() + oldyyshifted + xx; + + uint32_t r = (GPalette.BaseColors[*spot].r * (64 - level) + GPalette.BaseColors[basecolor].r * level) / 256; + uint32_t g = (GPalette.BaseColors[*spot].g * (64 - level) + GPalette.BaseColors[basecolor].g * level) / 256; + uint32_t b = (GPalette.BaseColors[*spot].b * (64 - level) + GPalette.BaseColors[basecolor].b * level) / 256; + + *spot = (uint8_t)RGB256k.RGB[r][g][b]; + } +} + +void SWCanvas::Clear(DCanvas *canvas, int left, int top, int right, int bottom, int palcolor, uint32_t color) +{ + int x, y; + + if (left == right || top == bottom) + { + return; + } + + assert(left < right); + assert(top < bottom); + + int Width = canvas->GetWidth(); + int Height = canvas->GetHeight(); + int Pitch = canvas->GetPitch(); + + if (left >= Width || right <= 0 || top >= Height || bottom <= 0) + { + return; + } + left = MAX(0, left); + right = MIN(Width, right); + top = MAX(0, top); + bottom = MIN(Height, bottom); + + if (palcolor < 0) + { + palcolor = PalFromRGB(color); + } + + if (canvas->IsBgra()) + { + uint32_t fill_color = GPalette.BaseColors[palcolor]; + + uint32_t *dest = (uint32_t*)canvas->GetBuffer() + top * Pitch + left; + x = right - left; + for (y = top; y < bottom; y++) + { + for (int i = 0; i < x; i++) + dest[i] = fill_color; + dest += Pitch; + } + } + else + { + uint8_t *dest = canvas->GetBuffer() + top * Pitch + left; + x = right - left; + for (y = top; y < bottom; y++) + { + memset(dest, palcolor, x); + dest += Pitch; + } + } +} + +void SWCanvas::Dim(DCanvas *canvas, PalEntry color, float damount, int x1, int y1, int w, int h) +{ + if (damount == 0.f) + return; + + int Width = canvas->GetWidth(); + int Height = canvas->GetHeight(); + int Pitch = canvas->GetPitch(); + + if (x1 >= Width || y1 >= Height) + { + return; + } + if (x1 + w > Width) + { + w = Width - x1; + } + if (y1 + h > Height) + { + h = Height - y1; + } + if (w <= 0 || h <= 0) + { + return; + } + + if (canvas->IsBgra()) + { + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + x1 + y1*Pitch; + int gap = Pitch - w; + + uint32_t fg = color.d; + uint32_t fg_red = (fg >> 16) & 0xff; + uint32_t fg_green = (fg >> 8) & 0xff; + uint32_t fg_blue = fg & 0xff; + + uint32_t alpha = (uint32_t)clamp(damount * 256 + 0.5f, 0.0f, 256.0f); + uint32_t inv_alpha = 256 - alpha; + + fg_red *= alpha; + fg_green *= alpha; + fg_blue *= alpha; + + for (int y = h; y != 0; y--) + { + for (int x = w; x != 0; x--) + { + uint32_t bg_red = (*spot >> 16) & 0xff; + uint32_t bg_green = (*spot >> 8) & 0xff; + uint32_t bg_blue = (*spot) & 0xff; + + uint32_t red = (fg_red + bg_red * inv_alpha) / 256; + uint32_t green = (fg_green + bg_green * inv_alpha) / 256; + uint32_t blue = (fg_blue + bg_blue * inv_alpha) / 256; + + *spot = 0xff000000 | (red << 16) | (green << 8) | blue; + spot++; + } + spot += gap; + } + } + else + { + uint32_t *bg2rgb; + uint32_t fg; + + uint8_t *spot = canvas->GetBuffer() + x1 + y1*Pitch; + int gap = Pitch - w; + + int alpha = (int)((float)64 * damount); + int ialpha = 64 - alpha; + int dimmedcolor_r = color.r * alpha; + int dimmedcolor_g = color.g * alpha; + int dimmedcolor_b = color.b * alpha; + + if (!r_blendmethod) + { + { + int amount; + + amount = (int)(damount * 64); + bg2rgb = Col2RGB8[64 - amount]; + + fg = (((color.r * amount) >> 4) << 20) | + ((color.g * amount) >> 4) | + (((color.b * amount) >> 4) << 10); + } + + for (int y = h; y != 0; y--) + { + for (int x = w; x != 0; x--) + { + uint32_t bg; + + bg = bg2rgb[(*spot) & 0xff]; + bg = (fg + bg) | 0x1f07c1f; + *spot = RGB32k.All[bg&(bg >> 15)]; + spot++; + } + spot += gap; + } + } + else + { + for (int y = h; y != 0; y--) + { + for (int x = w; x != 0; x--) + { + uint32_t r = (dimmedcolor_r + GPalette.BaseColors[*spot].r * ialpha) >> 8; + uint32_t g = (dimmedcolor_g + GPalette.BaseColors[*spot].g * ialpha) >> 8; + uint32_t b = (dimmedcolor_b + GPalette.BaseColors[*spot].b * ialpha) >> 8; + *spot = (uint8_t)RGB256k.RGB[r][g][b]; + spot++; + } + spot += gap; + } + } + } +} + +int SWCanvas::PalFromRGB(uint32_t rgb) +{ + // For routines that take RGB colors, cache the previous lookup in case there + // are several repetitions with the same color. + static int LastPal = -1; + static uint32_t LastRGB; + + if (LastPal >= 0 && LastRGB == rgb) + { + return LastPal; + } + // Quick check for black and white. + if (rgb == MAKEARGB(255, 0, 0, 0)) + { + LastPal = GPalette.BlackIndex; + } + else if (rgb == MAKEARGB(255, 255, 255, 255)) + { + LastPal = GPalette.WhiteIndex; + } + else + { + LastPal = ColorMatcher.Pick(RPART(rgb), GPART(rgb), BPART(rgb)); + } + LastRGB = rgb; + return LastPal; +} diff --git a/src/swrenderer/r_swcanvas.h b/src/swrenderer/r_swcanvas.h new file mode 100644 index 0000000000..01c9cbb7db --- /dev/null +++ b/src/swrenderer/r_swcanvas.h @@ -0,0 +1,22 @@ + +#pragma once + +#include "v_video.h" +#include "r_data/colormaps.h" + +class SWCanvas +{ +public: + static void DrawTexture(DCanvas *canvas, FTexture *img, DrawParms &parms); + static void FillSimplePoly(DCanvas *canvas, FTexture *tex, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, DAngle rotation, + FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip); + static void DrawLine(DCanvas *canvas, int x0, int y0, int x1, int y1, int palColor, uint32_t realcolor); + static void DrawPixel(DCanvas *canvas, int x, int y, int palColor, uint32_t realcolor); + static void Clear(DCanvas *canvas, int left, int top, int right, int bottom, int palcolor, uint32_t color); + static void Dim(DCanvas *canvas, PalEntry color, float damount, int x1, int y1, int w, int h); + +private: + static void PUTTRANSDOT(DCanvas *canvas, int xx, int yy, int basecolor, int level); + static int PalFromRGB(uint32_t rgb); +}; diff --git a/src/swrenderer/r_swrenderer.cpp b/src/swrenderer/r_swrenderer.cpp new file mode 100644 index 0000000000..001e6f360d --- /dev/null +++ b/src/swrenderer/r_swrenderer.cpp @@ -0,0 +1,379 @@ +/* +** r_swrender.cpp +** Software renderer interface +** +**--------------------------------------------------------------------------- +** Copyright 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. +** +** 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 "swrenderer/scene/r_scene.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/things/r_playersprite.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "v_palette.h" +#include "v_video.h" +#include "m_png.h" +#include "r_swrenderer.h" +#include "scene/r_opaque_pass.h" +#include "scene/r_3dfloors.h" +#include "scene/r_portal.h" +#include "textures/textures.h" +#include "r_data/voxels.h" +#include "drawers/r_draw_rgba.h" +#include "polyrenderer/poly_renderer.h" +#include "p_setup.h" + +void gl_InitData(); +void gl_PreprocessLevel(); +void gl_CleanLevelData(); + +EXTERN_CVAR(Bool, r_shadercolormaps) +EXTERN_CVAR(Float, maxviewpitch) // [SP] CVAR from GZDoom + +CUSTOM_CVAR(Bool, r_polyrenderer, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self == 1 && !hasglnodes) + { + Printf("No GL BSP detected. You must restart the map before rendering will be correct\n"); + } + + if (usergame) + { + // [SP] Update pitch limits to the netgame/gamesim. + players[consoleplayer].SendPitchLimits(); + } +} + +using namespace swrenderer; + +FSoftwareRenderer::FSoftwareRenderer() +{ +} + +FSoftwareRenderer::~FSoftwareRenderer() +{ +} + +void FSoftwareRenderer::Init() +{ + mScene.Init(); +} + +bool FSoftwareRenderer::UsesColormap() const +{ + return true; +} + +void FSoftwareRenderer::PrecacheTexture(FTexture *tex, int cache) +{ + bool isbgra = screen->IsBgra(); + + if (tex != NULL) + { + if (cache & FTextureManager::HIT_Columnmode) + { + const FTexture::Span *spanp; + if (isbgra) + tex->GetColumnBgra(0, &spanp); + else + tex->GetColumn(0, &spanp); + } + else if (cache != 0) + { + if (isbgra) + tex->GetPixelsBgra(); + else + tex->GetPixels (); + } + else + { + tex->Unload (); + } + } +} + +void FSoftwareRenderer::Precache(uint8_t *texhitlist, TMap &actorhitlist) +{ + uint8_t *spritelist = new uint8_t[sprites.Size()]; + TMap::Iterator it(actorhitlist); + TMap::Pair *pair; + + memset(spritelist, 0, sprites.Size()); + + while (it.NextPair(pair)) + { + PClassActor *cls = pair->Key; + + for (int i = 0; i < cls->NumOwnedStates; i++) + { + spritelist[cls->OwnedStates[i].sprite] = true; + } + } + + // Precache textures (and sprites). + + for (int i = (int)(sprites.Size() - 1); i >= 0; i--) + { + if (spritelist[i]) + { + int j, k; + for (j = 0; j < sprites[i].numframes; j++) + { + const spriteframe_t *frame = &SpriteFrames[sprites[i].spriteframes + j]; + + for (k = 0; k < 16; k++) + { + FTextureID pic = frame->Texture[k]; + if (pic.isValid()) + { + texhitlist[pic.GetIndex()] = FTextureManager::HIT_Sprite; + } + } + } + } + } + delete[] spritelist; + + int cnt = TexMan.NumTextures(); + for (int i = cnt - 1; i >= 0; i--) + { + PrecacheTexture(TexMan.ByIndex(i), texhitlist[i]); + } +} + +void FSoftwareRenderer::RenderView(player_t *player) +{ + if (r_polyrenderer) + { + PolyRenderer::Instance()->Thread.Viewport->viewpoint = r_viewpoint; + PolyRenderer::Instance()->Thread.Viewport->viewwindow = r_viewwindow; + PolyRenderer::Instance()->RenderView(player); + r_viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + r_viewwindow = PolyRenderer::Instance()->Thread.Viewport->viewwindow; + } + else + { + mScene.MainThread()->Viewport->viewpoint = r_viewpoint; + mScene.MainThread()->Viewport->viewwindow = r_viewwindow; + mScene.RenderView(player); + r_viewpoint = mScene.MainThread()->Viewport->viewpoint; + r_viewwindow = mScene.MainThread()->Viewport->viewwindow; + } + + FCanvasTextureInfo::UpdateAll(); +} + +void FSoftwareRenderer::RemapVoxels() +{ + for (unsigned i=0; iCreateBgraSlabData(); + Voxels[i]->Remap(); + } +} + +void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) +{ + DCanvas *pic = new DSimpleCanvas (width, height, false); + PalEntry palette[256]; + + // Take a snapshot of the player's view + pic->ObjectFlags |= OF_Fixed; + pic->Lock (); + if (r_polyrenderer) + { + PolyRenderer::Instance()->Thread.Viewport->viewpoint = r_viewpoint; + PolyRenderer::Instance()->Thread.Viewport->viewwindow = r_viewwindow; + PolyRenderer::Instance()->RenderViewToCanvas(player->mo, pic, 0, 0, width, height, true); + r_viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + r_viewwindow = PolyRenderer::Instance()->Thread.Viewport->viewwindow; + } + else + { + mScene.MainThread()->Viewport->viewpoint = r_viewpoint; + mScene.MainThread()->Viewport->viewwindow = r_viewwindow; + mScene.RenderViewToCanvas(player->mo, pic, 0, 0, width, height); + r_viewpoint = mScene.MainThread()->Viewport->viewpoint; + r_viewwindow = mScene.MainThread()->Viewport->viewwindow; + } + screen->GetFlashedPalette (palette); + M_CreatePNG (file, pic->GetBuffer(), palette, SS_PAL, width, height, pic->GetPitch()); + pic->Unlock (); + pic->Destroy(); + pic->ObjectFlags |= OF_YesReallyDelete; + delete pic; +} + +void FSoftwareRenderer::DrawRemainingPlayerSprites() +{ + if (!r_polyrenderer) + { + mScene.MainThread()->Viewport->viewpoint = r_viewpoint; + mScene.MainThread()->Viewport->viewwindow = r_viewwindow; + mScene.MainThread()->PlayerSprites->RenderRemaining(); + r_viewpoint = mScene.MainThread()->Viewport->viewpoint; + r_viewwindow = mScene.MainThread()->Viewport->viewwindow; + } + else + { + PolyRenderer::Instance()->Thread.Viewport->viewpoint = r_viewpoint; + PolyRenderer::Instance()->Thread.Viewport->viewwindow = r_viewwindow; + PolyRenderer::Instance()->RenderRemainingPlayerSprites(); + r_viewpoint = PolyRenderer::Instance()->Thread.Viewport->viewpoint; + r_viewwindow = PolyRenderer::Instance()->Thread.Viewport->viewwindow; + } +} + +int FSoftwareRenderer::GetMaxViewPitch(bool down) +{ + const int MAX_DN_ANGLE = 56; // Max looking down angle + const int MAX_UP_ANGLE = 32; // Max looking up angle + return (r_polyrenderer) ? int(maxviewpitch) : (down ? MAX_DN_ANGLE : MAX_UP_ANGLE); +} + +bool FSoftwareRenderer::RequireGLNodes() +{ + return true; +} + +void FSoftwareRenderer::OnModeSet () +{ + mScene.ScreenResized(); +} + +void FSoftwareRenderer::SetClearColor(int color) +{ + mScene.SetClearColor(color); +} + +void FSoftwareRenderer::RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) +{ + auto viewport = r_polyrenderer ? PolyRenderer::Instance()->Thread.Viewport.get() : mScene.MainThread()->Viewport.get(); + + // Grab global state shared with rest of zdoom + viewport->viewpoint = r_viewpoint; + viewport->viewwindow = r_viewwindow; + + uint8_t *Pixels = viewport->RenderTarget->IsBgra() ? (uint8_t*)tex->GetPixelsBgra() : (uint8_t*)tex->GetPixels(); + DSimpleCanvas *Canvas = viewport->RenderTarget->IsBgra() ? tex->GetCanvasBgra() : tex->GetCanvas(); + + // curse Doom's overuse of global variables in the renderer. + // These get clobbered by rendering to a camera texture but they need to be preserved so the final rendering can be done with the correct palette. + CameraLight savedCameraLight = *CameraLight::Instance(); + + DAngle savedfov = viewport->viewpoint.FieldOfView; + R_SetFOV (viewport->viewpoint, (double)fov); + + if (r_polyrenderer) + PolyRenderer::Instance()->RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), tex->bFirstUpdate); + else + mScene.RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), tex->bFirstUpdate); + + R_SetFOV (viewport->viewpoint, savedfov); + + if (Canvas->IsBgra()) + { + if (Pixels == Canvas->GetBuffer()) + { + FTexture::FlipSquareBlockBgra((uint32_t*)Pixels, tex->GetWidth(), tex->GetHeight()); + } + else + { + FTexture::FlipNonSquareBlockBgra((uint32_t*)Pixels, (const uint32_t*)Canvas->GetBuffer(), tex->GetWidth(), tex->GetHeight(), Canvas->GetPitch()); + } + } + else + { + if (Pixels == Canvas->GetBuffer()) + { + FTexture::FlipSquareBlockRemap(Pixels, tex->GetWidth(), tex->GetHeight(), GPalette.Remap); + } + else + { + FTexture::FlipNonSquareBlockRemap(Pixels, Canvas->GetBuffer(), tex->GetWidth(), tex->GetHeight(), Canvas->GetPitch(), GPalette.Remap); + } + } + + if (viewport->RenderTarget->IsBgra()) + { + // True color render still sometimes uses palette textures (for sprites, mostly). + // We need to make sure that both pixel buffers contain data: + int width = tex->GetWidth(); + int height = tex->GetHeight(); + uint8_t *palbuffer = (uint8_t *)tex->GetPixels(); + uint32_t *bgrabuffer = (uint32_t*)tex->GetPixelsBgra(); + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + uint32_t color = bgrabuffer[y]; + int r = RPART(color); + int g = GPART(color); + int b = BPART(color); + palbuffer[y] = RGB32k.RGB[r >> 3][g >> 3][b >> 3]; + } + palbuffer += height; + bgrabuffer += height; + } + } + + tex->SetUpdated(); + + *CameraLight::Instance() = savedCameraLight; + + // Sync state back to zdoom + r_viewpoint = viewport->viewpoint; + r_viewwindow = viewport->viewwindow; +} + +sector_t *FSoftwareRenderer::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) +{ + return mScene.MainThread()->OpaquePass->FakeFlat(sec, tempsec, floorlightlevel, ceilinglightlevel, nullptr, 0, 0, 0, 0); +} + +void FSoftwareRenderer::PreprocessLevel() +{ + gl_PreprocessLevel(); +} + +void FSoftwareRenderer::CleanLevelData() +{ + gl_CleanLevelData(); +} + +double FSoftwareRenderer::GetVisibility() +{ + return LightVisibility::Instance()->GetVisibility(); +} + +void FSoftwareRenderer::SetVisibility(double vis) +{ + LightVisibility::Instance()->SetVisibility(mScene.MainThread()->Viewport.get(), vis); +} diff --git a/src/swrenderer/r_swrenderer.h b/src/swrenderer/r_swrenderer.h new file mode 100644 index 0000000000..d79d4806cf --- /dev/null +++ b/src/swrenderer/r_swrenderer.h @@ -0,0 +1,49 @@ + +#pragma once + +#include "r_renderer.h" +#include "swrenderer/scene/r_scene.h" + +struct FSoftwareRenderer : public FRenderer +{ + FSoftwareRenderer(); + ~FSoftwareRenderer(); + + // Can be overridden so that the colormaps for sector color/fade won't be built. + bool UsesColormap() const override; + + // precache textures + void Precache(uint8_t *texhitlist, TMap &actorhitlist) override; + + // render 3D view + void RenderView(player_t *player) override; + + // Remap voxel palette + void RemapVoxels() override; + + // renders view to a savegame picture + void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override; + + // draws player sprites with hardware acceleration (only useful for software rendering) + void DrawRemainingPlayerSprites() override; + + int GetMaxViewPitch(bool down) override; + bool RequireGLNodes() override; + + void OnModeSet() override; + void SetClearColor(int color) override; + void Init() override; + void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) override; + sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) override; + + void PreprocessLevel() override; + void CleanLevelData() override; + + double GetVisibility() override; + void SetVisibility(double vis) override; + +private: + void PrecacheTexture(FTexture *tex, int cache); + + swrenderer::RenderScene mScene; +}; diff --git a/src/swrenderer/scene/r_3dfloors.cpp b/src/swrenderer/scene/r_3dfloors.cpp new file mode 100644 index 0000000000..f1496cd9e7 --- /dev/null +++ b/src/swrenderer/scene/r_3dfloors.cpp @@ -0,0 +1,184 @@ +/* +** r_3dfloors.cpp +** software 3D floors addon +** +** by kgsws +*/ + +#include "templates.h" +#include "doomdef.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "r_opaque_pass.h" +#include "c_cvars.h" +#include "r_3dfloors.h" +#include "r_utility.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/r_memory.h" + +CVAR(Int, r_3dfloors, true, 0); + +namespace swrenderer +{ + Clip3DFloors::Clip3DFloors(RenderThread *thread) + { + Thread = thread; + } + + void Clip3DFloors::SetFakeFloor(F3DFloor *newFakeFloor) + { + auto &clip = FakeFloors[newFakeFloor]; + if (clip == nullptr) + { + clip = Thread->FrameMemory->NewObject(newFakeFloor); + } + fakeFloor = clip; + } + + void Clip3DFloors::Cleanup() + { + FakeFloors.clear(); + + fakeActive = false; + fake3D = 0; + while (CurrentSkybox) + { + DeleteHeights(); + LeaveSkybox(); + } + ResetClip(); + DeleteHeights(); + } + + void Clip3DFloors::DeleteHeights() + { + height_cur = height_top; + while (height_cur) + { + height_top = height_cur; + height_cur = height_cur->next; + M_Free(height_top); + } + height_max = -1; + height_top = height_cur = nullptr; + } + + void Clip3DFloors::AddHeight(secplane_t *add, sector_t *sec) + { + HeightLevel *near; + HeightLevel *curr; + + double height = add->ZatPoint(Thread->Viewport->viewpoint.Pos); + if (height >= sec->CenterCeiling()) return; + if (height <= sec->CenterFloor()) return; + + fakeActive = true; + + if (height_max >= 0) + { + near = height_top; + while (near && near->height < height) near = near->next; + if (near) + { + if (near->height == height) return; + curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); + curr->height = height; + curr->prev = near->prev; + curr->next = near; + if (near->prev) near->prev->next = curr; + else height_top = curr; + near->prev = curr; + } + else + { + curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); + curr->height = height; + curr->prev = height_cur; + curr->next = nullptr; + height_cur->next = curr; + height_cur = curr; + } + } + else + { + height_top = height_cur = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); + height_top->height = height; + height_top->prev = nullptr; + height_top->next = nullptr; + } + height_max++; + } + + void Clip3DFloors::NewClip() + { + ClipStack *curr; + + curr = (ClipStack*)M_Malloc(sizeof(ClipStack)); + curr->next = 0; + memcpy(curr->floorclip, Thread->OpaquePass->floorclip, sizeof(short) * MAXWIDTH); + memcpy(curr->ceilingclip, Thread->OpaquePass->ceilingclip, sizeof(short) * MAXWIDTH); + curr->ffloor = fakeFloor; + assert(fakeFloor->floorclip == nullptr); + assert(fakeFloor->ceilingclip == nullptr); + fakeFloor->floorclip = curr->floorclip; + fakeFloor->ceilingclip = curr->ceilingclip; + if (clip_top) + { + clip_cur->next = curr; + clip_cur = curr; + } + else + { + clip_top = clip_cur = curr; + } + } + + void Clip3DFloors::ResetClip() + { + clip_cur = clip_top; + while (clip_cur) + { + assert(clip_cur->ffloor->floorclip != nullptr); + assert(clip_cur->ffloor->ceilingclip != nullptr); + clip_cur->ffloor->ceilingclip = clip_cur->ffloor->floorclip = nullptr; + clip_top = clip_cur; + clip_cur = clip_cur->next; + M_Free(clip_top); + } + clip_cur = clip_top = nullptr; + } + + void Clip3DFloors::EnterSkybox() + { + HeightStack current; + + current.height_top = height_top; + current.height_cur = height_cur; + current.height_max = height_max; + + toplist.Push(current); + + height_top = nullptr; + height_cur = nullptr; + height_max = -1; + + CurrentSkybox++; + } + + void Clip3DFloors::LeaveSkybox() + { + HeightStack current; + + current.height_top = nullptr; + current.height_cur = nullptr; + current.height_max = -1; + + toplist.Pop(current); + + height_top = current.height_top; + height_cur = current.height_cur; + height_max = current.height_max; + + CurrentSkybox--; + } +} diff --git a/src/swrenderer/scene/r_3dfloors.h b/src/swrenderer/scene/r_3dfloors.h new file mode 100644 index 0000000000..d697755b23 --- /dev/null +++ b/src/swrenderer/scene/r_3dfloors.h @@ -0,0 +1,104 @@ + +#pragma once + +#include "p_3dfloors.h" +#include + +EXTERN_CVAR(Int, r_3dfloors); + +namespace swrenderer +{ + class RenderThread; + class FakeFloorClip; + + struct HeightLevel + { + double height; + struct HeightLevel *prev; + struct HeightLevel *next; + }; + + struct HeightStack + { + HeightLevel *height_top; + HeightLevel *height_cur; + int height_max; + }; + + struct ClipStack + { + short floorclip[MAXWIDTH]; + short ceilingclip[MAXWIDTH]; + FakeFloorClip *ffloor; + ClipStack *next; + }; + + enum Fake3DOpaque + { + // BSP stage: + FAKE3D_FAKEFLOOR = 1, // fake floor, mark seg as FAKE + FAKE3D_FAKECEILING = 2, // fake ceiling, mark seg as FAKE + FAKE3D_FAKEBACK = 4, // RenderLine with fake backsector, mark seg as FAKE + FAKE3D_FAKEMASK = 7, + FAKE3D_CLIPBOTFRONT = 8, // use front sector clipping info (bottom) + FAKE3D_CLIPTOPFRONT = 16, // use front sector clipping info (top) + }; + + enum Fake3DTranslucent + { + // sorting stage: + FAKE3D_CLIPBOTTOM = 1, // clip bottom + FAKE3D_CLIPTOP = 2, // clip top + FAKE3D_REFRESHCLIP = 4, // refresh clip info + FAKE3D_DOWN2UP = 8, // rendering from down to up (floors) + }; + + class FakeFloorClip + { + public: + FakeFloorClip(F3DFloor *fakeFloor) : fakeFloor(fakeFloor) { } + + F3DFloor *fakeFloor = nullptr; + short *floorclip = nullptr; + short *ceilingclip = nullptr; + int validcount = -1; + }; + + class Clip3DFloors + { + public: + Clip3DFloors(RenderThread *thread); + + void Cleanup(); + + void DeleteHeights(); + void AddHeight(secplane_t *add, sector_t *sec); + void NewClip(); + void ResetClip(); + void EnterSkybox(); + void LeaveSkybox(); + void SetFakeFloor(F3DFloor *fakeFloor); + void ClearFakeFloors() { FakeFloors.clear(); } + + RenderThread *Thread = nullptr; + + int fake3D = 0; + + FakeFloorClip *fakeFloor = nullptr; + fixed_t fakeAlpha = 0; + bool fakeActive = false; + double sclipBottom = 0; + double sclipTop = 0; + HeightLevel *height_top = nullptr; + HeightLevel *height_cur = nullptr; + int CurrentSkybox = 0; + + private: + int height_max = -1; + TArray toplist; + ClipStack *clip_top = nullptr; + ClipStack *clip_cur = nullptr; + + std::unordered_map FakeFloors; + }; +} diff --git a/src/swrenderer/scene/r_light.cpp b/src/swrenderer/scene/r_light.cpp new file mode 100644 index 0000000000..d0f76ee9f3 --- /dev/null +++ b/src/swrenderer/scene/r_light.cpp @@ -0,0 +1,231 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "r_utility.h" +#include "d_player.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "gl/data/gl_data.h" + +CVAR(Bool, r_shadercolormaps, true, CVAR_ARCHIVE) +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) + +namespace swrenderer +{ + CameraLight *CameraLight::Instance() + { + static CameraLight instance; + return &instance; + } + + void CameraLight::SetCamera(RenderViewport *viewport, AActor *actor) + { + AActor *camera = viewport->viewpoint.camera; + player_t *player = actor->player; + if (camera && camera->player != nullptr) + player = camera->player; + + realfixedcolormap = nullptr; + fixedcolormap = nullptr; + fixedlightlev = -1; + + if (player != nullptr && camera == player->mo) + { + if (player->fixedcolormap >= 0 && player->fixedcolormap < (int)SpecialColormaps.Size()) + { + realfixedcolormap = &SpecialColormaps[player->fixedcolormap]; + if (viewport->RenderTarget == screen && (viewport->RenderTarget->IsBgra() || ((DFrameBuffer *)screen->Accel2D && r_shadercolormaps))) + { + // Render everything fullbright. The copy to video memory will + // apply the special colormap, so it won't be restricted to the + // palette. + fixedcolormap = &realcolormaps; + } + else + { + fixedcolormap = &SpecialColormaps[player->fixedcolormap]; + } + } + else if (player->fixedlightlevel >= 0 && player->fixedlightlevel < NUMCOLORMAPS) + { + fixedlightlev = player->fixedlightlevel * 256; + // [SP] Emulate GZDoom's light-amp goggles. + if (r_fullbrightignoresectorcolor && fixedlightlev >= 0) + { + fixedcolormap = &FullNormalLight; + } + } + } + // [RH] Inverse light for shooting the Sigil + if (fixedcolormap == nullptr && viewport->viewpoint.extralight == INT_MIN) + { + fixedcolormap = &SpecialColormaps[INVERSECOLORMAP]; + viewport->viewpoint.extralight = 0; + } + } + + ///////////////////////////////////////////////////////////////////////// + + LightVisibility *LightVisibility::Instance() + { + static LightVisibility instance; + return &instance; + } + + // Changes how rapidly things get dark with distance + void LightVisibility::SetVisibility(RenderViewport *viewport, double vis) + { + // Allow negative visibilities, just for novelty's sake + vis = clamp(vis, -204.7, 204.7); // (205 and larger do not work in 5:4 aspect ratio) + + CurrentVisibility = vis; + + if (viewport->viewwindow.FocalTangent == 0 || viewport->FocalLengthY == 0) + { // If r_visibility is called before the renderer is all set up, don't + // divide by zero. This will be called again later, and the proper + // values can be initialized then. + return; + } + + BaseVisibility = vis; + + MaxVisForWall = (viewport->InvZtoScale * (SCREENWIDTH*r_Yaspect) / (viewwidth*SCREENHEIGHT * viewport->viewwindow.FocalTangent)); + MaxVisForWall = 32767.0 / MaxVisForWall; + MaxVisForFloor = 32767.0 / (viewheight >> 2) * viewport->FocalLengthY / 160; + + // Prevent overflow on walls + if (BaseVisibility < 0 && BaseVisibility < -MaxVisForWall) + WallVisibility = -MaxVisForWall; + else if (BaseVisibility > 0 && BaseVisibility > MaxVisForWall) + WallVisibility = MaxVisForWall; + else + WallVisibility = BaseVisibility; + + WallVisibility = (viewport->InvZtoScale * SCREENWIDTH*AspectBaseHeight(viewport->viewwindow.WidescreenRatio) / + (viewwidth*SCREENHEIGHT * 3)) * (WallVisibility * viewport->viewwindow.FocalTangent); + + // Prevent overflow on floors/ceilings. Note that the calculation of + // MaxVisForFloor means that planes less than two units from the player's + // view could still overflow, but there is no way to totally eliminate + // that while still using fixed point math. + if (BaseVisibility < 0 && BaseVisibility < -MaxVisForFloor) + FloorVisibility = -MaxVisForFloor; + else if (BaseVisibility > 0 && BaseVisibility > MaxVisForFloor) + FloorVisibility = MaxVisForFloor; + else + FloorVisibility = BaseVisibility; + + FloorVisibility = 160.0 * FloorVisibility / viewport->FocalLengthY; + + TiltVisibility = float(vis * viewport->viewwindow.FocalTangent * (16.f * 320.f) / viewwidth); + + NoLightFade = glset.nolightfade; + } + + fixed_t LightVisibility::LightLevelToShade(int lightlevel, bool foggy) + { + bool nolightfade = !foggy && (glset.nolightfade); + if (nolightfade) + { + return (MAX(255 - lightlevel, 0) * NUMCOLORMAPS) << (FRACBITS - 8); + } + else + { + // Convert a light level into an unbounded colormap index (shade). Result is + // fixed point. Why the +12? I wish I knew, but experimentation indicates it + // is necessary in order to best reproduce Doom's original lighting. + return (NUMCOLORMAPS * 2 * FRACUNIT) - ((lightlevel + 12) * (FRACUNIT*NUMCOLORMAPS / 128)); + } + } + + // Controls how quickly light ramps across a 1/z range. Set this, and it + // sets all the r_*Visibility variables (except r_SkyVisibilily, which is + // currently unused). + CCMD(r_visibility) + { + if (argv.argc() < 2) + { + Printf("Visibility is %g\n", Renderer->GetVisibility()); + } + else if (!netgame) + { + Renderer->SetVisibility(atof(argv[1])); + } + else + { + Printf("Visibility cannot be changed in net games.\n"); + } + } + + ///////////////////////////////////////////////////////////////////////// + + void ColormapLight::SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack) + { + if (fadeToBlack) + { + if (invertColormap) // Fade to white + { + basecolormap = GetSpecialLights(basecolormap->Color, MAKERGB(255, 255, 255), basecolormap->Desaturate); + invertColormap = false; + } + else // Fade to black + { + basecolormap = GetSpecialLights(basecolormap->Color, MAKERGB(0, 0, 0), basecolormap->Desaturate); + } + } + + if (invertColormap) + { + basecolormap = GetSpecialLights(basecolormap->Color, basecolormap->Fade.InverseColor(), basecolormap->Desaturate); + } + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap()) + { + BaseColormap = cameraLight->FixedColormap(); + ColormapNum = 0; + } + else if (cameraLight->FixedLightLevel() >= 0) + { + BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap; + ColormapNum = cameraLight->FixedLightLevel() >> COLORMAPSHIFT; + } + else if (fullbright) + { + BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap; + ColormapNum = 0; + } + else + { + BaseColormap = basecolormap; + ColormapNum = GETPALOOKUP(visibility, shade); + } + } +} diff --git a/src/swrenderer/scene/r_light.h b/src/swrenderer/scene/r_light.h new file mode 100644 index 0000000000..be697cc95e --- /dev/null +++ b/src/swrenderer/scene/r_light.h @@ -0,0 +1,113 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include "r_defs.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_utility.h" +#include "swrenderer/viewport/r_viewport.h" + +// Lighting. +// +// [RH] This has changed significantly from Doom, which used lookup +// tables based on 1/z for walls and z for flats and only recognized +// 16 discrete light levels. The terminology I use is borrowed from Build. + +// The size of a single colormap, in bits +#define COLORMAPSHIFT 8 + +// MAXLIGHTSCALE from original DOOM, divided by 2. +#define MAXLIGHTVIS (24.0) + +// Convert a shade and visibility to a clamped colormap index. +// Result is not fixed point. +// Change R_CalcTiltedLighting() when this changes. +#define GETPALOOKUP(vis,shade) (clamp (((shade)-FLOAT2FIXED(MIN(MAXLIGHTVIS,double(vis))))>>FRACBITS, 0, NUMCOLORMAPS-1)) + +// Calculate the light multiplier for dc_light/ds_light +// This is used instead of GETPALOOKUP when ds_colormap/dc_colormap is set to the base colormap +// Returns a value between 0 and 1 in fixed point +#define LIGHTSCALE(vis,shade) FLOAT2FIXED(clamp((FIXED2DBL(shade) - (MIN(MAXLIGHTVIS,double(vis)))) / NUMCOLORMAPS, 0.0, (NUMCOLORMAPS-1)/(double)NUMCOLORMAPS)) + +struct FSWColormap; + +namespace swrenderer +{ + class CameraLight + { + public: + static CameraLight *Instance(); + + int FixedLightLevel() const { return fixedlightlev; } + FSWColormap *FixedColormap() const { return fixedcolormap; } + FSpecialColormap *ShaderColormap() const { return realfixedcolormap; } + + fixed_t FixedLightLevelShade() const { return (FixedLightLevel() >> COLORMAPSHIFT) << FRACBITS; } + + void SetCamera(RenderViewport *viewport, AActor *actor); + void ClearShaderColormap() { realfixedcolormap = nullptr; } + + private: + int fixedlightlev = 0; + FSWColormap *fixedcolormap = nullptr; + FSpecialColormap *realfixedcolormap = nullptr; + }; + + class LightVisibility + { + public: + static LightVisibility *Instance(); + + void SetVisibility(RenderViewport *viewport, double visibility); + double GetVisibility() const { return CurrentVisibility; } + + double WallGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : WallVisibility; } + double SpriteGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : WallVisibility; } + double ParticleGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : (WallVisibility * 0.5); } + double FlatPlaneGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : FloorVisibility; } + double SlopePlaneGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : TiltVisibility; } + + // The vis value to pass into the GETPALOOKUP or LIGHTSCALE macros + double WallVis(double screenZ, bool foggy) const { return WallGlobVis(foggy) / screenZ; } + double SpriteVis(double screenZ, bool foggy) const { return SpriteGlobVis(foggy) / screenZ; } + double ParticleVis(double screenZ, bool foggy) const { return ParticleGlobVis(foggy) / screenZ; } + double FlatPlaneVis(int screenY, double planeZ, bool foggy, RenderViewport *viewport) const { return FlatPlaneGlobVis(foggy) / fabs(planeZ - viewport->viewpoint.Pos.Z) * fabs(viewport->CenterY - screenY); } + + static fixed_t LightLevelToShade(int lightlevel, bool foggy); + static int ActualExtraLight(bool fog, RenderViewport *viewport) { return fog ? 0 : viewport->viewpoint.extralight << 4; } + + private: + double BaseVisibility = 0.0; + double WallVisibility = 0.0; + double FloorVisibility = 0.0; + float TiltVisibility = 0.0f; + + bool NoLightFade = false; + + double CurrentVisibility = 8.f; + double MaxVisForWall = 0.0; + double MaxVisForFloor = 0.0; + }; + + class ColormapLight + { + public: + int ColormapNum = 0; + FSWColormap *BaseColormap = nullptr; + + void SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack); + }; +} diff --git a/src/swrenderer/scene/r_opaque_pass.cpp b/src/swrenderer/scene/r_opaque_pass.cpp new file mode 100644 index 0000000000..0fefb42a1f --- /dev/null +++ b/src/swrenderer/scene/r_opaque_pass.cpp @@ -0,0 +1,1050 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// BSP traversal, handling of LineSegs for rendering. +// +//----------------------------------------------------------------------------- + + +#include + +#include "templates.h" + +#include "doomdef.h" + +#include "m_bbox.h" + +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" + +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/things/r_wallsprite.h" +#include "swrenderer/things/r_voxel.h" +#include "swrenderer/things/r_particle.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_renderthread.h" +#include "r_3dfloors.h" +#include "r_portal.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "c_console.h" +#include "p_maputl.h" + +// State. +#include "doomstat.h" +#include "r_state.h" +#include "r_opaque_pass.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "g_levellocals.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); +EXTERN_CVAR(Bool, r_drawvoxels); + +namespace swrenderer +{ + RenderOpaquePass::RenderOpaquePass(RenderThread *thread) : renderline(thread) + { + Thread = thread; + } + + sector_t *RenderOpaquePass::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, seg_t *backline, int backx1, int backx2, double frontcz1, double frontcz2) + { + // If player's view height is underneath fake floor, lower the + // drawn ceiling to be just under the floor height, and replace + // the drawn floor and ceiling textures, and light level, with + // the control sector's. + // + // Similar for ceiling, only reflected. + + // [RH] allow per-plane lighting + if (floorlightlevel != nullptr) + { + *floorlightlevel = sec->GetFloorLight(); + } + + if (ceilinglightlevel != nullptr) + { + *ceilinglightlevel = sec->GetCeilingLight(); + } + + FakeSide = WaterFakeSide::Center; + + const sector_t *s = sec->GetHeightSec(); + if (s != nullptr) + { + sector_t *heightsec = Thread->Viewport->viewpoint.sector->heightsec; + bool underwater = r_fakingunderwater || + (heightsec && heightsec->floorplane.PointOnSide(Thread->Viewport->viewpoint.Pos) <= 0); + bool doorunderwater = false; + int diffTex = (s->MoreFlags & SECF_CLIPFAKEPLANES); + + // Replace sector being drawn with a copy to be hacked + *tempsec = *sec; + + // Replace floor and ceiling height with control sector's heights. + if (diffTex) + { + if (s->floorplane.CopyPlaneIfValid(&tempsec->floorplane, &sec->ceilingplane)) + { + tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); + } + else if (s->MoreFlags & SECF_FAKEFLOORONLY) + { + if (underwater) + { + tempsec->ColorMap = s->ColorMap; + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel != nullptr) + { + *floorlightlevel = s->GetFloorLight(); + } + + if (ceilinglightlevel != nullptr) + { + *ceilinglightlevel = s->GetCeilingLight(); + } + } + FakeSide = WaterFakeSide::BelowFloor; + return tempsec; + } + return sec; + } + } + else + { + tempsec->floorplane = s->floorplane; + } + + if (!(s->MoreFlags & SECF_FAKEFLOORONLY)) + { + if (diffTex) + { + if (s->ceilingplane.CopyPlaneIfValid(&tempsec->ceilingplane, &sec->floorplane)) + { + tempsec->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false); + } + } + else + { + tempsec->ceilingplane = s->ceilingplane; + } + } + + double refceilz = s->ceilingplane.ZatPoint(Thread->Viewport->viewpoint.Pos); + double orgceilz = sec->ceilingplane.ZatPoint(Thread->Viewport->viewpoint.Pos); + +#if 1 + // [RH] Allow viewing underwater areas through doors/windows that + // are underwater but not in a water sector themselves. + // Only works if you cannot see the top surface of any deep water + // sectors at the same time. + if (backline && !r_fakingunderwater && backline->frontsector->heightsec == nullptr) + { + if (frontcz1 <= s->floorplane.ZatPoint(backline->v1) && + frontcz2 <= s->floorplane.ZatPoint(backline->v2)) + { + // Check that the window is actually visible + for (int z = backx1; z < backx2; ++z) + { + if (floorclip[z] > ceilingclip[z]) + { + doorunderwater = true; + r_fakingunderwater = true; + break; + } + } + } + } +#endif + + if (underwater || doorunderwater) + { + tempsec->floorplane = sec->floorplane; + tempsec->ceilingplane = s->floorplane; + tempsec->ceilingplane.FlipVert(); + tempsec->ceilingplane.ChangeHeight(-1 / 65536.); + tempsec->ColorMap = s->ColorMap; + } + + // killough 11/98: prevent sudden light changes from non-water sectors: + if ((underwater && !backline) || doorunderwater) + { // head-below-floor hack + tempsec->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false); + tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; + + tempsec->ceilingplane = s->floorplane; + tempsec->ceilingplane.FlipVert(); + tempsec->ceilingplane.ChangeHeight(-1 / 65536.); + if (s->GetTexture(sector_t::ceiling) == skyflatnum) + { + tempsec->floorplane = tempsec->ceilingplane; + tempsec->floorplane.FlipVert(); + tempsec->floorplane.ChangeHeight(+1 / 65536.); + tempsec->SetTexture(sector_t::ceiling, tempsec->GetTexture(sector_t::floor), false); + tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform; + } + else + { + tempsec->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false); + tempsec->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform; + } + + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel != nullptr) + { + *floorlightlevel = s->GetFloorLight(); + } + + if (ceilinglightlevel != nullptr) + { + *ceilinglightlevel = s->GetCeilingLight(); + } + } + FakeSide = WaterFakeSide::BelowFloor; + } + else if (heightsec && heightsec->ceilingplane.PointOnSide(Thread->Viewport->viewpoint.Pos) <= 0 && + orgceilz > refceilz && !(s->MoreFlags & SECF_FAKEFLOORONLY)) + { // Above-ceiling hack + tempsec->ceilingplane = s->ceilingplane; + tempsec->floorplane = s->ceilingplane; + tempsec->floorplane.FlipVert(); + tempsec->floorplane.ChangeHeight(+1 / 65536.); + tempsec->ColorMap = s->ColorMap; + tempsec->ColorMap = s->ColorMap; + + tempsec->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false); + tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false); + tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform; + + if (s->GetTexture(sector_t::floor) != skyflatnum) + { + tempsec->ceilingplane = sec->ceilingplane; + tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); + tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; + } + + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel != nullptr) + { + *floorlightlevel = s->GetFloorLight(); + } + + if (ceilinglightlevel != nullptr) + { + *ceilinglightlevel = s->GetCeilingLight(); + } + } + FakeSide = WaterFakeSide::AboveCeiling; + } + sec = tempsec; // Use other sector + } + return sec; + } + + + + // Checks BSP node/subtree bounding box. + // Returns true if some part of the bbox might be visible. + bool RenderOpaquePass::CheckBBox(float *bspcoord) + { + static const int checkcoord[12][4] = + { + { 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 } + }; + + int boxx; + int boxy; + int boxpos; + + double x1, y1, x2, y2; + double rx1, ry1, rx2, ry2; + int sx1, sx2; + + // Find the corners of the box + // that define the edges from current viewpoint. + if (Thread->Viewport->viewpoint.Pos.X <= bspcoord[BOXLEFT]) + boxx = 0; + else if (Thread->Viewport->viewpoint.Pos.X < bspcoord[BOXRIGHT]) + boxx = 1; + else + boxx = 2; + + if (Thread->Viewport->viewpoint.Pos.Y >= bspcoord[BOXTOP]) + boxy = 0; + else if (Thread->Viewport->viewpoint.Pos.Y > bspcoord[BOXBOTTOM]) + boxy = 1; + else + boxy = 2; + + boxpos = (boxy << 2) + boxx; + if (boxpos == 5) + return true; + + x1 = bspcoord[checkcoord[boxpos][0]] - Thread->Viewport->viewpoint.Pos.X; + y1 = bspcoord[checkcoord[boxpos][1]] - Thread->Viewport->viewpoint.Pos.Y; + x2 = bspcoord[checkcoord[boxpos][2]] - Thread->Viewport->viewpoint.Pos.X; + y2 = bspcoord[checkcoord[boxpos][3]] - Thread->Viewport->viewpoint.Pos.Y; + + // check clip list for an open space + + // Sitting on a line? + if (y1 * (x1 - x2) + x1 * (y2 - y1) >= -EQUAL_EPSILON) + return true; + + rx1 = x1 * Thread->Viewport->viewpoint.Sin - y1 * Thread->Viewport->viewpoint.Cos; + rx2 = x2 * Thread->Viewport->viewpoint.Sin - y2 * Thread->Viewport->viewpoint.Cos; + ry1 = x1 * Thread->Viewport->viewpoint.TanCos + y1 * Thread->Viewport->viewpoint.TanSin; + ry2 = x2 * Thread->Viewport->viewpoint.TanCos + y2 * Thread->Viewport->viewpoint.TanSin; + + if (Thread->Portal->MirrorFlags & RF_XFLIP) + { + double t = -rx1; + rx1 = -rx2; + rx2 = t; + swapvalues(ry1, ry2); + } + + auto viewport = Thread->Viewport.get(); + + if (rx1 >= -ry1) + { + if (rx1 > ry1) return false; // left edge is off the right side + if (ry1 == 0) return false; + sx1 = xs_RoundToInt(viewport->CenterX + rx1 * viewport->CenterX / ry1); + } + else + { + if (rx2 < -ry2) return false; // wall is off the left side + if (rx1 - rx2 - ry2 + ry1 == 0) return false; // wall does not intersect view volume + sx1 = 0; + } + + if (rx2 <= ry2) + { + if (rx2 < -ry2) return false; // right edge is off the left side + if (ry2 == 0) return false; + sx2 = xs_RoundToInt(viewport->CenterX + rx2 * viewport->CenterX / ry2); + } + else + { + if (rx1 > ry1) return false; // wall is off the right side + if (ry2 - ry1 - rx2 + rx1 == 0) return false; // wall does not intersect view volume + sx2 = viewwidth; + } + + // Find the first clippost that touches the source post + // (adjacent pixels are touching). + + return Thread->ClipSegments->IsVisible(sx1, sx2); + } + + void RenderOpaquePass::AddPolyobjs(subsector_t *sub) + { + if (sub->BSP == nullptr || sub->BSP->bDirty) + { + sub->BuildPolyBSP(); + } + if (sub->BSP->Nodes.Size() == 0) + { + RenderSubsector(&sub->BSP->Subsectors[0]); + } + else + { + RenderBSPNode(&sub->BSP->Nodes.Last()); + } + } + + // kg3D - add fake segs, never rendered + void RenderOpaquePass::FakeDrawLoop(subsector_t *sub, VisiblePlane *floorplane, VisiblePlane *ceilingplane, bool foggy, FDynamicColormap *basecolormap) + { + int count; + seg_t* line; + + count = sub->numlines; + line = sub->firstline; + + while (count--) + { + if ((line->sidedef) && !(line->sidedef->Flags & WALLF_POLYOBJ)) + { + renderline.Render(line, InSubsector, frontsector, nullptr, floorplane, ceilingplane, foggy, basecolormap); + } + line++; + } + } + + void RenderOpaquePass::RenderSubsector(subsector_t *sub) + { + // Determine floor/ceiling planes. + // Add sprites of things in sector. + // Draw one or more line segments. + + int count; + seg_t* line; + sector_t tempsec; // killough 3/7/98: deep water hack + int floorlightlevel; // killough 3/16/98: set floor lightlevel + int ceilinglightlevel; // killough 4/11/98 + bool outersubsector; + int fll, cll, position; + FSectorPortal *portal; + + // kg3D - fake floor stuff + VisiblePlane *backupfp; + VisiblePlane *backupcp; + //secplane_t templane; + lightlist_t *light; + + if (InSubsector != nullptr) + { // InSubsector is not nullptr. This means we are rendering from a mini-BSP. + outersubsector = false; + } + else + { + outersubsector = true; + InSubsector = sub; + } + +#ifdef RANGECHECK + if (outersubsector && sub - subsectors >= (ptrdiff_t)numsubsectors) + I_Error("RenderSubsector: ss %ti with numss = %i", sub - subsectors, numsubsectors); +#endif + + assert(sub->sector != nullptr); + + if (sub->polys) + { // Render the polyobjs in the subsector first + AddPolyobjs(sub); + if (outersubsector) + { + InSubsector = nullptr; + } + return; + } + + frontsector = sub->sector; + frontsector->MoreFlags |= SECF_DRAWN; + count = sub->numlines; + line = sub->firstline; + + // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect + frontsector = FakeFlat(frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, nullptr, 0, 0, 0, 0); + + fll = floorlightlevel; + cll = ceilinglightlevel; + + // [RH] set foggy flag + bool foggy = level.fadeto || frontsector->ColorMap->Fade || (level.flags & LEVEL_HASFADETABLE); + + // kg3D - fake lights + CameraLight *cameraLight = CameraLight::Instance(); + FDynamicColormap *basecolormap; + if (cameraLight->FixedLightLevel() < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(frontsector, &frontsector->ceilingplane, false); + basecolormap = light->extra_colormap; + // If this is the real ceiling, don't discard plane lighting R_FakeFlat() + // accounted for. + if (light->p_lightlevel != &frontsector->lightlevel) + { + ceilinglightlevel = *light->p_lightlevel; + } + } + else + { + basecolormap = (r_fullbrightignoresectorcolor && cameraLight->FixedLightLevel() >= 0) ? &FullNormalLight : frontsector->ColorMap; + } + + portal = frontsector->ValidatePortal(sector_t::ceiling); + + VisiblePlane *ceilingplane = frontsector->ceilingplane.PointOnSide(Thread->Viewport->viewpoint.Pos) > 0 || + frontsector->GetTexture(sector_t::ceiling) == skyflatnum || + portal != nullptr || + (frontsector->heightsec && + !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && + frontsector->heightsec->GetTexture(sector_t::floor) == skyflatnum) ? + Thread->PlaneList->FindPlane(frontsector->ceilingplane, // killough 3/8/98 + frontsector->GetTexture(sector_t::ceiling), + ceilinglightlevel + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), // killough 4/11/98 + frontsector->GetAlpha(sector_t::ceiling), + !!(frontsector->GetFlags(sector_t::ceiling) & PLANEF_ADDITIVE), + frontsector->planes[sector_t::ceiling].xform, + frontsector->sky, + portal, + basecolormap + ) : nullptr; + + if (ceilingplane) + ceilingplane->AddLights(Thread, frontsector->lighthead); + + if (cameraLight->FixedLightLevel() < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(frontsector, &frontsector->floorplane, false); + basecolormap = light->extra_colormap; + // If this is the real floor, don't discard plane lighting R_FakeFlat() + // accounted for. + if (light->p_lightlevel != &frontsector->lightlevel) + { + floorlightlevel = *light->p_lightlevel; + } + } + else + { + basecolormap = (r_fullbrightignoresectorcolor && cameraLight->FixedLightLevel() >= 0) ? &FullNormalLight : frontsector->ColorMap; + } + + // killough 3/7/98: Add (x,y) offsets to flats, add deep water check + // killough 3/16/98: add floorlightlevel + // killough 10/98: add support for skies transferred from sidedefs + portal = frontsector->ValidatePortal(sector_t::floor); + + VisiblePlane *floorplane = frontsector->floorplane.PointOnSide(Thread->Viewport->viewpoint.Pos) > 0 || // killough 3/7/98 + frontsector->GetTexture(sector_t::floor) == skyflatnum || + portal != nullptr || + (frontsector->heightsec && + !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && + frontsector->heightsec->GetTexture(sector_t::ceiling) == skyflatnum) ? + Thread->PlaneList->FindPlane(frontsector->floorplane, + frontsector->GetTexture(sector_t::floor), + floorlightlevel + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), // killough 3/16/98 + frontsector->GetAlpha(sector_t::floor), + !!(frontsector->GetFlags(sector_t::floor) & PLANEF_ADDITIVE), + frontsector->planes[sector_t::floor].xform, + frontsector->sky, + portal, + basecolormap + ) : nullptr; + + if (floorplane) + floorplane->AddLights(Thread, frontsector->lighthead); + + // kg3D - fake planes rendering + if (r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size()) + { + backupfp = floorplane; + backupcp = ceilingplane; + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + // first check all floors + for (int i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++) + { + clip3d->SetFakeFloor(frontsector->e->XFloor.ffloors[i]); + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_EXISTS)) continue; + if (!clip3d->fakeFloor->fakeFloor->model) continue; + if (clip3d->fakeFloor->fakeFloor->bottom.plane->isSlope()) continue; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_NOSHADE) || (clip3d->fakeFloor->fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) + { + clip3d->AddHeight(clip3d->fakeFloor->fakeFloor->top.plane, frontsector); + } + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_RENDERPLANES)) continue; + if (clip3d->fakeFloor->fakeFloor->alpha == 0) continue; + if (clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE && clip3d->fakeFloor->fakeFloor->flags & FF_INVERTSECTOR) continue; + clip3d->fakeAlpha = MIN(Scale(clip3d->fakeFloor->fakeFloor->alpha, OPAQUE, 255), OPAQUE); + if (clip3d->fakeFloor->validcount != validcount) + { + clip3d->fakeFloor->validcount = validcount; + clip3d->NewClip(); + } + double fakeHeight = clip3d->fakeFloor->fakeFloor->top.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight < Thread->Viewport->viewpoint.Pos.Z && + fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)) + { + clip3d->fake3D = FAKE3D_FAKEFLOOR; + tempsec = *clip3d->fakeFloor->fakeFloor->model; + tempsec.floorplane = *clip3d->fakeFloor->fakeFloor->top.plane; + tempsec.ceilingplane = *clip3d->fakeFloor->fakeFloor->bottom.plane; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE) && !(clip3d->fakeFloor->fakeFloor->flags & FF_INVERTSECTOR)) + { + tempsec.SetTexture(sector_t::floor, tempsec.GetTexture(sector_t::ceiling)); + position = sector_t::ceiling; + } + else position = sector_t::floor; + frontsector = &tempsec; + + if (cameraLight->FixedLightLevel() < 0 && sub->sector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(sub->sector, &frontsector->floorplane, false); + basecolormap = light->extra_colormap; + floorlightlevel = *light->p_lightlevel; + } + + ceilingplane = nullptr; + floorplane = Thread->PlaneList->FindPlane(frontsector->floorplane, + frontsector->GetTexture(sector_t::floor), + floorlightlevel + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), // killough 3/16/98 + frontsector->GetAlpha(sector_t::floor), + !!(clip3d->fakeFloor->fakeFloor->flags & FF_ADDITIVETRANS), + frontsector->planes[position].xform, + frontsector->sky, + nullptr, + basecolormap); + + if (floorplane) + floorplane->AddLights(Thread, frontsector->lighthead); + + FakeDrawLoop(sub, floorplane, ceilingplane, foggy, basecolormap); + clip3d->fake3D = 0; + frontsector = sub->sector; + } + } + // and now ceilings + for (unsigned int i = 0; i < frontsector->e->XFloor.ffloors.Size(); i++) + { + clip3d->SetFakeFloor(frontsector->e->XFloor.ffloors[i]); + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_EXISTS)) continue; + if (!clip3d->fakeFloor->fakeFloor->model) continue; + if (clip3d->fakeFloor->fakeFloor->top.plane->isSlope()) continue; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_NOSHADE) || (clip3d->fakeFloor->fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) + { + clip3d->AddHeight(clip3d->fakeFloor->fakeFloor->bottom.plane, frontsector); + } + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_RENDERPLANES)) continue; + if (clip3d->fakeFloor->fakeFloor->alpha == 0) continue; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE) && (clip3d->fakeFloor->fakeFloor->flags & (FF_SWIMMABLE | FF_INVERTSECTOR)) == (FF_SWIMMABLE | FF_INVERTSECTOR)) continue; + clip3d->fakeAlpha = MIN(Scale(clip3d->fakeFloor->fakeFloor->alpha, OPAQUE, 255), OPAQUE); + + if (clip3d->fakeFloor->validcount != validcount) + { + clip3d->fakeFloor->validcount = validcount; + clip3d->NewClip(); + } + double fakeHeight = clip3d->fakeFloor->fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight > Thread->Viewport->viewpoint.Pos.Z && + fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)) + { + clip3d->fake3D = FAKE3D_FAKECEILING; + tempsec = *clip3d->fakeFloor->fakeFloor->model; + tempsec.floorplane = *clip3d->fakeFloor->fakeFloor->top.plane; + tempsec.ceilingplane = *clip3d->fakeFloor->fakeFloor->bottom.plane; + if ((!(clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE) && !(clip3d->fakeFloor->fakeFloor->flags & FF_INVERTSECTOR)) || + (clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE && clip3d->fakeFloor->fakeFloor->flags & FF_INVERTSECTOR)) + { + tempsec.SetTexture(sector_t::ceiling, tempsec.GetTexture(sector_t::floor)); + position = sector_t::floor; + } + else position = sector_t::ceiling; + frontsector = &tempsec; + + tempsec.ceilingplane.ChangeHeight(-1 / 65536.); + if (cameraLight->FixedLightLevel() < 0 && sub->sector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(sub->sector, &frontsector->ceilingplane, false); + basecolormap = light->extra_colormap; + ceilinglightlevel = *light->p_lightlevel; + } + tempsec.ceilingplane.ChangeHeight(1 / 65536.); + + floorplane = nullptr; + ceilingplane = Thread->PlaneList->FindPlane(frontsector->ceilingplane, // killough 3/8/98 + frontsector->GetTexture(sector_t::ceiling), + ceilinglightlevel + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), // killough 4/11/98 + frontsector->GetAlpha(sector_t::ceiling), + !!(clip3d->fakeFloor->fakeFloor->flags & FF_ADDITIVETRANS), + frontsector->planes[position].xform, + frontsector->sky, + nullptr, + basecolormap); + + if (ceilingplane) + ceilingplane->AddLights(Thread, frontsector->lighthead); + + FakeDrawLoop(sub, floorplane, ceilingplane, foggy, basecolormap); + clip3d->fake3D = 0; + frontsector = sub->sector; + } + } + clip3d->fakeFloor = nullptr; + floorplane = backupfp; + ceilingplane = backupcp; + } + + basecolormap = frontsector->ColorMap; + floorlightlevel = fll; + ceilinglightlevel = cll; + + // killough 9/18/98: Fix underwater slowdown, by passing real sector + // instead of fake one. Improve sprite lighting by basing sprite + // lightlevels on floor & ceiling lightlevels in the surrounding area. + // [RH] Handle sprite lighting like Duke 3D: If the ceiling is a sky, sprites are lit by + // it, otherwise they are lit by the floor. + AddSprites(sub->sector, frontsector->GetTexture(sector_t::ceiling) == skyflatnum ? ceilinglightlevel : floorlightlevel, FakeSide, foggy, basecolormap); + + // [RH] Add particles + if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors) + { // Only do it for the main BSP. + int shade = LightVisibility::LightLevelToShade((floorlightlevel + ceilinglightlevel) / 2 + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), foggy); + for (int i = ParticlesInSubsec[(unsigned int)(sub - subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) + { + RenderParticle::Project(Thread, Particles + i, subsectors[sub - subsectors].sector, shade, FakeSide, foggy); + } + } + + count = sub->numlines; + line = sub->firstline; + + while (count--) + { + if (!outersubsector || line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) + { + // kg3D - fake planes bounding calculation + if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) + { + backupfp = floorplane; + backupcp = ceilingplane; + floorplane = nullptr; + ceilingplane = nullptr; + Clip3DFloors *clip3d = Thread->Clip3D.get(); + for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++) + { + clip3d->SetFakeFloor(line->backsector->e->XFloor.ffloors[i]); + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_EXISTS)) continue; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_RENDERPLANES)) continue; + if (!clip3d->fakeFloor->fakeFloor->model) continue; + clip3d->fake3D = FAKE3D_FAKEBACK; + tempsec = *clip3d->fakeFloor->fakeFloor->model; + tempsec.floorplane = *clip3d->fakeFloor->fakeFloor->top.plane; + tempsec.ceilingplane = *clip3d->fakeFloor->fakeFloor->bottom.plane; + if (clip3d->fakeFloor->validcount != validcount) + { + clip3d->fakeFloor->validcount = validcount; + clip3d->NewClip(); + } + renderline.Render(line, InSubsector, frontsector, &tempsec, floorplane, ceilingplane, foggy, basecolormap); // fake + } + clip3d->fakeFloor = nullptr; + clip3d->fake3D = 0; + floorplane = backupfp; + ceilingplane = backupcp; + } + renderline.Render(line, InSubsector, frontsector, nullptr, floorplane, ceilingplane, foggy, basecolormap); // now real + } + line++; + } + if (outersubsector) + { + InSubsector = nullptr; + } + } + + void RenderOpaquePass::RenderScene() + { + SeenSpriteSectors.clear(); + SeenActors.clear(); + + InSubsector = nullptr; + RenderBSPNode(nodes + numnodes - 1); // The head node is the last node output. + } + + // + // RenderBSPNode + // Renders all subsectors below a given node, traversing subtree recursively. + // Just call with BSP root and -1. + // killough 5/2/98: reformatted, removed tail recursion + + void RenderOpaquePass::RenderBSPNode(void *node) + { + if (numnodes == 0) + { + RenderSubsector(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(Thread->Viewport->viewpoint.Pos, bsp); + + // Recursively divide front space (toward the viewer). + RenderBSPNode(bsp->children[side]); + + // Possibly divide back space (away from the viewer). + side ^= 1; + if (!CheckBBox(bsp->bbox[side])) + return; + + node = bsp->children[side]; + } + RenderSubsector((subsector_t *)((uint8_t *)node - 1)); + } + + void RenderOpaquePass::ClearClip() + { + // clip ceiling to console bottom + fillshort(floorclip, viewwidth, viewheight); + fillshort(ceilingclip, viewwidth, !screen->Accel2D && ConBottom > viewwindowy && !Thread->Viewport->RenderingToCanvas() ? (ConBottom - viewwindowy) : 0); + } + + void RenderOpaquePass::AddSprites(sector_t *sec, int lightlevel, WaterFakeSide fakeside, bool foggy, FDynamicColormap *basecolormap) + { + // 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 (sec->touching_renderthings == nullptr || SeenSpriteSectors.find(sec) != SeenSpriteSectors.end()/*|| sec->validcount == validcount*/) + return; + + // Well, now it will be done. + //sec->validcount = validcount; + SeenSpriteSectors.insert(sec); + + int spriteshade = LightVisibility::LightLevelToShade(lightlevel + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), foggy); + + // Handle all things in sector. + for (auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext) + { + auto thing = p->m_thing; + if (SeenActors.find(thing) != SeenActors.end()) continue; + SeenActors.insert(thing); + //if (thing->validcount == validcount) continue; + //thing->validcount = validcount; + + FIntCVar *cvar = thing->GetClass()->distancecheck; + if (cvar != nullptr && *cvar >= 0) + { + double dist = (thing->Pos() - Thread->Viewport->viewpoint.Pos).LengthSquared(); + double check = (double)**cvar; + if (dist >= check * check) + { + continue; + } + } + + // find fake level + F3DFloor *fakeceiling = nullptr; + F3DFloor *fakefloor = nullptr; + for (auto rover : thing->Sector->e->XFloor.ffloors) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES)) continue; + if (!(rover->flags & FF_SOLID) || rover->alpha != 255) continue; + if (!fakefloor) + { + if (!rover->top.plane->isSlope()) + { + if (rover->top.plane->ZatPoint(0., 0.) <= thing->Z()) fakefloor = rover; + } + } + if (!rover->bottom.plane->isSlope()) + { + if (rover->bottom.plane->ZatPoint(0., 0.) >= thing->Top()) fakeceiling = rover; + } + } + + if (IsPotentiallyVisible(thing)) + { + ThingSprite sprite; + if (GetThingSprite(thing, sprite)) + { + FDynamicColormap *thingColormap = basecolormap; + int thingShade = spriteshade; + if (sec->sectornum != thing->Sector->sectornum) // compare sectornums to account for R_FakeFlat copies. + { + int lightlevel = thing->Sector->GetTexture(sector_t::ceiling) == skyflatnum ? thing->Sector->GetCeilingLight() : thing->Sector->GetFloorLight(); + thingShade = LightVisibility::LightLevelToShade(lightlevel + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()), foggy); + thingColormap = thing->Sector->ColorMap; + } + + if ((sprite.renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) + { + RenderWallSprite::Project(Thread, thing, sprite.pos, sprite.picnum, sprite.spriteScale, sprite.renderflags, thingShade, foggy, thingColormap); + } + else if (sprite.voxel) + { + RenderVoxel::Project(Thread, thing, sprite.pos, sprite.voxel, sprite.spriteScale, sprite.renderflags, fakeside, fakefloor, fakeceiling, sec, thingShade, foggy, thingColormap); + } + else + { + RenderSprite::Project(Thread, thing, sprite.pos, sprite.tex, sprite.spriteScale, sprite.renderflags, fakeside, fakefloor, fakeceiling, sec, thingShade, foggy, thingColormap); + } + } + } + } + } + + bool RenderOpaquePass::IsPotentiallyVisible(AActor *thing) + { + // Don't waste time projecting sprites that are definitely not visible. + if (thing == nullptr || + (thing->renderflags & RF_INVISIBLE) || + !thing->RenderStyle.IsVisible(thing->Alpha) || + !thing->IsVisibleToPlayer() || + !thing->IsInsideVisibleAngles()) + { + return false; + } + + // [ZZ] Or less definitely not visible (hue) + // [ZZ] 10.01.2016: don't try to clip stuff inside a skybox against the current portal. + RenderPortal *renderportal = Thread->Portal.get(); + if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && !!P_PointOnLineSidePrecise(thing->Pos(), renderportal->CurrentPortal->dst)) + return false; + + return true; + } + + bool RenderOpaquePass::GetThingSprite(AActor *thing, ThingSprite &sprite) + { + sprite.pos = thing->InterpolatedPosition(Thread->Viewport->viewpoint.TicFrac); + sprite.pos.Z += thing->GetBobOffset(Thread->Viewport->viewpoint.TicFrac); + + sprite.spritenum = thing->sprite; + sprite.tex = nullptr; + sprite.voxel = nullptr; + sprite.spriteScale = thing->Scale; + sprite.renderflags = thing->renderflags; + + if (thing->player != nullptr) + { + P_CheckPlayerSprite(thing, sprite.spritenum, sprite.spriteScale); + } + + if (thing->picnum.isValid()) + { + sprite.picnum = thing->picnum; + + sprite.tex = TexMan(sprite.picnum); + if (sprite.tex->UseType == FTexture::TEX_Null) + { + return false; + } + + if (sprite.tex->Rotations != 0xFFFF) + { + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[sprite.tex->Rotations]; + DAngle ang = (sprite.pos - Thread->Viewport->viewpoint.Pos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + sprite.picnum = sprframe->Texture[rot]; + if (sprframe->Flip & (1 << rot)) + { + sprite.renderflags ^= RF_XFLIP; + } + sprite.tex = TexMan[sprite.picnum]; // Do not animate the rotation + } + } + else + { + // decide which texture to use for the sprite + if ((unsigned)sprite.spritenum >= sprites.Size()) + { + DPrintf(DMSG_ERROR, "R_ProjectSprite: invalid sprite number %u\n", sprite.spritenum); + return false; + } + spritedef_t *sprdef = &sprites[sprite.spritenum]; + if (thing->frame >= sprdef->numframes) + { + // If there are no frames at all for this sprite, don't draw it. + return false; + } + else + { + //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; + DAngle ang = (sprite.pos - Thread->Viewport->viewpoint.Pos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + sprite.picnum = sprframe->Texture[rot]; + if (sprframe->Flip & (1 << rot)) + { + sprite.renderflags ^= RF_XFLIP; + } + sprite.tex = TexMan[sprite.picnum]; // Do not animate the rotation + if (r_drawvoxels) + { + sprite.voxel = sprframe->Voxel; + } + } + + if (sprite.voxel == nullptr && (sprite.tex == nullptr || sprite.tex->UseType == FTexture::TEX_Null)) + { + return false; + } + + if (sprite.spriteScale.Y < 0) + { + sprite.spriteScale.Y = -sprite.spriteScale.Y; + sprite.renderflags ^= RF_YFLIP; + } + if (sprite.spriteScale.X < 0) + { + sprite.spriteScale.X = -sprite.spriteScale.X; + sprite.renderflags ^= RF_XFLIP; + } + } + + return true; + } +} diff --git a/src/swrenderer/scene/r_opaque_pass.h b/src/swrenderer/scene/r_opaque_pass.h new file mode 100644 index 0000000000..123e08e0a1 --- /dev/null +++ b/src/swrenderer/scene/r_opaque_pass.h @@ -0,0 +1,93 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "tarray.h" +#include +#include "r_defs.h" +#include "swrenderer/line/r_line.h" +#include "swrenderer/scene/r_3dfloors.h" +#include + +struct FVoxelDef; + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlane; + + // The 3072 below is just an arbitrary value picked to avoid + // drawing lines the player is too close to that would overflow + // the texture calculations. + #define TOO_CLOSE_Z (3072.0 / (1<<12)) + + enum class WaterFakeSide + { + Center, + BelowFloor, + AboveCeiling + }; + + struct ThingSprite + { + DVector3 pos; + int spritenum; + FTexture *tex; + FVoxelDef *voxel; + FTextureID picnum; + DVector2 spriteScale; + int renderflags; + }; + + class RenderOpaquePass + { + public: + RenderOpaquePass(RenderThread *thread); + + void ClearClip(); + void RenderScene(); + + void ResetFakingUnderwater() { r_fakingunderwater = false; } + sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, seg_t *backline, int backx1, int backx2, double frontcz1, double frontcz2); + + void ClearSeenSprites() { SeenSpriteSectors.clear(); SeenActors.clear(); } + + short floorclip[MAXWIDTH]; + short ceilingclip[MAXWIDTH]; + + RenderThread *Thread = nullptr; + + private: + void RenderBSPNode(void *node); + void RenderSubsector(subsector_t *sub); + + bool CheckBBox(float *bspcoord); + void AddPolyobjs(subsector_t *sub); + void FakeDrawLoop(subsector_t *sub, VisiblePlane *floorplane, VisiblePlane *ceilingplane, bool foggy, FDynamicColormap *basecolormap); + + void AddSprites(sector_t *sec, int lightlevel, WaterFakeSide fakeside, bool foggy, FDynamicColormap *basecolormap); + + bool IsPotentiallyVisible(AActor *thing); + bool GetThingSprite(AActor *thing, ThingSprite &sprite); + + subsector_t *InSubsector = nullptr; + sector_t *frontsector = nullptr; + WaterFakeSide FakeSide = WaterFakeSide::Center; + bool r_fakingunderwater = false; + + SWRenderLine renderline; + std::set SeenSpriteSectors; + std::set SeenActors; + }; +} diff --git a/src/swrenderer/scene/r_portal.cpp b/src/swrenderer/scene/r_portal.cpp new file mode 100644 index 0000000000..b34fe374ad --- /dev/null +++ b/src/swrenderer/scene/r_portal.cpp @@ -0,0 +1,546 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "doomdef.h" +#include "d_net.h" +#include "doomstat.h" +#include "m_random.h" +#include "m_bbox.h" +#include "r_portal.h" +#include "r_sky.h" +#include "st_stuff.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "v_video.h" +#include "stats.h" +#include "i_video.h" +#include "i_system.h" +#include "a_sharedglobal.h" +#include "r_data/r_translate.h" +#include "p_3dmidtex.h" +#include "r_data/r_interpolate.h" +#include "v_palette.h" +#include "po_man.h" +#include "p_effect.h" +#include "st_start.h" +#include "v_font.h" +#include "r_data/colormaps.h" +#include "p_maputl.h" +#include "p_setup.h" +#include "version.h" +#include "r_utility.h" +#include "r_3dfloors.h" +#include "g_levellocals.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +CVAR(Int, r_portal_recursions, 4, CVAR_ARCHIVE) +CVAR(Bool, r_highlight_portals, false, CVAR_ARCHIVE) +CVAR(Bool, r_skyboxes, true, 0) + +// Avoid infinite recursion with stacked sectors by limiting them. +#define MAX_SKYBOX_PLANES 1000 + +namespace swrenderer +{ + RenderPortal::RenderPortal(RenderThread *thread) + { + Thread = thread; + } + + // Draws any recorded sky boxes and then frees them. + // + // The process: + // 1. Move the camera to coincide with the SkyViewpoint. + // 2. Clear out the old planes. (They have already been drawn.) + // 3. Clear a window out of the ClipSegs just large enough for the plane. + // 4. Pretend the existing vissprites and drawsegs aren't there. + // 5. Create a drawseg at 0 distance to clip sprites to the visplane. It + // doesn't need to be associated with a line in the map, since there + // will never be any sprites in front of it. + // 6. Render the BSP, then planes, then masked stuff. + // 7. Restore the previous vissprites and drawsegs. + // 8. Repeat for any other sky boxes. + // 9. Put the camera back where it was to begin with. + // + void RenderPortal::RenderPlanePortals() + { + numskyboxes = 0; + + VisiblePlaneList *planes = Thread->PlaneList.get(); + DrawSegmentList *drawseglist = Thread->DrawSegments.get(); + + if (!planes->HasPortalPlanes()) + return; + + Thread->Clip3D->EnterSkybox(); + CurrentPortalInSkybox = true; + + int savedextralight = Thread->Viewport->viewpoint.extralight; + DVector3 savedpos = Thread->Viewport->viewpoint.Pos; + DRotator savedangles = Thread->Viewport->viewpoint.Angles; + double savedvisibility = LightVisibility::Instance()->GetVisibility(); + AActor *savedcamera = Thread->Viewport->viewpoint.camera; + sector_t *savedsector = Thread->Viewport->viewpoint.sector; + + for (VisiblePlane *pl = planes->PopFirstPortalPlane(); pl != nullptr; pl = planes->PopFirstPortalPlane()) + { + if (pl->right < pl->left || !r_skyboxes || numskyboxes == MAX_SKYBOX_PLANES || pl->portal == nullptr) + { + pl->Render(Thread, OPAQUE, false, false); + continue; + } + + numskyboxes++; + + FSectorPortal *port = pl->portal; + switch (port->mType) + { + case PORTS_SKYVIEWPOINT: + { + // Don't let gun flashes brighten the sky box + AActor *sky = port->mSkybox; + Thread->Viewport->viewpoint.extralight = 0; + LightVisibility::Instance()->SetVisibility(Thread->Viewport.get(), sky->args[0] * 0.25f); + + Thread->Viewport->viewpoint.Pos = sky->InterpolatedPosition(Thread->Viewport->viewpoint.TicFrac); + Thread->Viewport->viewpoint.Angles.Yaw = savedangles.Yaw + (sky->PrevAngles.Yaw + deltaangle(sky->PrevAngles.Yaw, sky->Angles.Yaw) * Thread->Viewport->viewpoint.TicFrac); + + CopyStackedViewParameters(); + break; + } + + case PORTS_STACKEDSECTORTHING: + case PORTS_PORTAL: + case PORTS_LINKEDPORTAL: + Thread->Viewport->viewpoint.extralight = pl->extralight; + LightVisibility::Instance()->SetVisibility(Thread->Viewport.get(), pl->visibility); + Thread->Viewport->viewpoint.Pos.X = pl->viewpos.X + port->mDisplacement.X; + Thread->Viewport->viewpoint.Pos.Y = pl->viewpos.Y + port->mDisplacement.Y; + Thread->Viewport->viewpoint.Pos.Z = pl->viewpos.Z; + Thread->Viewport->viewpoint.Angles.Yaw = pl->viewangle; + break; + + case PORTS_HORIZON: + case PORTS_PLANE: + // not implemented yet + + default: + pl->Render(Thread, OPAQUE, false, false); + numskyboxes--; + continue; + } + + SetInSkyBox(port); + if (port->mPartner > 0) SetInSkyBox(&level.sectorPortals[port->mPartner]); + Thread->Viewport->viewpoint.camera = nullptr; + Thread->Viewport->viewpoint.sector = port->mDestination; + assert(Thread->Viewport->viewpoint.sector != nullptr); + R_SetViewAngle(Thread->Viewport->viewpoint, Thread->Viewport->viewwindow); + Thread->OpaquePass->ClearSeenSprites(); + Thread->Clip3D->ClearFakeFloors(); + + planes->ClearKeepFakePlanes(); + Thread->ClipSegments->Clear(pl->left, pl->right); + WindowLeft = pl->left; + WindowRight = pl->right; + + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + for (int i = pl->left; i < pl->right; i++) + { + if (pl->top[i] == 0x7fff) + { + ceilingclip[i] = viewheight; + floorclip[i] = -1; + } + else + { + ceilingclip[i] = pl->top[i]; + floorclip[i] = pl->bottom[i]; + } + } + + drawseglist->PushPortal(); + Thread->SpriteList->PushPortal(); + viewposStack.Push(Thread->Viewport->viewpoint.Pos); + visplaneStack.Push(pl); + + // Create a drawseg to clip sprites to the sky plane + DrawSegment *draw_segment = Thread->FrameMemory->NewObject(); + draw_segment->CurrentPortalUniq = CurrentPortalUniq; + draw_segment->siz1 = INT_MAX; + draw_segment->siz2 = INT_MAX; + draw_segment->sz1 = 0; + draw_segment->sz2 = 0; + draw_segment->x1 = pl->left; + draw_segment->x2 = pl->right; + draw_segment->silhouette = SIL_BOTH; + draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory(pl->right - pl->left); + draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory(pl->right - pl->left); + draw_segment->maskedtexturecol = nullptr; + draw_segment->swall = nullptr; + draw_segment->bFogBoundary = false; + draw_segment->curline = nullptr; + draw_segment->fake = 0; + draw_segment->foggy = false; + memcpy(draw_segment->sprbottomclip, floorclip + pl->left, (pl->right - pl->left) * sizeof(short)); + memcpy(draw_segment->sprtopclip, ceilingclip + pl->left, (pl->right - pl->left) * sizeof(short)); + drawseglist->Push(draw_segment); + + Thread->OpaquePass->RenderScene(); + Thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) + planes->Render(); + + ClearInSkyBox(port); + if (port->mPartner > 0) SetInSkyBox(&level.sectorPortals[port->mPartner]); + } + + // Draw all the masked textures in a second pass, in the reverse order they + // were added. This must be done separately from the previous step for the + // sake of nested skyboxes. + while (viewposStack.Size() > 0) + { + // Masked textures and planes need the view coordinates restored for proper positioning. + viewposStack.Pop(Thread->Viewport->viewpoint.Pos); + + Thread->TranslucentPass->Render(); + + VisiblePlane *pl = nullptr; // quiet, GCC! + visplaneStack.Pop(pl); + if (pl->Alpha > 0 && pl->picnum != skyflatnum) + { + pl->Render(Thread, pl->Alpha, pl->Additive, true); + } + + Thread->SpriteList->PopPortal(); + drawseglist->PopPortal(); + } + + Thread->Viewport->viewpoint.camera = savedcamera; + Thread->Viewport->viewpoint.sector = savedsector; + Thread->Viewport->viewpoint.Pos = savedpos; + LightVisibility::Instance()->SetVisibility(Thread->Viewport.get(), savedvisibility); + Thread->Viewport->viewpoint.extralight = savedextralight; + Thread->Viewport->viewpoint.Angles = savedangles; + R_SetViewAngle(Thread->Viewport->viewpoint, Thread->Viewport->viewwindow); + + CurrentPortalInSkybox = false; + Thread->Clip3D->LeaveSkybox(); + + if (Thread->Clip3D->fakeActive) return; + + planes->ClearPortalPlanes(); + } + + void RenderPortal::RenderLinePortals() + { + // [RH] Walk through mirrors + // [ZZ] Merged with portals + size_t lastportal = WallPortals.Size(); + for (unsigned int i = 0; i < lastportal; i++) + { + RenderLinePortal(WallPortals[i], 0); + } + + CurrentPortal = nullptr; + CurrentPortalUniq = 0; + } + + void RenderPortal::RenderLinePortal(PortalDrawseg* pds, int depth) + { + auto viewport = Thread->Viewport.get(); + auto &viewpoint = viewport->viewpoint; + + // [ZZ] check depth. fill portal with black if it's exceeding the visual recursion limit, and continue like nothing happened. + if (depth >= r_portal_recursions) + { + uint8_t color = (uint8_t)BestColor((uint32_t *)GPalette.BaseColors, 0, 0, 0, 0, 255); + int spacing = viewport->RenderTarget->GetPitch(); + for (int x = pds->x1; x < pds->x2; x++) + { + if (x < 0 || x >= viewport->RenderTarget->GetWidth()) + continue; + + int Ytop = pds->ceilingclip[x - pds->x1]; + int Ybottom = pds->floorclip[x - pds->x1]; + + if (viewport->RenderTarget->IsBgra()) + { + uint32_t *dest = (uint32_t*)viewport->RenderTarget->GetBuffer() + x + Ytop * spacing; + + uint32_t c = GPalette.BaseColors[color].d; + for (int y = Ytop; y <= Ybottom; y++) + { + *dest = c; + dest += spacing; + } + } + else + { + uint8_t *dest = viewport->RenderTarget->GetBuffer() + x + Ytop * spacing; + + for (int y = Ytop; y <= Ybottom; y++) + { + *dest = color; + dest += spacing; + } + } + } + + if (r_highlight_portals) + RenderLinePortalHighlight(pds); + + return; + } + + DAngle startang = viewpoint.Angles.Yaw; + DVector3 startpos = viewpoint.Pos; + DVector3 savedpath[2] = { viewpoint.Path[0], viewpoint.Path[1] }; + ActorRenderFlags savedvisibility = viewpoint.camera ? viewpoint.camera->renderflags & RF_INVISIBLE : ActorRenderFlags::FromInt(0); + + viewpoint.camera->renderflags &= ~RF_INVISIBLE; + + CurrentPortalUniq++; + + unsigned int portalsAtStart = WallPortals.Size(); + + if (pds->mirror) + { + //vertex_t *v1 = ds->curline->v1; + vertex_t *v1 = pds->src->v1; + + // Reflect the current view behind the mirror. + if (pds->src->Delta().X == 0) + { // vertical mirror + viewpoint.Pos.X = v1->fX() - startpos.X + v1->fX(); + } + else if (pds->src->Delta().Y == 0) + { // horizontal mirror + viewpoint.Pos.Y = v1->fY() - startpos.Y + v1->fY(); + } + else + { // any mirror + vertex_t *v2 = pds->src->v2; + + double dx = v2->fX() - v1->fX(); + double dy = v2->fY() - v1->fY(); + double x1 = v1->fX(); + double y1 = v1->fY(); + double x = startpos.X; + double y = startpos.Y; + + // the above two cases catch len == 0 + double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); + + viewpoint.Pos.X = (x1 + r * dx) * 2 - x; + viewpoint.Pos.Y = (y1 + r * dy) * 2 - y; + } + viewpoint.Angles.Yaw = pds->src->Delta().Angle() * 2 - startang; + } + else + { + P_TranslatePortalXY(pds->src, viewpoint.Pos.X, viewpoint.Pos.Y); + P_TranslatePortalZ(pds->src, viewpoint.Pos.Z); + P_TranslatePortalAngle(pds->src, viewpoint.Angles.Yaw); + P_TranslatePortalXY(pds->src, viewpoint.Path[0].X, viewpoint.Path[0].Y); + P_TranslatePortalXY(pds->src, viewpoint.Path[1].X, viewpoint.Path[1].Y); + + if (!viewpoint.showviewer && viewpoint.camera && P_PointOnLineSidePrecise(viewpoint.Path[0], pds->dst) != P_PointOnLineSidePrecise(viewpoint.Path[1], pds->dst)) + { + double distp = (viewpoint.Path[0] - viewpoint.Path[1]).Length(); + if (distp > EQUAL_EPSILON) + { + double dist1 = (viewpoint.Pos - viewpoint.Path[0]).Length(); + double dist2 = (viewpoint.Pos - viewpoint.Path[1]).Length(); + + if (dist1 + dist2 < distp + 1) + { + viewpoint.camera->renderflags |= RF_INVISIBLE; + } + } + } + } + + viewpoint.Sin = viewpoint.Angles.Yaw.Sin(); + viewpoint.Cos = viewpoint.Angles.Yaw.Cos(); + + viewpoint.TanSin = Thread->Viewport->viewwindow.FocalTangent * viewpoint.Sin; + viewpoint.TanCos = Thread->Viewport->viewwindow.FocalTangent * viewpoint.Cos; + + CopyStackedViewParameters(); + + validcount++; + PortalDrawseg* prevpds = CurrentPortal; + CurrentPortal = pds; + + Thread->PlaneList->ClearKeepFakePlanes(); + Thread->ClipSegments->Clear(pds->x1, pds->x2); + + WindowLeft = pds->x1; + WindowRight = pds->x2; + + // RF_XFLIP should be removed before calling the root function + int prevmf = MirrorFlags; + if (pds->mirror) + { + if (MirrorFlags & RF_XFLIP) + MirrorFlags &= ~RF_XFLIP; + else MirrorFlags |= RF_XFLIP; + } + + // some portals have height differences, account for this here + Thread->Clip3D->EnterSkybox(); // push 3D floor height map + CurrentPortalInSkybox = false; // first portal in a skybox should set this variable to false for proper clipping in skyboxes. + + // first pass, set clipping + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + memcpy(ceilingclip + pds->x1, &pds->ceilingclip[0], pds->len * sizeof(*ceilingclip)); + memcpy(floorclip + pds->x1, &pds->floorclip[0], pds->len * sizeof(*floorclip)); + + Thread->OpaquePass->RenderScene(); + Thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) + if (!savedvisibility && viewpoint.camera) viewpoint.camera->renderflags &= ~RF_INVISIBLE; + + PlaneCycles.Clock(); + Thread->PlaneList->Render(); + RenderPlanePortals(); + PlaneCycles.Unclock(); + + double vzp = viewpoint.Pos.Z; + + int prevuniq = CurrentPortalUniq; + // depth check is in another place right now + unsigned int portalsAtEnd = WallPortals.Size(); + for (; portalsAtStart < portalsAtEnd; portalsAtStart++) + { + RenderLinePortal(WallPortals[portalsAtStart], depth + 1); + } + int prevuniq2 = CurrentPortalUniq; + CurrentPortalUniq = prevuniq; + + if (Thread->MainThread) + NetUpdate(); + + MaskedCycles.Clock(); // [ZZ] count sprites in portals/mirrors along with normal ones. + Thread->TranslucentPass->Render(); // this is required since with portals there often will be cases when more than 80% of the view is inside a portal. + MaskedCycles.Unclock(); + + if (Thread->MainThread) + NetUpdate(); + + Thread->Clip3D->LeaveSkybox(); // pop 3D floor height map + CurrentPortalUniq = prevuniq2; + + // draw a red line around a portal if it's being highlighted + if (r_highlight_portals) + RenderLinePortalHighlight(pds); + + CurrentPortal = prevpds; + MirrorFlags = prevmf; + viewpoint.Angles.Yaw = startang; + viewpoint.Pos = startpos; + viewpoint.Path[0] = savedpath[0]; + viewpoint.Path[1] = savedpath[1]; + } + + void RenderPortal::RenderLinePortalHighlight(PortalDrawseg* pds) + { + // [ZZ] NO OVERFLOW CHECKS HERE + // I believe it won't break. if it does, blame me. :( + + auto viewport = Thread->Viewport.get(); + + if (viewport->RenderTarget->IsBgra()) // Assuming this is just a debug function + return; + + uint8_t color = (uint8_t)BestColor((uint32_t *)GPalette.BaseColors, 255, 0, 0, 0, 255); + + uint8_t* pixels = viewport->RenderTarget->GetBuffer(); + // top edge + for (int x = pds->x1; x < pds->x2; x++) + { + if (x < 0 || x >= viewport->RenderTarget->GetWidth()) + continue; + + int p = x - pds->x1; + int Ytop = pds->ceilingclip[p]; + int Ybottom = pds->floorclip[p]; + + if (x == pds->x1 || x == pds->x2 - 1) + { + viewport->RenderTarget->DrawLine(x, Ytop, x, Ybottom + 1, color, 0); + continue; + } + + int YtopPrev = pds->ceilingclip[p - 1]; + int YbottomPrev = pds->floorclip[p - 1]; + + if (abs(Ytop - YtopPrev) > 1) + viewport->RenderTarget->DrawLine(x, YtopPrev, x, Ytop, color, 0); + else *(pixels + Ytop * viewport->RenderTarget->GetPitch() + x) = color; + + if (abs(Ybottom - YbottomPrev) > 1) + viewport->RenderTarget->DrawLine(x, YbottomPrev, x, Ybottom, color, 0); + else *(pixels + Ybottom * viewport->RenderTarget->GetPitch() + x) = color; + } + } + + void RenderPortal::CopyStackedViewParameters() + { + stacked_viewpos = Thread->Viewport->viewpoint.Pos; + stacked_angle = Thread->Viewport->viewpoint.Angles; + stacked_extralight = Thread->Viewport->viewpoint.extralight; + stacked_visibility = LightVisibility::Instance()->GetVisibility(); + } + + void RenderPortal::SetMainPortal() + { + WindowLeft = 0; + WindowRight = viewwidth; + MirrorFlags = 0; + CurrentPortal = nullptr; + CurrentPortalUniq = 0; + WallPortals.Clear(); + SectorPortalsInSkyBox.clear(); + } + + void RenderPortal::AddLinePortal(line_t *linedef, int x1, int x2, const short *topclip, const short *bottomclip) + { + WallPortals.Push(Thread->FrameMemory->NewObject(Thread, linedef, x1, x2, topclip, bottomclip)); + } +} +/* +ADD_STAT(skyboxes) +{ + FString out; + out.Format("%d skybox planes", swrenderer::RenderPortal::Instance()->numskyboxes); + return out; +} +*/ diff --git a/src/swrenderer/scene/r_portal.h b/src/swrenderer/scene/r_portal.h new file mode 100644 index 0000000000..b4382c68a1 --- /dev/null +++ b/src/swrenderer/scene/r_portal.h @@ -0,0 +1,72 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/segments/r_portalsegment.h" +#include + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlane; + + class RenderPortal + { + public: + RenderPortal(RenderThread *thread); + + void SetMainPortal(); + void CopyStackedViewParameters(); + + void RenderPlanePortals(); + void RenderLinePortals(); + + void AddLinePortal(line_t *linedef, int x1, int x2, const short *topclip, const short *bottomclip); + + RenderThread *Thread = nullptr; + + int WindowLeft = 0; + int WindowRight = 0; + uint16_t MirrorFlags = 0; + + PortalDrawseg* CurrentPortal = nullptr; + int CurrentPortalUniq = 0; + bool CurrentPortalInSkybox = false; + + // These are copies of the main parameters used when drawing stacked sectors. + // When you change the main parameters, you should copy them here too *unless* + // you are changing them to draw a stacked sector. Otherwise, stacked sectors + // won't draw in skyboxes properly. + int stacked_extralight = 0; + double stacked_visibility = 0.0; + DVector3 stacked_viewpos; + DRotator stacked_angle; + + int numskyboxes = 0; // For ADD_STAT(skyboxes) + + void SetInSkyBox(FSectorPortal *portal) { SectorPortalsInSkyBox.insert(portal); } + void ClearInSkyBox(FSectorPortal *portal) { SectorPortalsInSkyBox.erase(portal); } + bool InSkyBox(FSectorPortal *portal) const { return SectorPortalsInSkyBox.find(portal) != SectorPortalsInSkyBox.end(); } + + private: + void RenderLinePortal(PortalDrawseg* pds, int depth); + void RenderLinePortalHighlight(PortalDrawseg* pds); + + TArray viewposStack; + TArray visplaneStack; + TArray WallPortals; + + std::set SectorPortalsInSkyBox; // Instead of portal->mFlags & PORTSF_INSKYBOX + }; +} diff --git a/src/swrenderer/scene/r_scene.cpp b/src/swrenderer/scene/r_scene.cpp new file mode 100644 index 0000000000..5f0c41bd6f --- /dev/null +++ b/src/swrenderer/scene/r_scene.cpp @@ -0,0 +1,442 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "p_effect.h" +#include "po_man.h" +#include "st_stuff.h" +#include "r_data/r_interpolate.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/segments/r_portalsegment.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_thread.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/things/r_playersprite.h" + +EXTERN_CVAR(Bool, r_shadercolormaps) +EXTERN_CVAR(Int, r_clearbuffer) + +CVAR(Bool, r_scene_multithreaded, false, 0); + +namespace swrenderer +{ + cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; + + RenderScene::RenderScene() + { + Threads.push_back(std::make_unique(this)); + } + + RenderScene::~RenderScene() + { + StopThreads(); + } + + void RenderScene::SetClearColor(int color) + { + clearcolor = color; + } + + void RenderScene::RenderView(player_t *player) + { + auto viewport = MainThread()->Viewport.get(); + viewport->RenderTarget = screen; + + int width = SCREENWIDTH; + int height = SCREENHEIGHT; + float trueratio; + ActiveRatio(width, height, &trueratio); + viewport->SetViewport(width, height, trueratio); + + if (r_clearbuffer != 0) + { + if (!viewport->RenderTarget->IsBgra()) + { + memset(viewport->RenderTarget->GetBuffer(), clearcolor, viewport->RenderTarget->GetPitch() * viewport->RenderTarget->GetHeight()); + } + else + { + uint32_t bgracolor = GPalette.BaseColors[clearcolor].d; + int size = viewport->RenderTarget->GetPitch() * viewport->RenderTarget->GetHeight(); + uint32_t *dest = (uint32_t *)viewport->RenderTarget->GetBuffer(); + for (int i = 0; i < size; i++) + dest[i] = bgracolor; + } + } + + RenderActorView(player->mo); + + // Apply special colormap if the target cannot do it + if (CameraLight::Instance()->ShaderColormap() && viewport->RenderTarget->IsBgra() && !(r_shadercolormaps && screen->Accel2D)) + { + MainThread()->DrawQueue->Push(CameraLight::Instance()->ShaderColormap(), screen); + RenderDrawQueues(); + } + } + + void RenderScene::RenderDrawQueues() + { + // Use reverse order so main thread is drawn last + std::vector queues; + for (auto it = Threads.rbegin(); it != Threads.rend(); ++it) + { + queues.push_back((*it)->DrawQueue); + } + DrawerThreads::Execute(queues); + + //using namespace std::chrono_literals; + //std::this_thread::sleep_for(0.5s); + } + + void RenderScene::RenderActorView(AActor *actor, bool dontmaplines) + { + WallCycles.Reset(); + PlaneCycles.Reset(); + MaskedCycles.Reset(); + WallScanCycles.Reset(); + + R_SetupFrame(MainThread()->Viewport->viewpoint, MainThread()->Viewport->viewwindow, actor); + CameraLight::Instance()->SetCamera(MainThread()->Viewport.get(), actor); + MainThread()->Viewport->SetupFreelook(); + + NetUpdate(); + + this->dontmaplines = dontmaplines; + + // [RH] Setup particles for this frame + P_FindParticleSubsectors(); + + ActorRenderFlags savedflags = MainThread()->Viewport->viewpoint.camera->renderflags; + // Never draw the player unless in chasecam mode + if (!MainThread()->Viewport->viewpoint.showviewer) + { + MainThread()->Viewport->viewpoint.camera->renderflags |= RF_INVISIBLE; + } + + RenderThreadSlices(); + MainThread()->PlayerSprites->Render(); + RenderDrawQueues(); + + MainThread()->Viewport->viewpoint.camera->renderflags = savedflags; + interpolator.RestoreInterpolations(); + + // If we don't want shadered colormaps, NULL it now so that the + // copy to the screen does not use a special colormap shader. + if (!r_shadercolormaps && !MainThread()->Viewport->RenderTarget->IsBgra()) + { + CameraLight::Instance()->ClearShaderColormap(); + } + } + + void RenderScene::RenderThreadSlices() + { + int numThreads = std::thread::hardware_concurrency(); + if (numThreads == 0) + numThreads = 4; + + if (!r_scene_multithreaded) + numThreads = 1; + + if (numThreads != (int)Threads.size()) + { + StopThreads(); + StartThreads(numThreads); + } + + // Setup threads: + std::unique_lock start_lock(start_mutex); + for (int i = 0; i < numThreads; i++) + { + *Threads[i]->Viewport = *MainThread()->Viewport; + Threads[i]->X1 = viewwidth * i / numThreads; + Threads[i]->X2 = viewwidth * (i + 1) / numThreads; + } + run_id++; + start_lock.unlock(); + + // Notify threads to run + if (Threads.size() > 1) + { + start_condition.notify_all(); + } + + // Do the main thread ourselves: + RenderThreadSlice(MainThread()); + + // Wait for everyone to finish: + if (Threads.size() > 1) + { + std::unique_lock end_lock(end_mutex); + finished_threads++; + end_condition.wait(end_lock, [&]() { return finished_threads == Threads.size(); }); + finished_threads = 0; + } + } + + void RenderScene::RenderThreadSlice(RenderThread *thread) + { + thread->FrameMemory->Clear(); + thread->Clip3D->Cleanup(); + thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) + thread->Portal->CopyStackedViewParameters(); + thread->ClipSegments->Clear(0, viewwidth); + thread->DrawSegments->Clear(); + thread->PlaneList->Clear(); + thread->TranslucentPass->Clear(); + thread->OpaquePass->ClearClip(); + thread->OpaquePass->ResetFakingUnderwater(); // [RH] Hack to make windows into underwater areas possible + thread->Portal->SetMainPortal(); + + // Cull things outside the range seen by this thread + VisibleSegmentRenderer visitor; + if (thread->X1 > 0) + thread->ClipSegments->Clip(0, thread->X1, true, &visitor); + if (thread->X2 < viewwidth) + thread->ClipSegments->Clip(thread->X2, viewwidth, true, &visitor); + + if (thread->MainThread) + WallCycles.Clock(); + + thread->OpaquePass->RenderScene(); + thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) + + if (thread == MainThread()) + WallCycles.Unclock(); + + if (thread->MainThread) + NetUpdate(); + + if (viewactive) + { + if (thread->MainThread) + PlaneCycles.Clock(); + + thread->PlaneList->Render(); + thread->Portal->RenderPlanePortals(); + + if (thread->MainThread) + PlaneCycles.Unclock(); + + thread->Portal->RenderLinePortals(); + + if (thread->MainThread) + NetUpdate(); + + if (thread->MainThread) + MaskedCycles.Clock(); + + thread->TranslucentPass->Render(); + + if (thread->MainThread) + MaskedCycles.Unclock(); + + if (thread->MainThread) + NetUpdate(); + } + } + + void RenderScene::StartThreads(size_t numThreads) + { + while (Threads.size() < (size_t)numThreads) + { + auto thread = std::make_unique(this, false); + auto renderthread = thread.get(); + int start_run_id = run_id; + thread->thread = std::thread([=]() + { + int last_run_id = start_run_id; + while (true) + { + // Wait until we are signalled to run: + std::unique_lock start_lock(start_mutex); + start_condition.wait(start_lock, [&]() { return run_id != last_run_id || shutdown_flag; }); + if (shutdown_flag) + break; + last_run_id = run_id; + start_lock.unlock(); + + RenderThreadSlice(renderthread); + + // Notify main thread that we finished: + std::unique_lock end_lock(end_mutex); + finished_threads++; + end_lock.unlock(); + end_condition.notify_all(); + } + }); + Threads.push_back(std::move(thread)); + } + } + + void RenderScene::StopThreads() + { + std::unique_lock lock(start_mutex); + shutdown_flag = true; + lock.unlock(); + start_condition.notify_all(); + while (Threads.size() > 1) + { + Threads.back()->thread.join(); + Threads.pop_back(); + } + lock.lock(); + shutdown_flag = false; + } + + void RenderScene::RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines) + { + auto viewport = MainThread()->Viewport.get(); + + const bool savedviewactive = viewactive; + + viewwidth = width; + viewport->RenderTarget = canvas; + + R_SetWindow(MainThread()->Viewport->viewpoint, MainThread()->Viewport->viewwindow, 12, width, height, height, true); + viewwindowx = x; + viewwindowy = y; + viewactive = true; + viewport->SetViewport(width, height, MainThread()->Viewport->viewwindow.WidescreenRatio); + + RenderActorView(actor, dontmaplines); + + viewport->RenderTarget = screen; + + R_ExecuteSetViewSize(MainThread()->Viewport->viewpoint, MainThread()->Viewport->viewwindow); + float trueratio; + ActiveRatio(width, height, &trueratio); + screen->Lock(true); + viewport->SetViewport(width, height, trueratio); + screen->Unlock(); + + viewactive = savedviewactive; + } + + void RenderScene::ScreenResized() + { + auto viewport = MainThread()->Viewport.get(); + viewport->RenderTarget = screen; + int width = SCREENWIDTH; + int height = SCREENHEIGHT; + float trueratio; + ActiveRatio(width, height, &trueratio); + screen->Lock(true); + viewport->SetViewport(SCREENWIDTH, SCREENHEIGHT, trueratio); + screen->Unlock(); + } + + void RenderScene::Init() + { + // viewwidth / viewheight are set by the defaults + fillshort(zeroarray, MAXWIDTH, 0); + + R_InitShadeMaps(); + } + + void RenderScene::Deinit() + { + MainThread()->TranslucentPass->Deinit(); + MainThread()->Clip3D->Cleanup(); + } + + ///////////////////////////////////////////////////////////////////////// + + ADD_STAT(fps) + { + FString out; + out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms", + FrameCycles.TimeMS(), WallCycles.TimeMS(), PlaneCycles.TimeMS(), MaskedCycles.TimeMS()); + return out; + } + + static double f_acc, w_acc, p_acc, m_acc; + static int acc_c; + + ADD_STAT(fps_accumulated) + { + f_acc += FrameCycles.TimeMS(); + w_acc += WallCycles.TimeMS(); + p_acc += PlaneCycles.TimeMS(); + m_acc += MaskedCycles.TimeMS(); + acc_c++; + FString out; + out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms %d counts", + f_acc / acc_c, w_acc / acc_c, p_acc / acc_c, m_acc / acc_c, acc_c); + Printf(PRINT_LOG, "%s\n", out.GetChars()); + return out; + } + + static double bestwallcycles = HUGE_VAL; + + ADD_STAT(wallcycles) + { + FString out; + double cycles = WallCycles.Time(); + if (cycles && cycles < bestwallcycles) + bestwallcycles = cycles; + out.Format("%g", bestwallcycles); + return out; + } + + CCMD(clearwallcycles) + { + bestwallcycles = HUGE_VAL; + } + +#if 0 + // The replacement code for Build's wallscan doesn't have any timing calls so this does not work anymore. + static double bestscancycles = HUGE_VAL; + + ADD_STAT(scancycles) + { + FString out; + double scancycles = WallScanCycles.Time(); + if (scancycles && scancycles < bestscancycles) + bestscancycles = scancycles; + out.Format("%g", bestscancycles); + return out; + } + + CCMD(clearscancycles) + { + bestscancycles = HUGE_VAL; + } +#endif +} diff --git a/src/swrenderer/scene/r_scene.h b/src/swrenderer/scene/r_scene.h new file mode 100644 index 0000000000..d3680d422b --- /dev/null +++ b/src/swrenderer/scene/r_scene.h @@ -0,0 +1,73 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "r_defs.h" +#include "d_player.h" + +extern cycle_t FrameCycles; + +namespace swrenderer +{ + extern cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; + + class RenderThread; + + class RenderScene + { + public: + RenderScene(); + ~RenderScene(); + + void Init(); + void ScreenResized(); + void Deinit(); + + void SetClearColor(int color); + + void RenderView(player_t *player); + void RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines = false); + + bool DontMapLines() const { return dontmaplines; } + + RenderThread *MainThread() { return Threads.front().get(); } + + private: + void RenderActorView(AActor *actor, bool dontmaplines = false); + void RenderDrawQueues(); + void RenderThreadSlices(); + void RenderThreadSlice(RenderThread *thread); + + void StartThreads(size_t numThreads); + void StopThreads(); + + bool dontmaplines = false; + int clearcolor = 0; + + std::vector> Threads; + std::mutex start_mutex; + std::condition_variable start_condition; + bool shutdown_flag = false; + int run_id = 0; + std::mutex end_mutex; + std::condition_variable end_condition; + size_t finished_threads = 0; + }; +} diff --git a/src/swrenderer/scene/r_translucent_pass.cpp b/src/swrenderer/scene/r_translucent_pass.cpp new file mode 100644 index 0000000000..442643332b --- /dev/null +++ b/src/swrenderer/scene/r_translucent_pass.cpp @@ -0,0 +1,217 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "g_levellocals.h" +#include "p_maputl.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/things/r_visiblespritelist.h" +#include "swrenderer/things/r_voxel.h" +#include "swrenderer/things/r_particle.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/things/r_wallsprite.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/line/r_renderdrawsegment.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Int, r_drawfuzz) +EXTERN_CVAR(Bool, r_drawvoxels) +EXTERN_CVAR(Bool, r_blendmethod) + +CVAR(Bool, r_fullbrightignoresectorcolor, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +namespace swrenderer +{ + RenderTranslucentPass::RenderTranslucentPass(RenderThread *thread) + { + Thread = thread; + } + + void RenderTranslucentPass::Deinit() + { + RenderVoxel::Deinit(); + } + + void RenderTranslucentPass::Clear() + { + Thread->SpriteList->Clear(); + } + + void RenderTranslucentPass::CollectPortals() + { + // This function collects all drawsegs that may be of interest to R_ClipSpriteColumnWithPortals + // Having that function over the entire list of drawsegs can break down performance quite drastically. + // This is doing the costly stuff only once so that R_ClipSpriteColumnWithPortals can + // a) exit early if no relevant info is found and + // b) skip most of the collected drawsegs which have no portal attached. + portaldrawsegs.Clear(); + DrawSegmentList *drawseglist = Thread->DrawSegments.get(); + for (unsigned int index = 0; index != drawseglist->SegmentsCount(); index++) + { + DrawSegment *seg = drawseglist->Segment(index); + + // I don't know what makes this happen (some old top-down portal code or possibly skybox code? something adds null lines...) + // crashes at the first frame of the first map of Action2.wad + if (!seg->curline) continue; + + line_t* line = seg->curline->linedef; + // ignore minisegs from GL nodes. + if (!line) continue; + + // check if this line will clip sprites to itself + if (!line->isVisualPortal() && line->special != Line_Mirror) + continue; + + // don't clip sprites with portal's back side (it's transparent) + if (seg->curline->sidedef != line->sidedef[0]) + continue; + + portaldrawsegs.Push(seg); + } + } + + bool RenderTranslucentPass::ClipSpriteColumnWithPortals(int x, VisibleSprite *spr) + { + RenderPortal *renderportal = Thread->Portal.get(); + + // [ZZ] 10.01.2016: don't clip sprites from the root of a skybox. + if (renderportal->CurrentPortalInSkybox) + return false; + + for (DrawSegment *seg : portaldrawsegs) + { + // ignore segs from other portals + if (seg->CurrentPortalUniq != renderportal->CurrentPortalUniq) + continue; + + // (all checks that are already done in R_CollectPortals have been removed for performance reasons.) + + // don't clip if the sprite is in front of the portal + if (!P_PointOnLineSidePrecise(spr->WorldPos().X, spr->WorldPos().Y, seg->curline->linedef)) + continue; + + // now if current column is covered by this drawseg, we clip it away + if ((x >= seg->x1) && (x < seg->x2)) + return true; + } + + return false; + } + + void RenderTranslucentPass::DrawMaskedSingle(bool renew) + { + RenderPortal *renderportal = Thread->Portal.get(); + + auto &sortedSprites = Thread->SpriteList->SortedSprites; + for (int i = sortedSprites.Size(); i > 0; i--) + { + if (sortedSprites[i - 1]->IsCurrentPortalUniq(renderportal->CurrentPortalUniq)) + { + sortedSprites[i - 1]->Render(Thread); + } + } + + // render any remaining masked mid textures + + if (renew) + { + Thread->Clip3D->fake3D |= FAKE3D_REFRESHCLIP; + } + + DrawSegmentList *drawseglist = Thread->DrawSegments.get(); + for (unsigned int index = 0; index != drawseglist->SegmentsCount(); index++) + { + DrawSegment *ds = drawseglist->Segment(index); + + // [ZZ] the same as above + if (ds->CurrentPortalUniq != renderportal->CurrentPortalUniq) + continue; + // kg3D - no fake segs + if (ds->fake) continue; + if (ds->maskedtexturecol != nullptr || ds->bFogBoundary) + { + RenderDrawSegment renderer(Thread); + renderer.Render(ds, ds->x1, ds->x2); + } + } + } + + void RenderTranslucentPass::Render() + { + CollectPortals(); + Thread->SpriteList->Sort(); + Thread->DrawSegments->BuildSegmentGroups(); + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + if (clip3d->height_top == nullptr) + { // kg3D - no visible 3D floors, normal rendering + DrawMaskedSingle(false); + } + else + { // kg3D - correct sorting + // ceilings + for (HeightLevel *hl = clip3d->height_cur; hl != nullptr && hl->height >= Thread->Viewport->viewpoint.Pos.Z; hl = hl->prev) + { + if (hl->next) + { + clip3d->fake3D = FAKE3D_CLIPBOTTOM | FAKE3D_CLIPTOP; + clip3d->sclipTop = hl->next->height; + } + else + { + clip3d->fake3D = FAKE3D_CLIPBOTTOM; + } + clip3d->sclipBottom = hl->height; + DrawMaskedSingle(true); + Thread->PlaneList->RenderHeight(hl->height); + } + + // floors + clip3d->fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPTOP; + clip3d->sclipTop = clip3d->height_top->height; + DrawMaskedSingle(true); + for (HeightLevel *hl = clip3d->height_top; hl != nullptr && hl->height < Thread->Viewport->viewpoint.Pos.Z; hl = hl->next) + { + Thread->PlaneList->RenderHeight(hl->height); + if (hl->next) + { + clip3d->fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPTOP | FAKE3D_CLIPBOTTOM; + clip3d->sclipTop = hl->next->height; + } + else + { + clip3d->fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPBOTTOM; + } + clip3d->sclipBottom = hl->height; + DrawMaskedSingle(true); + } + clip3d->DeleteHeights(); + clip3d->fake3D = 0; + } + } +} diff --git a/src/swrenderer/scene/r_translucent_pass.h b/src/swrenderer/scene/r_translucent_pass.h new file mode 100644 index 0000000000..44551b2867 --- /dev/null +++ b/src/swrenderer/scene/r_translucent_pass.h @@ -0,0 +1,48 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "tarray.h" + +#define MINZ double((2048*4) / double(1 << 20)) + +struct particle_t; +struct FVoxel; + +namespace swrenderer +{ + class RenderThread; + class VisibleSprite; + struct DrawSegment; + + class RenderTranslucentPass + { + public: + RenderTranslucentPass(RenderThread *thread); + + void Deinit(); + void Clear(); + void Render(); + + bool ClipSpriteColumnWithPortals(int x, VisibleSprite *spr); + + RenderThread *Thread = nullptr; + + private: + void CollectPortals(); + void DrawMaskedSingle(bool renew); + + TArray portaldrawsegs; + }; +} diff --git a/src/swrenderer/segments/r_clipsegment.cpp b/src/swrenderer/segments/r_clipsegment.cpp new file mode 100644 index 0000000000..4e25daa350 --- /dev/null +++ b/src/swrenderer/segments/r_clipsegment.cpp @@ -0,0 +1,186 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "swrenderer/segments/r_clipsegment.h" + +namespace swrenderer +{ + void RenderClipSegment::Clear(short left, short right) + { + solidsegs[0].first = -0x7fff; + solidsegs[0].last = left; + solidsegs[1].first = right; + solidsegs[1].last = 0x7fff; + newend = solidsegs+2; + } + + bool RenderClipSegment::Check(int first, int last) + { + cliprange_t *start; + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = solidsegs; + while (start->last < first) + start++; + + if (first < start->first) + { + return true; + } + + // Bottom contained in start? + if (last > start->last) + { + return true; + } + + return false; + } + + bool RenderClipSegment::IsVisible(int sx1, int sx2) + { + // Does not cross a pixel. + if (sx2 <= sx1) + return false; + + cliprange_t *start = solidsegs; + while (start->last < sx2) + start++; + + if (sx1 >= start->first && sx2 <= start->last) + { + // The clippost contains the new span. + return false; + } + + return true; + } + + bool RenderClipSegment::Clip(int first, int last, bool solid, VisibleSegmentRenderer *visitor) + { + cliprange_t *next, *start; + int i, j; + bool res = false; + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = solidsegs; + while (start->last < first) + start++; + + if (first < start->first) + { + res = true; + if (last <= start->first) + { + // Post is entirely visible (above start). + if (!visitor->RenderWallSegment(first, last)) + return true; + + // Insert a new clippost for solid walls. + if (solid) + { + if (last == start->first) + { + start->first = first; + } + else + { + next = newend; + newend++; + while (next != start) + { + *next = *(next - 1); + next--; + } + next->first = first; + next->last = last; + } + } + return true; + } + + // There is a fragment above *start. + if (visitor->RenderWallSegment(first, start->first) && solid) + { + start->first = first; // Adjust the clip size for solid walls + } + } + + // Bottom contained in start? + if (last <= start->last) + return res; + + bool clipsegment; + next = start; + while (last >= (next + 1)->first) + { + // There is a fragment between two posts. + clipsegment = visitor->RenderWallSegment(next->last, (next + 1)->first); + next++; + + if (last <= next->last) + { + // Bottom is contained in next. + last = next->last; + goto crunch; + } + } + + // There is a fragment after *next. + clipsegment = visitor->RenderWallSegment(next->last, last); + + crunch: + if (!clipsegment) + { + return true; + } + if (solid) + { + // Adjust the clip size. + start->last = last; + + if (next != start) + { + // Remove start+1 to next from the clip list, + // because start now covers their area. + for (i = 1, j = (int)(newend - next); j > 0; i++, j--) + { + start[i] = next[i]; + } + newend = start + i; + } + } + return true; + } +} diff --git a/src/swrenderer/segments/r_clipsegment.h b/src/swrenderer/segments/r_clipsegment.h new file mode 100644 index 0000000000..48a083ba6e --- /dev/null +++ b/src/swrenderer/segments/r_clipsegment.h @@ -0,0 +1,44 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +namespace swrenderer +{ + typedef bool(*VisibleSegmentCallback)(int x1, int x2); + + class VisibleSegmentRenderer + { + public: + virtual ~VisibleSegmentRenderer() { } + virtual bool RenderWallSegment(int x1, int x2) { return true; } + }; + + class RenderClipSegment + { + public: + void Clear(short left, short right); + bool Clip(int x1, int x2, bool solid, VisibleSegmentRenderer *visitor); + bool Check(int first, int last); + bool IsVisible(int x1, int x2); + + private: + struct cliprange_t + { + short first, last; + }; + + cliprange_t *newend; // newend is one past the last valid seg + cliprange_t solidsegs[MAXWIDTH / 2 + 2]; + }; +} diff --git a/src/swrenderer/segments/r_drawsegment.cpp b/src/swrenderer/segments/r_drawsegment.cpp new file mode 100644 index 0000000000..e4bc3d26b4 --- /dev/null +++ b/src/swrenderer/segments/r_drawsegment.cpp @@ -0,0 +1,166 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "d_net.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/line/r_fogboundary.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + DrawSegmentList::DrawSegmentList(RenderThread *thread) + { + Thread = thread; + } + + void DrawSegmentList::Clear() + { + Segments.Clear(); + StartIndices.Clear(); + StartIndices.Push(0); + + InterestingSegments.Clear(); + StartInterestingIndices.Clear(); + StartInterestingIndices.Push(0); + } + + void DrawSegmentList::PushPortal() + { + StartIndices.Push(Segments.Size()); + StartInterestingIndices.Push(InterestingSegments.Size()); + } + + void DrawSegmentList::PopPortal() + { + Segments.Resize(StartIndices.Last()); + StartIndices.Pop(); + + InterestingSegments.Resize(StartInterestingIndices.Last()); + StartInterestingIndices.Pop(); + } + + void DrawSegmentList::Push(DrawSegment *segment) + { + Segments.Push(segment); + } + + void DrawSegmentList::PushInteresting(DrawSegment *segment) + { + InterestingSegments.Push(segment); + } + + void DrawSegmentList::BuildSegmentGroups() + { + SegmentGroups.Clear(); + + unsigned int groupSize = 100; + for (unsigned int index = 0; index < SegmentsCount(); index += groupSize) + { + auto ds = Segment(index); + + DrawSegmentGroup group; + group.BeginIndex = index; + group.EndIndex = MIN(index + groupSize, SegmentsCount()); + group.x1 = ds->x1; + group.x2 = ds->x2; + group.neardepth = MIN(ds->sz1, ds->sz2); + group.fardepth = MAX(ds->sz1, ds->sz2); + + for (unsigned int groupIndex = group.BeginIndex + 1; groupIndex < group.EndIndex; groupIndex++) + { + ds = Segment(groupIndex); + group.x1 = MIN(group.x1, ds->x1); + group.x2 = MAX(group.x2, ds->x2); + group.neardepth = MIN(group.neardepth, ds->sz1); + group.neardepth = MIN(group.neardepth, ds->sz2); + group.fardepth = MAX(ds->sz1, group.fardepth); + group.fardepth = MAX(ds->sz2, group.fardepth); + } + + for (int x = group.x1; x < group.x2; x++) + { + cliptop[x] = 0; + clipbottom[x] = viewheight; + } + + for (unsigned int groupIndex = group.BeginIndex; groupIndex < group.EndIndex; groupIndex++) + { + ds = Segment(groupIndex); + + // kg3D - no clipping on fake segs + if (ds->fake) continue; + + if (ds->silhouette & SIL_BOTTOM) + { + short *clip1 = clipbottom + ds->x1; + const short *clip2 = ds->sprbottomclip; + int i = ds->x2 - ds->x1; + do + { + if (*clip1 > *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + + if (ds->silhouette & SIL_TOP) + { + short *clip1 = cliptop + ds->x1; + const short *clip2 = ds->sprtopclip; + int i = ds->x2 - ds->x1; + do + { + if (*clip1 < *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + } + + group.sprtopclip = Thread->FrameMemory->AllocMemory(group.x2 - group.x1); + group.sprbottomclip = Thread->FrameMemory->AllocMemory(group.x2 - group.x1); + memcpy(group.sprtopclip, cliptop + group.x1, (group.x2 - group.x1) * sizeof(short)); + memcpy(group.sprbottomclip, clipbottom + group.x1, (group.x2 - group.x1) * sizeof(short)); + + SegmentGroups.Push(group); + } + } +} diff --git a/src/swrenderer/segments/r_drawsegment.h b/src/swrenderer/segments/r_drawsegment.h new file mode 100644 index 0000000000..9bfd5dc65f --- /dev/null +++ b/src/swrenderer/segments/r_drawsegment.h @@ -0,0 +1,94 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/line/r_line.h" + +namespace swrenderer +{ + struct DrawSegment + { + seg_t *curline; + float light, lightstep; + float iscale, iscalestep; + short x1, x2; // Same as sx1 and sx2, but clipped to the drawseg + short sx1, sx2; // left, right of parent seg on screen + float sz1, sz2; // z for left, right of parent seg on screen + float siz1, siz2; // 1/z for left, right of parent seg on screen + float cx, cy, cdx, cdy; + float yscale; + uint8_t silhouette; // 0=none, 1=bottom, 2=top, 3=both + uint8_t bFogBoundary; + uint8_t bFakeBoundary; // for fake walls + int shade; + bool foggy; + + // Pointers to lists for sprite clipping, all three adjusted so [x1] is first value. + short *sprtopclip; + short *sprbottomclip; + fixed_t *maskedtexturecol; + float *swall; + short *bkup; // sprtopclip backup, for mid and fake textures + + FWallTmapVals tmapvals; + + int fake; // ident fake drawseg, don't draw and clip sprites backups + int CurrentPortalUniq; // [ZZ] to identify the portal that this drawseg is in. used for sprite clipping. + }; + + struct DrawSegmentGroup + { + short x1, x2; + float neardepth, fardepth; + short *sprtopclip; + short *sprbottomclip; + unsigned int BeginIndex; + unsigned int EndIndex; + }; + + class DrawSegmentList + { + public: + DrawSegmentList(RenderThread *thread); + + TArray SegmentGroups; + + unsigned int SegmentsCount() const { return Segments.Size() - StartIndices.Last(); } + DrawSegment *Segment(unsigned int index) const { return Segments[Segments.Size() - 1 - index]; } + + unsigned int InterestingSegmentsCount() const { return InterestingSegments.Size() - StartInterestingIndices.Last(); } + DrawSegment *InterestingSegment(unsigned int index) const { return InterestingSegments[InterestingSegments.Size() - 1 - index]; } + + void Clear(); + void PushPortal(); + void PopPortal(); + void Push(DrawSegment *segment); + void PushInteresting(DrawSegment *segment); + + void BuildSegmentGroups(); + + RenderThread *Thread = nullptr; + + private: + TArray Segments; + TArray StartIndices; + + TArray InterestingSegments; // drawsegs that have something drawn on them + TArray StartInterestingIndices; + + // For building segment groups + short cliptop[MAXWIDTH]; + short clipbottom[MAXWIDTH]; + }; +} diff --git a/src/swrenderer/segments/r_portalsegment.cpp b/src/swrenderer/segments/r_portalsegment.cpp new file mode 100644 index 0000000000..f1591b6667 --- /dev/null +++ b/src/swrenderer/segments/r_portalsegment.cpp @@ -0,0 +1,64 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "swrenderer/segments/r_portalsegment.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + PortalDrawseg::PortalDrawseg(RenderThread *thread, line_t *linedef, int x1, int x2, const short *topclip, const short *bottomclip) : x1(x1), x2(x2) + { + src = linedef; + dst = linedef->special == Line_Mirror ? linedef : linedef->getPortalDestination(); + len = x2 - x1; + + ceilingclip = thread->FrameMemory->AllocMemory(len); + floorclip = thread->FrameMemory->AllocMemory(len); + memcpy(ceilingclip, topclip, len * sizeof(short)); + memcpy(floorclip, bottomclip, len * sizeof(short)); + + for (int i = 0; i < x2 - x1; i++) + { + if (ceilingclip[i] < 0) + ceilingclip[i] = 0; + if (ceilingclip[i] >= viewheight) + ceilingclip[i] = viewheight - 1; + if (floorclip[i] < 0) + floorclip[i] = 0; + if (floorclip[i] >= viewheight) + floorclip[i] = viewheight - 1; + } + + mirror = linedef->special == Line_Mirror; + } +} diff --git a/src/swrenderer/segments/r_portalsegment.h b/src/swrenderer/segments/r_portalsegment.h new file mode 100644 index 0000000000..74596bb4de --- /dev/null +++ b/src/swrenderer/segments/r_portalsegment.h @@ -0,0 +1,37 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +namespace swrenderer +{ + class RenderThread; + + /* portal structure, this is used in r_ code in order to store drawsegs with portals (and mirrors) */ + struct PortalDrawseg + { + PortalDrawseg(RenderThread *thread, line_t *linedef, int x1, int x2, const short *topclip, const short *bottomclip); + + line_t* src = nullptr; // source line (the one drawn) this doesn't change over render loops + line_t* dst = nullptr; // destination line (the one that the portal is linked with, equals 'src' for mirrors) + + int x1 = 0; // drawseg x1 + int x2 = 0; // drawseg x2 + + int len = 0; + short *ceilingclip = nullptr; + short *floorclip = nullptr; + + bool mirror = false; // true if this is a mirror (src should equal dst) + }; +} diff --git a/src/swrenderer/things/r_decal.cpp b/src/swrenderer/things/r_decal.cpp new file mode 100644 index 0000000000..1dd37c55c1 --- /dev/null +++ b/src/swrenderer/things/r_decal.cpp @@ -0,0 +1,338 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" +#include "r_sky.h" +#include "v_video.h" +#include "m_swap.h" +#include "w_wad.h" +#include "stats.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "r_decal.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/drawers/r_draw.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/things/r_wallsprite.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/viewport/r_spritedrawer.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + void RenderDecal::RenderDecals(RenderThread *thread, side_t *sidedef, DrawSegment *draw_segment, int wallshade, float lightleft, float lightstep, seg_t *curline, const FWallCoords &wallC, bool foggy, FDynamicColormap *basecolormap, const short *walltop, const short *wallbottom) + { + for (DBaseDecal *decal = sidedef->AttachedDecals; decal != NULL; decal = decal->WallNext) + { + Render(thread, sidedef, decal, draw_segment, wallshade, lightleft, lightstep, curline, wallC, foggy, basecolormap, walltop, wallbottom, 0); + } + } + + // pass = 0: when seg is first drawn + // = 1: drawing masked textures (including sprites) + // Currently, only pass = 0 is done or used + + void RenderDecal::Render(RenderThread *thread, side_t *wall, DBaseDecal *decal, DrawSegment *clipper, int wallshade, float lightleft, float lightstep, seg_t *curline, const FWallCoords &savecoord, bool foggy, FDynamicColormap *basecolormap, const short *walltop, const short *wallbottom, int pass) + { + DVector2 decal_left, decal_right, decal_pos; + int x1, x2; + double yscale; + uint8_t flipx; + double zpos; + int needrepeat = 0; + sector_t *front, *back; + bool calclighting; + bool rereadcolormap; + FDynamicColormap *usecolormap; + float light = 0; + const short *mfloorclip; + const short *mceilingclip; + + if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid()) + return; + + // Determine actor z + zpos = decal->Z; + front = curline->frontsector; + back = (curline->backsector != NULL) ? curline->backsector : curline->frontsector; + switch (decal->RenderFlags & RF_RELMASK) + { + default: + zpos = decal->Z; + break; + case RF_RELUPPER: + if (curline->linedef->flags & ML_DONTPEGTOP) + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + else + { + zpos = decal->Z + back->GetPlaneTexZ(sector_t::ceiling); + } + break; + case RF_RELLOWER: + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + else + { + zpos = decal->Z + back->GetPlaneTexZ(sector_t::floor); + } + break; + case RF_RELMID: + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::floor); + } + else + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + } + + FTexture *WallSpriteTile = TexMan(decal->PicNum, true); + flipx = (uint8_t)(decal->RenderFlags & RF_XFLIP); + + if (WallSpriteTile == NULL || WallSpriteTile->UseType == FTexture::TEX_Null) + { + return; + } + + // Determine left and right edges of sprite. Since this sprite is bound + // to a wall, we use the wall's angle instead of the decal's. This is + // pretty much the same as what R_AddLine() does. + + double edge_right = WallSpriteTile->GetWidth(); + double edge_left = WallSpriteTile->LeftOffset; + edge_right = (edge_right - edge_left) * decal->ScaleX; + edge_left *= decal->ScaleX; + + double dcx, dcy; + decal->GetXY(wall, dcx, dcy); + decal_pos = { dcx, dcy }; + + DVector2 angvec = (curline->v2->fPos() - curline->v1->fPos()).Unit(); + float maskedScaleY; + + decal_left = decal_pos - edge_left * angvec - thread->Viewport->viewpoint.Pos; + decal_right = decal_pos + edge_right * angvec - thread->Viewport->viewpoint.Pos; + + CameraLight *cameraLight; + double texturemid; + + FWallCoords WallC; + if (WallC.Init(thread, decal_left, decal_right, TOO_CLOSE_Z)) + return; + + x1 = WallC.sx1; + x2 = WallC.sx2; + + if (x1 >= clipper->x2 || x2 <= clipper->x1) + return; + + FWallTmapVals WallT; + WallT.InitFromWallCoords(thread, &WallC); + + // Get the top and bottom clipping arrays + switch (decal->RenderFlags & RF_CLIPMASK) + { + default: + // keep GCC quiet. + return; + + case RF_CLIPFULL: + if (curline->backsector == NULL) + { + if (pass != 0) + { + return; + } + mceilingclip = walltop; + mfloorclip = wallbottom; + } + else if (pass == 0) + { + mceilingclip = walltop; + mfloorclip = thread->OpaquePass->ceilingclip; + needrepeat = 1; + } + else + { + mceilingclip = clipper->sprtopclip - clipper->x1; + mfloorclip = clipper->sprbottomclip - clipper->x1; + } + break; + + case RF_CLIPUPPER: + if (pass != 0) + { + return; + } + mceilingclip = walltop; + mfloorclip = thread->OpaquePass->ceilingclip; + break; + + case RF_CLIPMID: + if (curline->backsector != NULL && pass != 2) + { + return; + } + mceilingclip = clipper->sprtopclip - clipper->x1; + mfloorclip = clipper->sprbottomclip - clipper->x1; + break; + + case RF_CLIPLOWER: + if (pass != 0) + { + return; + } + mceilingclip = thread->OpaquePass->floorclip; + mfloorclip = wallbottom; + break; + } + + yscale = decal->ScaleY; + texturemid = WallSpriteTile->TopOffset + (zpos - thread->Viewport->viewpoint.Pos.Z) / yscale; + + // Clip sprite to drawseg + x1 = MAX(clipper->x1, x1); + x2 = MIN(clipper->x2, x2); + if (x1 >= x2) + { + return; + } + + ProjectedWallTexcoords walltexcoords; + walltexcoords.Project(thread->Viewport.get(), WallSpriteTile->GetWidth(), x1, x2, WallT); + + if (flipx) + { + int i; + int right = (WallSpriteTile->GetWidth() << FRACBITS) - 1; + + for (i = x1; i < x2; i++) + { + walltexcoords.UPos[i] = right - walltexcoords.UPos[i]; + } + } + + // Prepare lighting + calclighting = false; + usecolormap = basecolormap; + rereadcolormap = true; + + // Decals that are added to the scene must fade to black. + if (decal->RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) + { + usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); + rereadcolormap = false; + } + + light = lightleft + (x1 - savecoord.sx1) * lightstep; + + cameraLight = CameraLight::Instance(); + + // Draw it + bool sprflipvert; + if (decal->RenderFlags & RF_YFLIP) + { + sprflipvert = true; + yscale = -yscale; + texturemid -= WallSpriteTile->GetHeight(); + } + else + { + sprflipvert = false; + } + + maskedScaleY = float(1 / yscale); + do + { + int x = x1; + + SpriteDrawerArgs drawerargs; + + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != NULL) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + else if (!foggy && (decal->RenderFlags & RF_FULLBRIGHT)) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, 0); + else + calclighting = true; + + bool visible = drawerargs.SetStyle(thread->Viewport.get(), decal->RenderStyle, (float)decal->Alpha, decal->Translation, decal->AlphaColor, basecolormap); + + // R_SetPatchStyle can modify basecolormap. + if (rereadcolormap) + { + usecolormap = basecolormap; + } + + if (visible) + { + while (x < x2) + { + if (calclighting) + { // calculate lighting + drawerargs.SetLight(usecolormap, light, wallshade); + } + DrawColumn(thread, drawerargs, x, WallSpriteTile, walltexcoords, texturemid, maskedScaleY, sprflipvert, mfloorclip, mceilingclip); + light += lightstep; + x++; + } + } + + // If this sprite is RF_CLIPFULL on a two-sided line, needrepeat will + // be set 1 if we need to draw on the lower wall. In all other cases, + // needrepeat will be 0, and the while will fail. + mceilingclip = thread->OpaquePass->floorclip; + mfloorclip = wallbottom; + } while (needrepeat--); + } + + void RenderDecal::DrawColumn(RenderThread *thread, SpriteDrawerArgs &drawerargs, int x, FTexture *WallSpriteTile, const ProjectedWallTexcoords &walltexcoords, double texturemid, float maskedScaleY, bool sprflipvert, const short *mfloorclip, const short *mceilingclip) + { + auto viewport = thread->Viewport.get(); + + float iscale = walltexcoords.VStep[x] * maskedScaleY; + double spryscale = 1 / iscale; + double sprtopscreen; + if (sprflipvert) + sprtopscreen = viewport->CenterY + texturemid * spryscale; + else + sprtopscreen = viewport->CenterY - texturemid * spryscale; + + drawerargs.DrawMaskedColumn(thread, x, FLOAT2FIXED(iscale), WallSpriteTile, walltexcoords.UPos[x], spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip); + } +} diff --git a/src/swrenderer/things/r_decal.h b/src/swrenderer/things/r_decal.h new file mode 100644 index 0000000000..4694014b75 --- /dev/null +++ b/src/swrenderer/things/r_decal.h @@ -0,0 +1,34 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +struct side_t; +class DBaseDecal; + +namespace swrenderer +{ + struct DrawSegment; + class ProjectedWallTexcoords; + class SpriteDrawerArgs; + + class RenderDecal + { + public: + static void RenderDecals(RenderThread *thread, side_t *wall, DrawSegment *draw_segment, int wallshade, float lightleft, float lightstep, seg_t *curline, const FWallCoords &wallC, bool foggy, FDynamicColormap *basecolormap, const short *walltop, const short *wallbottom); + + private: + static void Render(RenderThread *thread, side_t *wall, DBaseDecal *first, DrawSegment *clipper, int wallshade, float lightleft, float lightstep, seg_t *curline, const FWallCoords &wallC, bool foggy, FDynamicColormap *basecolormap, const short *walltop, const short *wallbottom, int pass); + static void DrawColumn(RenderThread *thread, SpriteDrawerArgs &drawerargs, int x, FTexture *WallSpriteTile, const ProjectedWallTexcoords &walltexcoords, double texturemid, float maskedScaleY, bool sprflipvert, const short *mfloorclip, const short *mceilingclip); + }; +} diff --git a/src/swrenderer/things/r_particle.cpp b/src/swrenderer/things/r_particle.cpp new file mode 100644 index 0000000000..d3c048653e --- /dev/null +++ b/src/swrenderer/things/r_particle.cpp @@ -0,0 +1,291 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "v_video.h" +#include "sc_man.h" +#include "s_sound.h" +#include "sbar.h" +#include "gi.h" +#include "r_sky.h" +#include "cmdlib.h" +#include "g_level.h" +#include "d_net.h" +#include "colormatcher.h" +#include "d_netinf.h" +#include "p_effect.h" +#include "v_palette.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "p_local.h" +#include "p_maputl.h" +#include "r_voxel.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/line/r_renderdrawsegment.h" +#include "swrenderer/things/r_particle.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + void RenderParticle::Project(RenderThread *thread, particle_t *particle, const sector_t *sector, int shade, WaterFakeSide fakeside, bool foggy) + { + double tr_x, tr_y; + double tx, ty; + double tz, tiz; + double xscale, yscale; + int x1, x2, y1, y2; + sector_t* heightsec = NULL; + + RenderPortal *renderportal = thread->Portal.get(); + + // [ZZ] Particle not visible through the portal plane + if (renderportal->CurrentPortal && !!P_PointOnLineSide(particle->Pos, renderportal->CurrentPortal->dst)) + return; + + // transform the origin point + tr_x = particle->Pos.X - thread->Viewport->viewpoint.Pos.X; + tr_y = particle->Pos.Y - thread->Viewport->viewpoint.Pos.Y; + + tz = tr_x * thread->Viewport->viewpoint.TanCos + tr_y * thread->Viewport->viewpoint.TanSin; + + // particle is behind view plane? + if (tz < MINZ) + return; + + tx = tr_x * thread->Viewport->viewpoint.Sin - tr_y * thread->Viewport->viewpoint.Cos; + + // Flip for mirrors + if (renderportal->MirrorFlags & RF_XFLIP) + { + tx = viewwidth - tx - 1; + } + + // too far off the side? + if (tz <= fabs(tx)) + return; + + tiz = 1 / tz; + xscale = thread->Viewport->viewwindow.centerx * tiz; + + // calculate edges of the shape + double psize = particle->size / 8.0; + + x1 = MAX(renderportal->WindowLeft, thread->Viewport->viewwindow.centerx + xs_RoundToInt((tx - psize) * xscale)); + x2 = MIN(renderportal->WindowRight, thread->Viewport->viewwindow.centerx + xs_RoundToInt((tx + psize) * xscale)); + + if (x1 >= x2) + return; + + auto viewport = thread->Viewport.get(); + + yscale = xscale; // YaspectMul is not needed for particles as they should always be square + ty = particle->Pos.Z - viewport->viewpoint.Pos.Z; + y1 = xs_RoundToInt(viewport->CenterY - (ty + psize) * yscale); + y2 = xs_RoundToInt(viewport->CenterY - (ty - psize) * yscale); + + // Clip the particle now. Because it's a point and projected as its subsector is + // entered, we don't need to clip it to drawsegs like a normal sprite. + + // Clip particles behind walls. + auto ceilingclip = thread->OpaquePass->ceilingclip; + auto floorclip = thread->OpaquePass->floorclip; + if (y1 < ceilingclip[x1]) y1 = ceilingclip[x1]; + if (y1 < ceilingclip[x2 - 1]) y1 = ceilingclip[x2 - 1]; + if (y2 >= floorclip[x1]) y2 = floorclip[x1] - 1; + if (y2 >= floorclip[x2 - 1]) y2 = floorclip[x2 - 1] - 1; + + if (y1 > y2) + return; + + // Clip particles above the ceiling or below the floor. + heightsec = sector->GetHeightSec(); + + const secplane_t *topplane; + const secplane_t *botplane; + FTextureID toppic; + FTextureID botpic; + FDynamicColormap *map; + + if (heightsec) // only clip things which are in special sectors + { + if (fakeside == WaterFakeSide::AboveCeiling) + { + topplane = §or->ceilingplane; + botplane = &heightsec->ceilingplane; + toppic = sector->GetTexture(sector_t::ceiling); + botpic = heightsec->GetTexture(sector_t::ceiling); + map = heightsec->ColorMap; + } + else if (fakeside == WaterFakeSide::BelowFloor) + { + topplane = &heightsec->floorplane; + botplane = §or->floorplane; + toppic = heightsec->GetTexture(sector_t::floor); + botpic = sector->GetTexture(sector_t::floor); + map = heightsec->ColorMap; + } + else + { + topplane = &heightsec->ceilingplane; + botplane = &heightsec->floorplane; + toppic = heightsec->GetTexture(sector_t::ceiling); + botpic = heightsec->GetTexture(sector_t::floor); + map = sector->ColorMap; + } + } + else + { + topplane = §or->ceilingplane; + botplane = §or->floorplane; + toppic = sector->GetTexture(sector_t::ceiling); + botpic = sector->GetTexture(sector_t::floor); + map = sector->ColorMap; + } + + if (botpic != skyflatnum && particle->Pos.Z < botplane->ZatPoint(particle->Pos)) + return; + if (toppic != skyflatnum && particle->Pos.Z >= topplane->ZatPoint(particle->Pos)) + return; + + // store information in a vissprite + RenderParticle *vis = thread->FrameMemory->NewObject(); + + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->heightsec = heightsec; + vis->xscale = FLOAT2FIXED(xscale); + vis->yscale = (float)xscale; + // vis->yscale *= InvZtoScale; + vis->depth = (float)tz; + vis->idepth = float(1 / tz); + vis->gpos = { (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z }; + vis->y1 = y1; + vis->y2 = y2; + vis->x1 = x1; + vis->x2 = x2; + vis->Translation = 0; + vis->startfrac = 255 & (particle->color >> 24); + vis->pic = NULL; + vis->renderflags = (short)(particle->alpha * 255.0f + 0.5f); + vis->FakeFlatStat = fakeside; + vis->floorclip = 0; + vis->foggy = foggy; + + vis->Light.SetColormap(tiz * LightVisibility::Instance()->ParticleGlobVis(foggy), shade, map, particle->bright != 0, false, false); + + thread->SpriteList->Push(vis); + } + + void RenderParticle::Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) + { + auto vis = this; + + int spacing; + uint8_t color = vis->Light.BaseColormap->Maps[vis->startfrac]; + int yl = vis->y1; + int ycount = vis->y2 - yl + 1; + int x1 = vis->x1; + int countbase = vis->x2 - x1; + + if (ycount <= 0 || countbase <= 0) + return; + + DrawMaskedSegsBehindParticle(thread); + + uint32_t fg = LightBgra::shade_pal_index_simple(color, LightBgra::calc_light_multiplier(LIGHTSCALE(0, vis->Light.ColormapNum << FRACBITS))); + + // vis->renderflags holds translucency level (0-255) + fixed_t fglevel = ((vis->renderflags + 1) << 8) & ~0x3ff; + uint32_t alpha = fglevel * 256 / FRACUNIT; + + auto viewport = thread->Viewport.get(); + + spacing = viewport->RenderTarget->GetPitch(); + + uint32_t fracstepx = PARTICLE_TEXTURE_SIZE * FRACUNIT / countbase; + uint32_t fracposx = fracstepx / 2; + + RenderTranslucentPass *translucentPass = thread->TranslucentPass.get(); + + if (viewport->RenderTarget->IsBgra()) + { + for (int x = x1; x < (x1 + countbase); x++, fracposx += fracstepx) + { + if (translucentPass->ClipSpriteColumnWithPortals(x, vis)) + continue; + uint32_t *dest = (uint32_t*)viewport->GetDest(x, yl); + thread->DrawQueue->Push(dest, yl, spacing, ycount, fg, alpha, fracposx); + } + } + else + { + for (int x = x1; x < (x1 + countbase); x++, fracposx += fracstepx) + { + if (translucentPass->ClipSpriteColumnWithPortals(x, vis)) + continue; + uint8_t *dest = viewport->GetDest(x, yl); + thread->DrawQueue->Push(dest, yl, spacing, ycount, fg, alpha, fracposx); + } + } + } + + void RenderParticle::DrawMaskedSegsBehindParticle(RenderThread *thread) + { + // Draw any masked textures behind this particle so that when the + // particle is drawn, it will be in front of them. + DrawSegmentList *segmentlist = thread->DrawSegments.get(); + for (unsigned int index = 0; index != segmentlist->InterestingSegmentsCount(); index++) + { + DrawSegment *ds = segmentlist->InterestingSegment(index); + + // kg3D - no fake segs + if (ds->fake) continue; + if (ds->x1 >= x2 || ds->x2 <= x1) + { + continue; + } + if ((ds->siz2 - ds->siz1) * ((x2 + x1) / 2 - ds->sx1) / (ds->sx2 - ds->sx1) + ds->siz1 < idepth) + { + // [ZZ] only draw stuff that's inside the same portal as the particle, other portals will care for themselves + if (ds->CurrentPortalUniq == CurrentPortalUniq) + { + RenderDrawSegment renderer(thread); + renderer.Render(ds, MAX(ds->x1, x1), MIN(ds->x2, x2)); + } + } + } + } +} diff --git a/src/swrenderer/things/r_particle.h b/src/swrenderer/things/r_particle.h new file mode 100644 index 0000000000..af2f59e6cc --- /dev/null +++ b/src/swrenderer/things/r_particle.h @@ -0,0 +1,42 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visiblesprite.h" +#include "swrenderer/scene/r_opaque_pass.h" + +struct particle_t; + +namespace swrenderer +{ + class RenderParticle : public VisibleSprite + { + public: + static void Project(RenderThread *thread, particle_t *, const sector_t *sector, int shade, WaterFakeSide fakeside, bool foggy); + + protected: + bool IsParticle() const override { return true; } + void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) override; + + private: + void DrawMaskedSegsBehindParticle(RenderThread *thread); + + fixed_t xscale = 0; + fixed_t startfrac = 0; // horizontal position of x1 + int y1 = 0, y2 = 0; + + uint32_t Translation = 0; + uint32_t FillColor = 0; + }; +} diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp new file mode 100644 index 0000000000..daa8e7ea45 --- /dev/null +++ b/src/swrenderer/things/r_playersprite.cpp @@ -0,0 +1,640 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "swrenderer/things/r_playersprite.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "v_video.h" +#include "sc_man.h" +#include "s_sound.h" +#include "sbar.h" +#include "gi.h" +#include "r_sky.h" +#include "cmdlib.h" +#include "g_level.h" +#include "d_net.h" +#include "colormatcher.h" +#include "d_netinf.h" +#include "p_effect.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "v_palette.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "p_local.h" +#include "p_maputl.h" +#include "r_voxel.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "g_levellocals.h" + +EXTERN_CVAR(Bool, st_scale) +EXTERN_CVAR(Bool, r_drawplayersprites) +EXTERN_CVAR(Bool, r_deathcamera) +EXTERN_CVAR(Bool, r_shadercolormaps) +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) + +namespace swrenderer +{ + RenderPlayerSprites::RenderPlayerSprites(RenderThread *thread) + { + Thread = thread; + } + + void RenderPlayerSprites::Render() + { + int i; + int lightnum; + DPSprite* psp; + DPSprite* weapon; + sector_t* sec = NULL; + int floorlight, ceilinglight; + F3DFloor *rover; + + if (!r_drawplayersprites || + !Thread->Viewport->viewpoint.camera || + !Thread->Viewport->viewpoint.camera->player || + (players[consoleplayer].cheats & CF_CHASECAM) || + (r_deathcamera && Thread->Viewport->viewpoint.camera->health <= 0)) + return; + + FDynamicColormap *basecolormap; + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() < 0 && Thread->Viewport->viewpoint.sector->e && Thread->Viewport->viewpoint.sector->e->XFloor.lightlist.Size()) + { + for (i = Thread->Viewport->viewpoint.sector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) + { + if (Thread->Viewport->viewpoint.Pos.Z <= Thread->Viewport->viewpoint.sector->e->XFloor.lightlist[i].plane.Zat0()) + { + rover = Thread->Viewport->viewpoint.sector->e->XFloor.lightlist[i].caster; + if (rover) + { + if (rover->flags & FF_DOUBLESHADOW && Thread->Viewport->viewpoint.Pos.Z <= rover->bottom.plane->Zat0()) + break; + sec = rover->model; + if (rover->flags & FF_FADEWALLS) + basecolormap = sec->ColorMap; + else + basecolormap = Thread->Viewport->viewpoint.sector->e->XFloor.lightlist[i].extra_colormap; + } + break; + } + } + if (!sec) + { + sec = Thread->Viewport->viewpoint.sector; + basecolormap = sec->ColorMap; + } + floorlight = ceilinglight = sec->lightlevel; + } + else + { // This used to use camera->Sector but due to interpolation that can be incorrect + // when the interpolated viewpoint is in a different sector than the camera. + sec = Thread->OpaquePass->FakeFlat(Thread->Viewport->viewpoint.sector, &tempsec, &floorlight, &ceilinglight, nullptr, 0, 0, 0, 0); + + // [RH] set basecolormap + basecolormap = sec->ColorMap; + } + + // [RH] set foggy flag + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); + + // get light level + lightnum = ((floorlight + ceilinglight) >> 1) + LightVisibility::ActualExtraLight(foggy, Thread->Viewport.get()); + int spriteshade = LightVisibility::LightLevelToShade(lightnum, foggy) - 24 * FRACUNIT; + + if (Thread->Viewport->viewpoint.camera->player != NULL) + { + auto viewport = Thread->Viewport.get(); + + double centerhack = viewport->CenterY; + double wx, wy; + float bobx, boby; + + viewport->CenterY = viewheight / 2; + + P_BobWeapon(viewport->viewpoint.camera->player, &bobx, &boby, viewport->viewpoint.TicFrac); + + // Interpolate the main weapon layer once so as to be able to add it to other layers. + if ((weapon = viewport->viewpoint.camera->player->FindPSprite(PSP_WEAPON)) != nullptr) + { + if (weapon->firstTic) + { + wx = weapon->x; + wy = weapon->y; + } + else + { + wx = weapon->oldx + (weapon->x - weapon->oldx) * viewport->viewpoint.TicFrac; + wy = weapon->oldy + (weapon->y - weapon->oldy) * viewport->viewpoint.TicFrac; + } + } + else + { + wx = 0; + wy = 0; + } + + // add all active psprites + psp = viewport->viewpoint.camera->player->psprites; + while (psp) + { + // [RH] Don't draw the targeter's crosshair if the player already has a crosshair set. + // It's possible this psprite's caller is now null but the layer itself hasn't been destroyed + // because it didn't tick yet (if we typed 'take all' while in the console for example). + // In this case let's simply not draw it to avoid crashing. + + if ((psp->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && psp->GetCaller() != nullptr) + { + RenderSprite(psp, viewport->viewpoint.camera, bobx, boby, wx, wy, viewport->viewpoint.TicFrac, spriteshade, basecolormap, foggy); + } + + psp = psp->GetNext(); + } + + viewport->CenterY = centerhack; + } + } + + void RenderPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy) + { + double tx; + int x1; + int x2; + double sx, sy; + spritedef_t* sprdef; + spriteframe_t* sprframe; + FTextureID picnum; + uint16_t flip; + FTexture* tex; + bool noaccel; + double alpha = owner->Alpha; + + // decide which patch to use + if ((unsigned)pspr->GetSprite() >= (unsigned)sprites.Size()) + { + DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite number %i\n", pspr->GetSprite()); + return; + } + sprdef = &sprites[pspr->GetSprite()]; + if (pspr->GetFrame() >= sprdef->numframes) + { + DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite frame %i : %i\n", pspr->GetSprite(), pspr->GetFrame()); + return; + } + sprframe = &SpriteFrames[sprdef->spriteframes + pspr->GetFrame()]; + + picnum = sprframe->Texture[0]; + flip = sprframe->Flip & 1; + tex = TexMan(picnum); + + if (tex->UseType == FTexture::TEX_Null || pspr->RenderStyle == STYLE_None) + return; + + if (pspr->firstTic) + { // Can't interpolate the first tic. + pspr->firstTic = false; + pspr->oldx = pspr->x; + pspr->oldy = pspr->y; + } + + sx = pspr->oldx + (pspr->x - pspr->oldx) * ticfrac; + sy = pspr->oldy + (pspr->y - pspr->oldy) * ticfrac + WEAPON_FUDGE_Y; + + if (pspr->Flags & PSPF_ADDBOB) + { + sx += bobx; + sy += boby; + } + + if (pspr->Flags & PSPF_ADDWEAPON && pspr->GetID() != PSP_WEAPON) + { + sx += wx; + sy += wy; + } + + auto viewport = Thread->Viewport.get(); + + double pspritexscale = viewport->viewwindow.centerxwide / 160.0; + double pspriteyscale = pspritexscale * viewport->YaspectMul; + double pspritexiscale = 1 / pspritexscale; + + // calculate edges of the shape + tx = sx - BASEXCENTER; + + tx -= tex->GetScaledLeftOffset(); + x1 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); + + // off the right side + if (x1 > viewwidth) + return; + + tx += tex->GetScaledWidth(); + x2 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); + + // off the left side + if (x2 <= 0) + return; + + // store information in a vissprite + NoAccelPlayerSprite vis; + + vis.renderflags = owner->renderflags; + + vis.texturemid = (BASEYCENTER - sy) * tex->Scale.Y + tex->TopOffset; + + if (Thread->Viewport->viewpoint.camera->player && (viewport->RenderTarget != screen || + viewheight == viewport->RenderTarget->GetHeight() || + (viewport->RenderTarget->GetWidth() > (BASEXCENTER * 2) && !st_scale))) + { // Adjust PSprite for fullscreen views + AWeapon *weapon = dyn_cast(pspr->GetCaller()); + if (weapon != nullptr && weapon->YAdjust != 0) + { + if (viewport->RenderTarget != screen || viewheight == viewport->RenderTarget->GetHeight()) + { + vis.texturemid -= weapon->YAdjust; + } + else + { + vis.texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust; + } + } + } + if (pspr->GetID() < PSP_TARGETCENTER) + { // Move the weapon down for 1280x1024. + vis.texturemid -= AspectPspriteOffset(viewport->viewwindow.WidescreenRatio); + } + vis.x1 = x1 < 0 ? 0 : x1; + vis.x2 = x2 >= viewwidth ? viewwidth : x2; + vis.xscale = FLOAT2FIXED(pspritexscale / tex->Scale.X); + vis.yscale = float(pspriteyscale / tex->Scale.Y); + vis.pic = tex; + + // If flip is used, provided that it's not already flipped (that would just invert itself) + // (It's an XOR...) + if (!(flip) != !(pspr->Flags & PSPF_FLIP)) + { + vis.xiscale = -FLOAT2FIXED(pspritexiscale * tex->Scale.X); + vis.startfrac = (tex->GetWidth() << FRACBITS) - 1; + } + else + { + vis.xiscale = FLOAT2FIXED(pspritexiscale * tex->Scale.X); + vis.startfrac = 0; + } + + if (vis.x1 > x1) + vis.startfrac += vis.xiscale*(vis.x1 - x1); + + noaccel = false; + FDynamicColormap *colormap_to_use = nullptr; + if (pspr->GetID() < PSP_TARGETCENTER) + { + // [MC] Set the render style + + if (pspr->Flags & PSPF_RENDERSTYLE) + { + const int rs = clamp(pspr->RenderStyle, 0, STYLE_Count); + + if (pspr->Flags & PSPF_FORCESTYLE) + { + vis.RenderStyle = LegacyRenderStyles[rs]; + } + else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Fuzzy]) + { + vis.RenderStyle = LegacyRenderStyles[STYLE_Fuzzy]; + } + else if (owner->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy]) + { + vis.RenderStyle = LegacyRenderStyles[STYLE_OptFuzzy]; + vis.RenderStyle.CheckFuzz(); + } + else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Subtract]) + { + vis.RenderStyle = LegacyRenderStyles[STYLE_Subtract]; + } + else + { + vis.RenderStyle = LegacyRenderStyles[rs]; + } + } + else + { + vis.RenderStyle = owner->RenderStyle; + } + + // Set the alpha based on if using the overlay's own or not. Also adjust + // and override the alpha if not forced. + if (pspr->Flags & PSPF_ALPHA) + { + if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy]) + { + alpha = owner->Alpha; + } + else if (vis.RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy]) + { + FRenderStyle style = vis.RenderStyle; + style.CheckFuzz(); + switch (style.BlendOp) + { + default: + alpha = pspr->alpha * owner->Alpha; + break; + case STYLEOP_Fuzz: + case STYLEOP_Sub: + alpha = owner->Alpha; + break; + } + + } + else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Subtract]) + { + alpha = owner->Alpha; + } + else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Add] || + vis.RenderStyle == LegacyRenderStyles[STYLE_Translucent] || + vis.RenderStyle == LegacyRenderStyles[STYLE_TranslucentStencil] || + vis.RenderStyle == LegacyRenderStyles[STYLE_AddStencil] || + vis.RenderStyle == LegacyRenderStyles[STYLE_AddShaded]) + { + alpha = owner->Alpha * pspr->alpha; + } + else + { + alpha = owner->Alpha; + } + } + + // Should normal renderstyle come out on top at the end and we desire alpha, + // switch it to translucent. Normal never applies any sort of alpha. + if ((pspr->Flags & PSPF_ALPHA) && + vis.RenderStyle == LegacyRenderStyles[STYLE_Normal] && + vis.Alpha < 1.0) + { + vis.RenderStyle = LegacyRenderStyles[STYLE_Translucent]; + alpha = owner->Alpha * pspr->alpha; + } + + // ALWAYS take priority if asked for, except fuzz. Fuzz does absolutely nothing + // no matter what way it's changed. + if (pspr->Flags & PSPF_FORCEALPHA) + { + //Due to lack of != operators... + if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy] || + vis.RenderStyle == LegacyRenderStyles[STYLE_SoulTrans] || + vis.RenderStyle == LegacyRenderStyles[STYLE_Stencil]) + { + } + else + { + alpha = pspr->alpha; + vis.RenderStyle.Flags |= STYLEF_ForceAlpha; + } + } + vis.Alpha = clamp(float(alpha), 0.f, 1.f); + + // Due to how some of the effects are handled, going to 0 or less causes some + // weirdness to display. There's no point rendering it anyway if it's 0. + if (vis.Alpha <= 0.) + return; + + //----------------------------------------------------------------------------- + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + bool invertcolormap = (vis.RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + + if (vis.RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + bool fullbright = !foggy && pspr->GetState()->GetFullbright(); + bool fadeToBlack = (vis.RenderStyle.Flags & STYLEF_FadeToBlack) != 0; + + vis.Light.SetColormap(0, spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack); + + colormap_to_use = (FDynamicColormap*)vis.Light.BaseColormap; + + if (Thread->Viewport->viewpoint.camera->Inventory != nullptr) + { + visstyle_t visstyle; + visstyle.Alpha = vis.Alpha; + visstyle.RenderStyle = STYLE_Count; + visstyle.Invert = false; + + Thread->Viewport->viewpoint.camera->Inventory->AlterWeaponSprite(&visstyle); + + vis.Alpha = visstyle.Alpha; + + if (visstyle.RenderStyle != STYLE_Count) + { + vis.RenderStyle = visstyle.RenderStyle; + } + + if (visstyle.Invert) + { + vis.Light.BaseColormap = &SpecialColormaps[INVERSECOLORMAP]; + vis.Light.ColormapNum = 0; + noaccel = true; + } + } + // If we're drawing with a special colormap, but shaders for them are disabled, do + // not accelerate. + if (!r_shadercolormaps && (vis.Light.BaseColormap >= &SpecialColormaps[0] && + vis.Light.BaseColormap <= &SpecialColormaps.Last())) + { + noaccel = true; + } + // If drawing with a BOOM colormap, disable acceleration. + if (vis.Light.BaseColormap == &NormalLight && NormalLight.Maps != realcolormaps.Maps) + { + noaccel = true; + } + // If the main colormap has fixed lights, and this sprite is being drawn with that + // colormap, disable acceleration so that the lights can remain fixed. + CameraLight *cameraLight = CameraLight::Instance(); + if (!noaccel && cameraLight->ShaderColormap() == nullptr && + NormalLightHasFixedLights && vis.Light.BaseColormap == &NormalLight && + vis.pic->UseBasePalette()) + { + noaccel = true; + } + } + else + { + colormap_to_use = basecolormap; + + vis.Light.BaseColormap = basecolormap; + vis.Light.ColormapNum = 0; + } + + // Check for hardware-assisted 2D. If it's available, and this sprite is not + // fuzzy, don't draw it until after the switch to 2D mode. + if (!noaccel && viewport->RenderTarget == screen && (DFrameBuffer *)screen->Accel2D) + { + FRenderStyle style = vis.RenderStyle; + style.CheckFuzz(); + if (style.BlendOp != STYLEOP_Fuzz) + { + HWAccelPlayerSprite accelSprite; + + accelSprite.pic = vis.pic; + accelSprite.texturemid = vis.texturemid; + accelSprite.yscale = vis.yscale; + accelSprite.xscale = vis.xscale; + + accelSprite.Alpha = vis.Alpha; + accelSprite.RenderStyle = vis.RenderStyle; + accelSprite.Translation = vis.Translation; + accelSprite.FillColor = vis.FillColor; + + accelSprite.basecolormap = colormap_to_use; + accelSprite.x1 = x1; + accelSprite.flip = vis.xiscale < 0; + + if (vis.Light.BaseColormap >= &SpecialColormaps[0] && + vis.Light.BaseColormap < &SpecialColormaps[SpecialColormaps.Size()]) + { + accelSprite.special = static_cast(vis.Light.BaseColormap); + } + else if (CameraLight::Instance()->ShaderColormap()) + { + accelSprite.special = CameraLight::Instance()->ShaderColormap(); + } + else if (colormap_to_use->Color == PalEntry(255, 255, 255) && + colormap_to_use->Desaturate == 0) + { + accelSprite.overlay = colormap_to_use->Fade; + accelSprite.overlay.a = uint8_t(vis.Light.ColormapNum * 255 / NUMCOLORMAPS); + } + else + { + accelSprite.usecolormapstyle = true; + accelSprite.colormapstyle.Color = colormap_to_use->Color; + accelSprite.colormapstyle.Fade = colormap_to_use->Fade; + accelSprite.colormapstyle.Desaturate = colormap_to_use->Desaturate; + accelSprite.colormapstyle.FadeLevel = vis.Light.ColormapNum / float(NUMCOLORMAPS); + } + + AcceleratedSprites.Push(accelSprite); + return; + } + } + + vis.Render(Thread); + } + + void RenderPlayerSprites::RenderRemaining() + { + for (const HWAccelPlayerSprite &sprite : AcceleratedSprites) + { + screen->DrawTexture(sprite.pic, + viewwindowx + sprite.x1, + viewwindowy + viewheight / 2 - sprite.texturemid * sprite.yscale - 0.5, + DTA_DestWidthF, FIXED2DBL(sprite.pic->GetWidth() * sprite.xscale), + DTA_DestHeightF, sprite.pic->GetHeight() * sprite.yscale, + DTA_TranslationIndex, sprite.Translation, + DTA_FlipX, sprite.flip, + DTA_TopOffset, 0, + DTA_LeftOffset, 0, + DTA_ClipLeft, viewwindowx, + DTA_ClipTop, viewwindowy, + DTA_ClipRight, viewwindowx + viewwidth, + DTA_ClipBottom, viewwindowy + viewheight, + DTA_Alpha, sprite.Alpha, + DTA_RenderStyle, sprite.RenderStyle, + DTA_FillColor, sprite.FillColor, + DTA_SpecialColormap, sprite.special, + DTA_ColorOverlay, sprite.overlay.d, + DTA_ColormapStyle, sprite.usecolormapstyle ? &sprite.colormapstyle : nullptr, + TAG_DONE); + } + + AcceleratedSprites.Clear(); + } + + ///////////////////////////////////////////////////////////////////////// + + void NoAccelPlayerSprite::Render(RenderThread *thread) + { + if (xscale == 0 || fabs(yscale) < (1.0f / 32000.0f)) + { // scaled to 0; can't see + return; + } + + SpriteDrawerArgs drawerargs; + drawerargs.SetLight(Light.BaseColormap, 0, Light.ColormapNum << FRACBITS); + + FDynamicColormap *basecolormap = static_cast(Light.BaseColormap); + + bool visible = drawerargs.SetStyle(thread->Viewport.get(), RenderStyle, Alpha, Translation, FillColor, basecolormap, Light.ColormapNum << FRACBITS); + if (!visible) + return; + + double spryscale = yscale; + bool sprflipvert = false; + fixed_t iscale = FLOAT2FIXED(1 / yscale); + + auto viewport = thread->Viewport.get(); + + double sprtopscreen; + if (renderflags & RF_YFLIP) + { + sprflipvert = true; + spryscale = -spryscale; + iscale = -iscale; + sprtopscreen = viewport->CenterY + (texturemid - pic->GetHeight()) * spryscale; + } + else + { + sprflipvert = false; + sprtopscreen = viewport->CenterY - texturemid * spryscale; + } + + // clip to screen bounds + short *mfloorclip = screenheightarray; + short *mceilingclip = zeroarray; + + fixed_t frac = startfrac; + for (int x = x1; x < x2; x++) + { + drawerargs.DrawMaskedColumn(thread, x, iscale, pic, frac, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, false); + frac += xiscale; + } + + if (thread->MainThread) + NetUpdate(); + } +} diff --git a/src/swrenderer/things/r_playersprite.h b/src/swrenderer/things/r_playersprite.h new file mode 100644 index 0000000000..588f9f8dc6 --- /dev/null +++ b/src/swrenderer/things/r_playersprite.h @@ -0,0 +1,93 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visiblesprite.h" +#include "r_data/colormaps.h" + +class DPSprite; + +namespace swrenderer +{ + class NoAccelPlayerSprite + { + public: + short x1 = 0; + short x2 = 0; + + double texturemid = 0.0; + + fixed_t xscale = 0; + float yscale = 0.0f; + + FTexture *pic = nullptr; + + fixed_t xiscale = 0; + fixed_t startfrac = 0; + + float Alpha = 0.0f; + FRenderStyle RenderStyle; + uint32_t Translation = 0; + uint32_t FillColor = 0; + + ColormapLight Light; + + short renderflags = 0; + + void Render(RenderThread *thread); + }; + + class HWAccelPlayerSprite + { + public: + FTexture *pic = nullptr; + double texturemid = 0.0; + float yscale = 0.0f; + fixed_t xscale = 0; + + float Alpha = 0.0f; + FRenderStyle RenderStyle; + uint32_t Translation = 0; + uint32_t FillColor = 0; + + FDynamicColormap *basecolormap = nullptr; + int x1 = 0; + + bool flip = false; + FSpecialColormap *special = nullptr; + PalEntry overlay = 0; + FColormapStyle colormapstyle; + bool usecolormapstyle = false; + }; + + class RenderPlayerSprites + { + public: + RenderPlayerSprites(RenderThread *thread); + + void Render(); + void RenderRemaining(); + + RenderThread *Thread = nullptr; + + private: + void RenderSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy); + + enum { BASEXCENTER = 160 }; + enum { BASEYCENTER = 100 }; + + TArray AcceleratedSprites; + sector_t tempsec; + }; +} diff --git a/src/swrenderer/things/r_sprite.cpp b/src/swrenderer/things/r_sprite.cpp new file mode 100644 index 0000000000..88bec93ce9 --- /dev/null +++ b/src/swrenderer/things/r_sprite.cpp @@ -0,0 +1,359 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "swrenderer/things/r_wallsprite.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "v_video.h" +#include "sc_man.h" +#include "s_sound.h" +#include "sbar.h" +#include "gi.h" +#include "r_sky.h" +#include "cmdlib.h" +#include "g_level.h" +#include "d_net.h" +#include "colormatcher.h" +#include "d_netinf.h" +#include "p_effect.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "v_palette.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "p_local.h" +#include "r_voxel.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "gl/dynlights/gl_dynlight.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) +EXTERN_CVAR(Bool, gl_light_sprites) + +namespace swrenderer +{ + void RenderSprite::Project(RenderThread *thread, AActor *thing, const DVector3 &pos, FTexture *tex, const DVector2 &spriteScale, int renderflags, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade, bool foggy, FDynamicColormap *basecolormap) + { + // transform the origin point + double tr_x = pos.X - thread->Viewport->viewpoint.Pos.X; + double tr_y = pos.Y - thread->Viewport->viewpoint.Pos.Y; + + double tz = tr_x * thread->Viewport->viewpoint.TanCos + tr_y * thread->Viewport->viewpoint.TanSin; + + // thing is behind view plane? + if (tz < MINZ) + return; + + double tx = tr_x * thread->Viewport->viewpoint.Sin - tr_y * thread->Viewport->viewpoint.Cos; + + // [RH] Flip for mirrors + RenderPortal *renderportal = thread->Portal.get(); + if (renderportal->MirrorFlags & RF_XFLIP) + { + tx = -tx; + } + //tx2 = tx >> 4; + + // too far off the side? + if (fabs(tx / 64) > fabs(tz)) + { + return; + } + + // [RH] Added scaling + int scaled_to = tex->GetScaledTopOffset(); + int scaled_bo = scaled_to - tex->GetScaledHeight(); + double gzt = pos.Z + spriteScale.Y * scaled_to; + double gzb = pos.Z + spriteScale.Y * scaled_bo; + + // killough 3/27/98: exclude things totally separated + // from the viewer, by either water or fake ceilings + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + + sector_t *heightsec = thing->Sector->GetHeightSec(); + + if (heightsec != nullptr) // only clip things which are in special sectors + { + if (fakeside == WaterFakeSide::AboveCeiling) + { + if (gzt < heightsec->ceilingplane.ZatPoint(pos)) + return; + } + else if (fakeside == WaterFakeSide::BelowFloor) + { + if (gzb >= heightsec->floorplane.ZatPoint(pos)) + return; + } + else + { + if (gzt < heightsec->floorplane.ZatPoint(pos)) + return; + if (!(heightsec->MoreFlags & SECF_FAKEFLOORONLY) && gzb >= heightsec->ceilingplane.ZatPoint(pos)) + return; + } + } + + auto viewport = thread->Viewport.get(); + + double xscale = viewport->CenterX / tz; + + // [RH] Reject sprites that are off the top or bottom of the screen + if (viewport->globaluclip * tz > viewport->viewpoint.Pos.Z - gzb || viewport->globaldclip * tz < viewport->viewpoint.Pos.Z - gzt) + { + return; + } + + // [RH] Flip for mirrors + renderflags ^= renderportal->MirrorFlags & RF_XFLIP; + + // calculate edges of the shape + const double thingxscalemul = spriteScale.X / tex->Scale.X; + + tx -= ((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; + double dtx1 = tx * xscale; + int x1 = viewport->viewwindow.centerx + xs_RoundToInt(dtx1); + + // off the right side? + if (x1 >= renderportal->WindowRight) + return; + + tx += tex->GetWidth() * thingxscalemul; + int x2 = viewport->viewwindow.centerx + xs_RoundToInt(tx * xscale); + + // off the left side or too small? + if ((x2 < renderportal->WindowLeft || x2 <= x1)) + return; + + xscale = spriteScale.X * xscale / tex->Scale.X; + fixed_t iscale = (fixed_t)(FRACUNIT / xscale); // Round towards zero to avoid wrapping in edge cases + + double yscale = spriteScale.Y / tex->Scale.Y; + + // store information in a vissprite + RenderSprite *vis = thread->FrameMemory->NewObject(); + + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->xscale = FLOAT2FIXED(xscale); + vis->yscale = float(viewport->InvZtoScale * yscale / tz); + vis->idepth = float(1 / tz); + vis->floorclip = thing->Floorclip / yscale; + vis->texturemid = tex->TopOffset - (viewport->viewpoint.Pos.Z - pos.Z + thing->Floorclip) / yscale; + vis->x1 = x1 < renderportal->WindowLeft ? renderportal->WindowLeft : x1; + vis->x2 = x2 > renderportal->WindowRight ? renderportal->WindowRight : x2; + //vis->Angle = thing->Angles.Yaw; + + if (renderflags & RF_XFLIP) + { + vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; + vis->xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + vis->startfrac += (fixed_t)(vis->xiscale * (vis->x1 - viewport->viewwindow.centerx + 0.5 - dtx1)); + + // killough 3/27/98: save sector for special clipping later + vis->heightsec = heightsec; + vis->sector = thing->Sector; + + vis->depth = (float)tz; + vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; + vis->gzb = (float)gzb; // [RH] use gzb, not thing->z + vis->gzt = (float)gzt; // killough 3/27/98 + vis->deltax = float(pos.X - viewport->viewpoint.Pos.X); + vis->deltay = float(pos.Y - viewport->viewpoint.Pos.Y); + vis->renderflags = renderflags; + if (thing->flags5 & MF5_BRIGHT) + vis->renderflags |= RF_FULLBRIGHT; // kg3D + vis->RenderStyle = thing->RenderStyle; + vis->FillColor = thing->fillcolor; + vis->Translation = thing->Translation; // [RH] thing translation table + vis->FakeFlatStat = fakeside; + vis->Alpha = float(thing->Alpha); + vis->fakefloor = fakefloor; + vis->fakeceiling = fakeceiling; + //vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP; + //vis->bSplitSprite = false; + + vis->pic = tex; + + vis->foggy = foggy; + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + bool invertcolormap = (vis->RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + if (vis->RenderStyle.Flags & STYLEF_InvertSource) + invertcolormap = !invertcolormap; + + if (vis->RenderStyle == LegacyRenderStyles[STYLE_Add] && basecolormap->Fade != 0) + { + basecolormap = GetSpecialLights(basecolormap->Color, 0, basecolormap->Desaturate); + } + + bool fullbright = !vis->foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT)); + bool fadeToBlack = (vis->RenderStyle.Flags & STYLEF_FadeToBlack) != 0; + + if (r_dynlights && gl_light_sprites) + { + float lit_red = 0; + float lit_green = 0; + float lit_blue = 0; + auto node = vis->sector->lighthead; + while (node != nullptr) + { + ADynamicLight *light = node->lightsource; + if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->flags4&MF4_DONTLIGHTSELF) || light->target != thing)) + { + float lx = (float)(light->X() - thing->X()); + float ly = (float)(light->Y() - thing->Y()); + float lz = (float)(light->Z() - thing->Center()); + float LdotL = lx * lx + ly * ly + lz * lz; + float radius = node->lightsource->GetRadius(); + if (radius * radius >= LdotL) + { + float distance = sqrt(LdotL); + float attenuation = 1.0f - distance / radius; + if (attenuation > 0.0f) + { + float red = light->GetRed() * (1.0f / 255.0f); + float green = light->GetGreen() * (1.0f / 255.0f); + float blue = light->GetBlue() * (1.0f / 255.0f); + /*if (light->IsSubtractive()) + { + float bright = FVector3(lr, lg, lb).Length(); + FVector3 lightColor(lr, lg, lb); + red = (bright - lr) * -1; + green = (bright - lg) * -1; + blue = (bright - lb) * -1; + }*/ + + lit_red += red * attenuation; + lit_green += green * attenuation; + lit_blue += blue * attenuation; + } + } + } + node = node->nextLight; + } + lit_red = clamp(lit_red * 255.0f, 0.0f, 255.0f); + lit_green = clamp(lit_green * 255.0f, 0.0f, 255.0f); + lit_blue = clamp(lit_blue * 255.0f, 0.0f, 255.0f); + vis->dynlightcolor = (((uint32_t)lit_red) << 16) | (((uint32_t)lit_green) << 8) | ((uint32_t)lit_blue); + } + else + { + vis->dynlightcolor = 0; + } + + vis->Light.SetColormap(LightVisibility::Instance()->SpriteGlobVis(foggy) / MAX(tz, MINZ), spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack); + + thread->SpriteList->Push(vis); + } + + void RenderSprite::Render(RenderThread *thread, short *mfloorclip, short *mceilingclip, int, int) + { + auto vis = this; + + fixed_t frac; + FTexture *tex; + int x2; + fixed_t xiscale; + + double spryscale, sprtopscreen; + bool sprflipvert; + + if (vis->xscale == 0 || fabs(vis->yscale) < (1.0f / 32000.0f)) + { // scaled to 0; can't see + return; + } + + SpriteDrawerArgs drawerargs; + drawerargs.SetLight(vis->Light.BaseColormap, 0, vis->Light.ColormapNum << FRACBITS); + drawerargs.SetDynamicLight(dynlightcolor); + + FDynamicColormap *basecolormap = static_cast(vis->Light.BaseColormap); + + bool visible = drawerargs.SetStyle(thread->Viewport.get(), vis->RenderStyle, vis->Alpha, vis->Translation, vis->FillColor, basecolormap, vis->Light.ColormapNum << FRACBITS); + + if (visible) + { + tex = vis->pic; + spryscale = vis->yscale; + sprflipvert = false; + fixed_t iscale = FLOAT2FIXED(1 / vis->yscale); + frac = vis->startfrac; + xiscale = vis->xiscale; + double texturemid = vis->texturemid; + + auto viewport = thread->Viewport.get(); + + if (vis->renderflags & RF_YFLIP) + { + sprflipvert = true; + spryscale = -spryscale; + iscale = -iscale; + texturemid -= vis->pic->GetHeight(); + sprtopscreen = viewport->CenterY + texturemid * spryscale; + } + else + { + sprflipvert = false; + sprtopscreen = viewport->CenterY - texturemid * spryscale; + } + + int x = vis->x1; + x2 = vis->x2; + + if (x < x2) + { + RenderTranslucentPass *translucentPass = thread->TranslucentPass.get(); + + while (x < x2) + { + if (!translucentPass->ClipSpriteColumnWithPortals(x, vis)) + drawerargs.DrawMaskedColumn(thread, x, iscale, tex, frac, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, false); + x++; + frac += xiscale; + } + } + } + + if (thread->MainThread) + NetUpdate(); + } +} diff --git a/src/swrenderer/things/r_sprite.h b/src/swrenderer/things/r_sprite.h new file mode 100644 index 0000000000..678c909090 --- /dev/null +++ b/src/swrenderer/things/r_sprite.h @@ -0,0 +1,38 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visiblesprite.h" + +namespace swrenderer +{ + class RenderSprite : public VisibleSprite + { + public: + static void Project(RenderThread *thread, AActor *thing, const DVector3 &pos, FTexture *tex, const DVector2 &spriteScale, int renderflags, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade, bool foggy, FDynamicColormap *basecolormap); + + protected: + void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) override; + + private: + fixed_t xscale = 0; + fixed_t startfrac = 0; // horizontal position of x1 + fixed_t xiscale = 0; // negative if flipped + + uint32_t Translation = 0; + uint32_t FillColor = 0; + + uint32_t dynlightcolor = 0; + }; +} diff --git a/src/swrenderer/things/r_visiblesprite.cpp b/src/swrenderer/things/r_visiblesprite.cpp new file mode 100644 index 0000000000..ea895321a0 --- /dev/null +++ b/src/swrenderer/things/r_visiblesprite.cpp @@ -0,0 +1,463 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "g_levellocals.h" +#include "p_maputl.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/things/r_voxel.h" +#include "swrenderer/things/r_particle.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/things/r_wallsprite.h" +#include "swrenderer/things/r_playersprite.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/line/r_renderdrawsegment.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + void VisibleSprite::Render(RenderThread *thread) + { + short *clipbot = thread->clipbot; + short *cliptop = thread->cliptop; + + VisibleSprite *spr = this; + + int i; + int x1, x2; + int r1, r2; + short topclip, botclip; + short *clip1, *clip2; + FSWColormap *colormap = spr->Light.BaseColormap; + int colormapnum = spr->Light.ColormapNum; + F3DFloor *rover; + + Clip3DFloors *clip3d = thread->Clip3D.get(); + + // [RH] Check for particles + if (spr->IsParticle()) + { + // kg3D - reject invisible parts + if ((clip3d->fake3D & FAKE3D_CLIPBOTTOM) && spr->gpos.Z <= clip3d->sclipBottom) return; + if ((clip3d->fake3D & FAKE3D_CLIPTOP) && spr->gpos.Z >= clip3d->sclipTop) return; + + spr->Render(thread, nullptr, nullptr, 0, 0); + return; + } + + x1 = spr->x1; + x2 = spr->x2; + + // [RH] Quickly reject sprites with bad x ranges. + if (x1 >= x2) + return; + + // Reject sprites outside the slice rendered by the thread + if (x2 < thread->X1 || x1 > thread->X2) + return; + + // [RH] Sprites split behind a one-sided line can also be discarded. + if (spr->sector == nullptr) + return; + + // kg3D - reject invisible parts + if ((clip3d->fake3D & FAKE3D_CLIPBOTTOM) && spr->gzt <= clip3d->sclipBottom) return; + if ((clip3d->fake3D & FAKE3D_CLIPTOP) && spr->gzb >= clip3d->sclipTop) return; + + // kg3D - correct colors now + CameraLight *cameraLight = CameraLight::Instance(); + if (!cameraLight->FixedColormap() && cameraLight->FixedLightLevel() < 0 && spr->sector->e && spr->sector->e->XFloor.lightlist.Size()) + { + if (!(clip3d->fake3D & FAKE3D_CLIPTOP)) + { + clip3d->sclipTop = spr->sector->ceilingplane.ZatPoint(thread->Viewport->viewpoint.Pos); + } + sector_t *sec = nullptr; + FDynamicColormap *mybasecolormap = nullptr; + for (i = spr->sector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) + { + if (clip3d->sclipTop <= spr->sector->e->XFloor.lightlist[i].plane.Zat0()) + { + rover = spr->sector->e->XFloor.lightlist[i].caster; + if (rover) + { + if (rover->flags & FF_DOUBLESHADOW && clip3d->sclipTop <= rover->bottom.plane->Zat0()) + { + break; + } + sec = rover->model; + if (rover->flags & FF_FADEWALLS) + { + mybasecolormap = sec->ColorMap; + } + else + { + mybasecolormap = spr->sector->e->XFloor.lightlist[i].extra_colormap; + } + } + break; + } + } + // found new values, recalculate + if (sec) + { + bool invertcolormap = (spr->RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + if (spr->RenderStyle.Flags & STYLEF_InvertSource) + invertcolormap = !invertcolormap; + + if (RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); + } + + bool isFullBright = !foggy && (renderflags & RF_FULLBRIGHT); + bool fadeToBlack = spr->RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0; + + int spriteshade = LightVisibility::LightLevelToShade(sec->lightlevel + LightVisibility::ActualExtraLight(spr->foggy, thread->Viewport.get()), foggy); + + Light.SetColormap(LightVisibility::Instance()->SpriteGlobVis(foggy) / MAX(MINZ, (double)spr->depth), spriteshade, mybasecolormap, isFullBright, invertcolormap, fadeToBlack); + } + } + + // [RH] Initialize the clipping arrays to their largest possible range + // instead of using a special "not clipped" value. This eliminates + // visual anomalies when looking down and should be faster, too. + topclip = 0; + botclip = viewheight; + + // killough 3/27/98: + // Clip the sprite against deep water and/or fake ceilings. + // [RH] rewrote this to be based on which part of the sector is really visible + + auto viewport = thread->Viewport.get(); + + double scale = viewport->InvZtoScale * spr->idepth; + double hzb = -DBL_MAX, hzt = DBL_MAX; + + if (spr->IsVoxel() && spr->floorclip != 0) + { + hzb = spr->gzb; + } + + if (spr->heightsec && !(spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) + { // only things in specially marked sectors + if (spr->FakeFlatStat != WaterFakeSide::AboveCeiling) + { + double hz = spr->heightsec->floorplane.ZatPoint(spr->gpos); + int h = xs_RoundToInt(viewport->CenterY - (hz - viewport->viewpoint.Pos.Z) * scale); + + if (spr->FakeFlatStat == WaterFakeSide::BelowFloor) + { // seen below floor: clip top + if (!spr->IsVoxel() && h > topclip) + { + topclip = short(MIN(h, viewheight)); + } + hzt = MIN(hzt, hz); + } + else + { // seen in the middle: clip bottom + if (!spr->IsVoxel() && h < botclip) + { + botclip = MAX(0, h); + } + hzb = MAX(hzb, hz); + } + } + if (spr->FakeFlatStat != WaterFakeSide::BelowFloor && !(spr->heightsec->MoreFlags & SECF_FAKEFLOORONLY)) + { + double hz = spr->heightsec->ceilingplane.ZatPoint(spr->gpos); + int h = xs_RoundToInt(viewport->CenterY - (hz - viewport->viewpoint.Pos.Z) * scale); + + if (spr->FakeFlatStat == WaterFakeSide::AboveCeiling) + { // seen above ceiling: clip bottom + if (!spr->IsVoxel() && h < botclip) + { + botclip = MAX(0, h); + } + hzb = MAX(hzb, hz); + } + else + { // seen in the middle: clip top + if (!spr->IsVoxel() && h > topclip) + { + topclip = MIN(h, viewheight); + } + hzt = MIN(hzt, hz); + } + } + } + // killough 3/27/98: end special clipping for deep water / fake ceilings + else if (!spr->IsVoxel() && spr->floorclip) + { // [RH] Move floorclip stuff from R_DrawVisSprite to here + //int clip = ((FLOAT2FIXED(CenterY) - FixedMul (spr->texturemid - (spr->pic->GetHeight() << FRACBITS) + spr->floorclip, spr->yscale)) >> FRACBITS); + int clip = xs_RoundToInt(viewport->CenterY - (spr->texturemid - spr->pic->GetHeight() + spr->floorclip) * spr->yscale); + if (clip < botclip) + { + botclip = MAX(0, clip); + } + } + + if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) + { + if (!spr->IsVoxel()) + { + double hz = clip3d->sclipBottom; + if (spr->fakefloor) + { + double floorz = spr->fakefloor->top.plane->Zat0(); + if (viewport->viewpoint.Pos.Z > floorz && floorz == clip3d->sclipBottom) + { + hz = spr->fakefloor->bottom.plane->Zat0(); + } + } + int h = xs_RoundToInt(viewport->CenterY - (hz - viewport->viewpoint.Pos.Z) * scale); + if (h < botclip) + { + botclip = MAX(0, h); + } + } + hzb = MAX(hzb, clip3d->sclipBottom); + } + if (clip3d->fake3D & FAKE3D_CLIPTOP) + { + if (!spr->IsVoxel()) + { + double hz = clip3d->sclipTop; + if (spr->fakeceiling != nullptr) + { + double ceilingZ = spr->fakeceiling->bottom.plane->Zat0(); + if (viewport->viewpoint.Pos.Z < ceilingZ && ceilingZ == clip3d->sclipTop) + { + hz = spr->fakeceiling->top.plane->Zat0(); + } + } + int h = xs_RoundToInt(viewport->CenterY - (hz - viewport->viewpoint.Pos.Z) * scale); + if (h > topclip) + { + topclip = short(MIN(h, viewheight)); + } + } + hzt = MIN(hzt, clip3d->sclipTop); + } + + if (topclip >= botclip) + { + spr->Light.BaseColormap = colormap; + spr->Light.ColormapNum = colormapnum; + return; + } + + i = x2 - x1; + clip1 = clipbot + x1; + clip2 = cliptop + x1; + do + { + *clip1++ = botclip; + *clip2++ = topclip; + } while (--i); + + // Scan drawsegs from end to start for obscuring segs. + // The first drawseg that is closer than the sprite is the clip seg. + + DrawSegmentList *segmentlist = thread->DrawSegments.get(); + for (unsigned int groupIndex = 0; groupIndex < segmentlist->SegmentGroups.Size(); groupIndex++) + { + auto &group = segmentlist->SegmentGroups[groupIndex]; + if (group.x1 >= x2 || group.x2 <= x1) + continue; + + if (group.fardepth < spr->depth) + { + r1 = MAX(group.x1, x1); + r2 = MIN(group.x2, x2); + + // Clip bottom + clip1 = clipbot + r1; + clip2 = group.sprbottomclip + r1 - group.x1; + i = r2 - r1; + do + { + if (*clip1 > *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + + // Clip top + clip1 = cliptop + r1; + clip2 = group.sprtopclip + r1 - group.x1; + i = r2 - r1; + do + { + if (*clip1 < *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + else + { + //for (unsigned int index = segmentlist->BeginIndex(); index != segmentlist->EndIndex(); index++) + for (unsigned int index = group.BeginIndex; index != group.EndIndex; index++) + { + DrawSegment *ds = segmentlist->Segment(index); + + // [ZZ] portal handling here + //if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) + // continue; + // [ZZ] WARNING: uncommenting the two above lines, totally breaks sprite clipping + + // kg3D - no clipping on fake segs + if (ds->fake) continue; + // determine if the drawseg obscures the sprite + if (ds->x1 >= x2 || ds->x2 <= x1 || + (!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == nullptr && + !ds->bFogBoundary)) + { + // does not cover sprite + continue; + } + + r1 = MAX(ds->x1, x1); + r2 = MIN(ds->x2, x2); + + float neardepth, fardepth; + if (!spr->IsWallSprite()) + { + if (ds->sz1 < ds->sz2) + { + neardepth = ds->sz1, fardepth = ds->sz2; + } + else + { + neardepth = ds->sz2, fardepth = ds->sz1; + } + } + else + { + // GCC complained about this case, is there something missing here? + fardepth = neardepth = 0; + } + + // Check if sprite is in front of draw seg: + if ((!spr->IsWallSprite() && neardepth > spr->depth) || ((spr->IsWallSprite() || fardepth > spr->depth) && + (spr->gpos.Y - ds->curline->v1->fY()) * (ds->curline->v2->fX() - ds->curline->v1->fX()) - + (spr->gpos.X - ds->curline->v1->fX()) * (ds->curline->v2->fY() - ds->curline->v1->fY()) <= 0)) + { + RenderPortal *renderportal = thread->Portal.get(); + + // seg is behind sprite, so draw the mid texture if it has one + if (ds->CurrentPortalUniq == renderportal->CurrentPortalUniq && // [ZZ] instead, portal uniq check is made here + (ds->maskedtexturecol != nullptr || ds->bFogBoundary)) + { + RenderDrawSegment renderer(thread); + renderer.Render(ds, r1, r2); + } + + continue; + } + + // clip this piece of the sprite + // killough 3/27/98: optimized and made much shorter + // [RH] Optimized further (at least for VC++; + // other compilers should be at least as good as before) + + if (ds->silhouette & SIL_BOTTOM) //bottom sil + { + clip1 = clipbot + r1; + clip2 = ds->sprbottomclip + r1 - ds->x1; + i = r2 - r1; + do + { + if (*clip1 > *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + + if (ds->silhouette & SIL_TOP) // top sil + { + clip1 = cliptop + r1; + clip2 = ds->sprtopclip + r1 - ds->x1; + i = r2 - r1; + do + { + if (*clip1 < *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + } + } + } + + // all clipping has been performed, so draw the sprite + + if (!spr->IsVoxel()) + { + spr->Render(thread, clipbot, cliptop, 0, 0); + } + else + { + // If it is completely clipped away, don't bother drawing it. + if (cliptop[x2] >= clipbot[x2]) + { + for (i = x1; i < x2; ++i) + { + if (cliptop[i] < clipbot[i]) + { + break; + } + } + if (i == x2) + { + spr->Light.BaseColormap = colormap; + spr->Light.ColormapNum = colormapnum; + return; + } + } + // Add everything outside the left and right edges to the clipping array + // for R_DrawVisVoxel(). + if (x1 > 0) + { + fillshort(cliptop, x1, viewheight); + } + if (x2 < viewwidth - 1) + { + fillshort(cliptop + x2, viewwidth - x2, viewheight); + } + int minvoxely = spr->gzt <= hzt ? 0 : xs_RoundToInt((spr->gzt - hzt) / spr->yscale); + int maxvoxely = spr->gzb > hzb ? INT_MAX : xs_RoundToInt((spr->gzt - hzb) / spr->yscale); + spr->Render(thread, cliptop, clipbot, minvoxely, maxvoxely); + } + spr->Light.BaseColormap = colormap; + spr->Light.ColormapNum = colormapnum; + } +} diff --git a/src/swrenderer/things/r_visiblesprite.h b/src/swrenderer/things/r_visiblesprite.h new file mode 100644 index 0000000000..7bd5b9de2e --- /dev/null +++ b/src/swrenderer/things/r_visiblesprite.h @@ -0,0 +1,80 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/line/r_line.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/things/r_visiblespritelist.h" + +#define MINZ double((2048*4) / double(1 << 20)) + +namespace swrenderer +{ + class RenderThread; + + class VisibleSprite + { + public: + VisibleSprite() { RenderStyle = STYLE_Normal; } + virtual ~VisibleSprite() { } + + void Render(RenderThread *thread); + + bool IsCurrentPortalUniq(int portalUniq) const { return CurrentPortalUniq == portalUniq; } + const FVector3 &WorldPos() const { return gpos; } + + double SortDist2D() const { return DVector2(deltax, deltay).LengthSquared(); } + float SortDist() const { return idepth; } + + protected: + virtual bool IsParticle() const { return false; } + virtual bool IsVoxel() const { return false; } + virtual bool IsWallSprite() const { return false; } + + virtual void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) = 0; + + FTexture *pic = nullptr; + + short x1 = 0, x2 = 0; + float gzb = 0.0f, gzt = 0.0f; // global bottom / top for silhouette clipping + + double floorclip = 0.0; + + double texturemid = 0.0; // floorclip + float yscale = 0.0f; // voxel and floorclip + + sector_t *heightsec = nullptr; // height sector for underwater/fake ceiling + WaterFakeSide FakeFlatStat = WaterFakeSide::Center; // which side of fake/floor ceiling sprite is on + + F3DFloor *fakefloor = nullptr; // 3d floor clipping + F3DFloor *fakeceiling = nullptr; + + FVector3 gpos = { 0.0f, 0.0f, 0.0f }; // origin in world coordinates + sector_t *sector = nullptr; // sector this sprite is in + + ColormapLight Light; + float Alpha = 0.0f; + FRenderStyle RenderStyle; + bool foggy = false; + short renderflags = 0; + + float depth = 0.0f; // Sort (draw segments), also light + + float deltax = 0.0f, deltay = 0.0f; // Sort (2d/voxel version) + float idepth = 0.0f; // Sort (non-voxel version) + + int CurrentPortalUniq = 0; // to identify the portal that this thing is in. used for clipping. + }; +} diff --git a/src/swrenderer/things/r_visiblespritelist.cpp b/src/swrenderer/things/r_visiblespritelist.cpp new file mode 100644 index 0000000000..eac6c91f30 --- /dev/null +++ b/src/swrenderer/things/r_visiblespritelist.cpp @@ -0,0 +1,104 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "g_levellocals.h" +#include "p_maputl.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/things/r_visiblespritelist.h" +#include "swrenderer/r_memory.h" + +namespace swrenderer +{ + void VisibleSpriteList::Clear() + { + Sprites.Clear(); + StartIndices.Clear(); + SortedSprites.Clear(); + DrewAVoxel = false; + } + + void VisibleSpriteList::PushPortal() + { + StartIndices.Push(Sprites.Size()); + } + + void VisibleSpriteList::PopPortal() + { + Sprites.Resize(StartIndices.Last()); + StartIndices.Pop(); + } + + void VisibleSpriteList::Push(VisibleSprite *sprite, bool isVoxel) + { + Sprites.Push(sprite); + if (isVoxel) + DrewAVoxel = true; + } + + void VisibleSpriteList::Sort() + { + bool compare2d = DrewAVoxel; + + unsigned int first = StartIndices.Size() == 0 ? 0 : StartIndices.Last(); + unsigned int count = Sprites.Size() - first; + + SortedSprites.Resize(count); + + if (count == 0) + return; + + if (!(i_compatflags & COMPATF_SPRITESORT)) + { + for (unsigned int i = 0; i < count; i++) + SortedSprites[i] = Sprites[first + i]; + } + else + { + // If the compatibility option is on sprites of equal distance need to + // be sorted in inverse order. This is most easily achieved by + // filling the sort array backwards before the sort. + for (unsigned int i = 0; i < count; i++) + SortedSprites[i] = Sprites[first + count - i - 1]; + } + + if (compare2d) + { + // This is an alternate version, for when one or more voxel is in view. + // It does a 2D distance test based on whichever one is furthest from + // the viewpoint. + + std::stable_sort(&SortedSprites[0], &SortedSprites[count], [](VisibleSprite *a, VisibleSprite *b) -> bool + { + return a->SortDist2D() < b->SortDist2D(); + }); + } + else + { + // This is the standard version, which does a simple test based on depth. + + std::stable_sort(&SortedSprites[0], &SortedSprites[count], [](VisibleSprite *a, VisibleSprite *b) -> bool + { + return a->SortDist() > b->SortDist(); + }); + } + } +} diff --git a/src/swrenderer/things/r_visiblespritelist.h b/src/swrenderer/things/r_visiblespritelist.h new file mode 100644 index 0000000000..2f53514fa3 --- /dev/null +++ b/src/swrenderer/things/r_visiblespritelist.h @@ -0,0 +1,37 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +namespace swrenderer +{ + struct DrawSegment; + class VisibleSprite; + + class VisibleSpriteList + { + public: + void Clear(); + void PushPortal(); + void PopPortal(); + void Push(VisibleSprite *sprite, bool isVoxel = false); + void Sort(); + + TArray SortedSprites; + + private: + TArray Sprites; + TArray StartIndices; + bool DrewAVoxel = false; + }; +} diff --git a/src/swrenderer/things/r_voxel.cpp b/src/swrenderer/things/r_voxel.cpp new file mode 100644 index 0000000000..46220d9dda --- /dev/null +++ b/src/swrenderer/things/r_voxel.cpp @@ -0,0 +1,975 @@ +// +//--------------------------------------------------------------------------- +// +// Voxel rendering +// Copyright(c) 1993 - 1997 Ken Silverman +// Copyright(c) 1998 - 2016 Randy Heit +// All rights reserved. +// +// Draw a voxel slab. This function is taken from the Build Engine. +// +// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman +// Ken Silverman's official web site: "http://www.advsys.net/ken" +// +// Permission has been obtained to use this code under the terms of +// the GNU General Public License v3.0. + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "r_data/sprites.h" +#include "d_net.h" +#include "po_man.h" +#include "r_utility.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/drawers/r_thread.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/things/r_voxel.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/viewport/r_spritedrawer.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) + +namespace swrenderer +{ + void RenderVoxel::Project(RenderThread *thread, AActor *thing, DVector3 pos, FVoxelDef *voxel, const DVector2 &spriteScale, int renderflags, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade, bool foggy, FDynamicColormap *basecolormap) + { + // transform the origin point + double tr_x = pos.X - thread->Viewport->viewpoint.Pos.X; + double tr_y = pos.Y - thread->Viewport->viewpoint.Pos.Y; + + double tz = tr_x * thread->Viewport->viewpoint.TanCos + tr_y * thread->Viewport->viewpoint.TanSin; + double tx = tr_x * thread->Viewport->viewpoint.Sin - tr_y * thread->Viewport->viewpoint.Cos; + + // [RH] Flip for mirrors + RenderPortal *renderportal = thread->Portal.get(); + if (renderportal->MirrorFlags & RF_XFLIP) + { + tx = -tx; + } + //tx2 = tx >> 4; + + // too far off the side? + if (fabs(tx / 128) > fabs(tz)) + { + return; + } + + double xscale = spriteScale.X * voxel->Scale; + double yscale = spriteScale.Y * voxel->Scale; + double piv = voxel->Voxel->Mips[0].Pivot.Z; + double gzt = pos.Z + yscale * piv - thing->Floorclip; + double gzb = pos.Z + yscale * (piv - voxel->Voxel->Mips[0].SizeZ); + if (gzt <= gzb) + return; + + // killough 3/27/98: exclude things totally separated + // from the viewer, by either water or fake ceilings + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + + sector_t *heightsec = thing->Sector->GetHeightSec(); + + if (heightsec != nullptr) // only clip things which are in special sectors + { + if (fakeside == WaterFakeSide::AboveCeiling) + { + if (gzt < heightsec->ceilingplane.ZatPoint(pos)) + return; + } + else if (fakeside == WaterFakeSide::BelowFloor) + { + if (gzb >= heightsec->floorplane.ZatPoint(pos)) + return; + } + else + { + if (gzt < heightsec->floorplane.ZatPoint(pos)) + return; + if (!(heightsec->MoreFlags & SECF_FAKEFLOORONLY) && gzb >= heightsec->ceilingplane.ZatPoint(pos)) + return; + } + } + + RenderVoxel *vis = thread->FrameMemory->NewObject(); + + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->xscale = FLOAT2FIXED(xscale); + vis->yscale = (float)yscale; + vis->x1 = renderportal->WindowLeft; + vis->x2 = renderportal->WindowRight; + vis->idepth = 1 / MINZ; + vis->floorclip = thing->Floorclip; + + pos.Z -= thing->Floorclip; + + vis->Angle = thing->Angles.Yaw + voxel->AngleOffset; + + int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin; + if (voxelspin != 0) + { + DAngle ang = double(I_FPSTime()) * voxelspin / 1000; + vis->Angle -= ang; + } + + vis->pa.vpos = { (float)thread->Viewport->viewpoint.Pos.X, (float)thread->Viewport->viewpoint.Pos.Y, (float)thread->Viewport->viewpoint.Pos.Z }; + vis->pa.vang = FAngle((float)thread->Viewport->viewpoint.Angles.Yaw.Degrees); + + // killough 3/27/98: save sector for special clipping later + vis->heightsec = heightsec; + vis->sector = thing->Sector; + + vis->depth = (float)tz; + vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; + vis->gzb = (float)gzb; // [RH] use gzb, not thing->z + vis->gzt = (float)gzt; // killough 3/27/98 + vis->deltax = float(pos.X - thread->Viewport->viewpoint.Pos.X); + vis->deltay = float(pos.Y - thread->Viewport->viewpoint.Pos.Y); + vis->renderflags = renderflags; + if (thing->flags5 & MF5_BRIGHT) + vis->renderflags |= RF_FULLBRIGHT; // kg3D + vis->RenderStyle = thing->RenderStyle; + vis->FillColor = thing->fillcolor; + vis->Translation = thing->Translation; // [RH] thing translation table + vis->FakeFlatStat = fakeside; + vis->Alpha = float(thing->Alpha); + vis->fakefloor = fakefloor; + vis->fakeceiling = fakeceiling; + vis->Light.ColormapNum = 0; + vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP; + //vis->bSplitSprite = false; + + vis->voxel = voxel->Voxel; + vis->foggy = foggy; + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + bool invertcolormap = (vis->RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + + if (vis->RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + // Sprites that are added to the scene must fade to black. + if (vis->RenderStyle == LegacyRenderStyles[STYLE_Add] && basecolormap->Fade != 0) + { + basecolormap = GetSpecialLights(basecolormap->Color, 0, basecolormap->Desaturate); + } + + bool fullbright = !vis->foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT)); + bool fadeToBlack = (vis->RenderStyle.Flags & STYLEF_FadeToBlack) != 0; + + vis->Light.SetColormap(LightVisibility::Instance()->SpriteGlobVis(foggy) / MAX(tz, MINZ), spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack); + + thread->SpriteList->Push(vis, true); + } + + void RenderVoxel::Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) + { + auto spr = this; + auto viewport = thread->Viewport.get(); + + SpriteDrawerArgs drawerargs; + drawerargs.SetLight(spr->Light.BaseColormap, 0, spr->Light.ColormapNum << FRACBITS); + + FDynamicColormap *basecolormap = (FDynamicColormap*)spr->Light.BaseColormap; + bool visible = drawerargs.SetStyle(viewport, spr->RenderStyle, spr->Alpha, spr->Translation, spr->FillColor, basecolormap); + if (!visible) + return; + + int flags = 0; + + /* + if (colfunc == fuzzcolfunc || colfunc == R_FillColumn) + { + flags = DVF_OFFSCREEN | DVF_SPANSONLY; + } + else if (colfunc != basecolfunc) + { + flags = DVF_OFFSCREEN; + } + if (flags != 0) + { + CheckOffscreenBuffer(viewport->RenderTarget->GetWidth(), viewport->RenderTarget->GetHeight(), !!(flags & DVF_SPANSONLY)); + } + if (spr->bInMirror) + { + flags |= DVF_MIRRORED; + } + */ + + // Render the voxel, either directly to the screen or offscreen. + DrawVoxel(thread, drawerargs, spr->pa.vpos, spr->pa.vang, spr->gpos, spr->Angle, + spr->xscale, FLOAT2FIXED(spr->yscale), spr->voxel, cliptop, clipbottom, + minZ, maxZ, flags); + + /* + // Blend the voxel, if that's what we need to do. + if ((flags & ~DVF_MIRRORED) != 0) + { + int pixelsize = viewport->RenderTarget->IsBgra() ? 4 : 1; + for (int x = 0; x < viewwidth; ++x) + { + if (!(flags & DVF_SPANSONLY) && (x & 3) == 0) + { + rt_initcols(OffscreenColorBuffer + x * OffscreenBufferHeight); + } + for (FCoverageBuffer::Span *span = OffscreenCoverageBuffer->Spans[x]; span != NULL; span = span->NextSpan) + { + if (flags & DVF_SPANSONLY) + { + dc_x = x; + dc_yl = span->Start; + dc_yh = span->Stop - 1; + dc_count = span->Stop - span->Start; + dc_dest = (ylookup[span->Start] + x) * pixelsize + dc_destorg; + colfunc(); + } + else + { + rt_span_coverage(x, span->Start, span->Stop - 1); + } + } + if (!(flags & DVF_SPANSONLY) && (x & 3) == 3) + { + rt_draw4cols(x - 3); + } + } + } + */ + } + + void RenderVoxel::DrawVoxel( + RenderThread *thread, SpriteDrawerArgs &drawerargs, + const FVector3 &globalpos, FAngle viewangle, const FVector3 &dasprpos, DAngle dasprang, fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, + short *daumost, short *dadmost, int minslabz, int maxslabz, int flags) + { + int i, j, k, x, y, syoff, ggxstart, ggystart, nxoff; + fixed_t cosang, sinang, sprcosang, sprsinang; + int backx, backy, gxinc, gyinc; + int daxscalerecip, dayscalerecip, cnt, gxstart, gystart, dazscale; + int lx, rx, nx, ny, x1=0, y1=0, x2=0, y2=0, yinc=0; + int yoff, xs=0, ys=0, xe, ye, xi=0, yi=0, cbackx, cbacky, dagxinc, dagyinc; + kvxslab_t *voxptr, *voxend; + FVoxelMipLevel *mip; + int z1a[64], z2a[64], yplc[64]; + + auto viewport = thread->Viewport.get(); + + const int nytooclose = viewport->viewwindow.centerxwide * 2100, nytoofar = 32768*32768 - 1048576; + const int xdimenscale = FLOAT2FIXED(viewport->viewwindow.centerxwide * viewport->YaspectMul / 160); + const double centerxwide_f = viewport->viewwindow.centerxwide; + const double centerxwidebig_f = centerxwide_f * 65536*65536*8; + + // Convert to Build's coordinate system. + fixed_t globalposx = xs_Fix<4>::ToFix(globalpos.X); + fixed_t globalposy = xs_Fix<4>::ToFix(-globalpos.Y); + fixed_t globalposz = xs_Fix<8>::ToFix(-globalpos.Z); + + fixed_t dasprx = xs_Fix<4>::ToFix(dasprpos.X); + fixed_t daspry = xs_Fix<4>::ToFix(-dasprpos.Y); + fixed_t dasprz = xs_Fix<8>::ToFix(-dasprpos.Z); + + // Shift the scales from 16 bits of fractional precision to 6. + // Also do some magic voodoo scaling to make them the right size. + daxscale = daxscale / (0xC000 >> 6); + dayscale = dayscale / (0xC000 >> 6); + if (daxscale <= 0 || dayscale <= 0) + { + // won't be visible. + return; + } + + angle_t viewang = viewangle.BAMs(); + cosang = FLOAT2FIXED(viewangle.Cos()) >> 2; + sinang = FLOAT2FIXED(-viewangle.Sin()) >> 2; + sprcosang = FLOAT2FIXED(dasprang.Cos()) >> 2; + sprsinang = FLOAT2FIXED(-dasprang.Sin()) >> 2; + + // Select mip level + i = abs(DMulScale6(dasprx - globalposx, cosang, daspry - globalposy, sinang)); + i = DivScale6(i, MIN(daxscale, dayscale)); + j = xs_Fix<13>::ToFix(viewport->FocalLengthX); + for (k = 0; i >= j && k < voxobj->NumMips; ++k) + { + i >>= 1; + } + if (k >= voxobj->NumMips) k = voxobj->NumMips - 1; + + mip = &voxobj->Mips[k]; if (mip->SlabData == NULL) return; + + minslabz >>= k; + maxslabz >>= k; + + daxscale <<= (k+8); dayscale <<= (k+8); + dazscale = FixedDiv(dayscale, FLOAT2FIXED(viewport->BaseYaspectMul)); + daxscale = fixed_t(daxscale / viewport->YaspectMul); + daxscale = Scale(daxscale, xdimenscale, viewport->viewwindow.centerxwide << 9); + dayscale = Scale(dayscale, FixedMul(xdimenscale, viewport->viewingrangerecip), viewport->viewwindow.centerxwide << 9); + + daxscalerecip = (1<<30) / daxscale; + dayscalerecip = (1<<30) / dayscale; + + fixed_t piv_x = fixed_t(mip->Pivot.X*256.); + fixed_t piv_y = fixed_t(mip->Pivot.Y*256.); + fixed_t piv_z = fixed_t(mip->Pivot.Z*256.); + + x = FixedMul(globalposx - dasprx, daxscalerecip); + y = FixedMul(globalposy - daspry, daxscalerecip); + backx = (DMulScale10(x, sprcosang, y, sprsinang) + piv_x) >> 8; + backy = (DMulScale10(y, sprcosang, x, -sprsinang) + piv_y) >> 8; + cbackx = clamp(backx, 0, mip->SizeX - 1); + cbacky = clamp(backy, 0, mip->SizeY - 1); + + sprcosang = MulScale14(daxscale, sprcosang); + sprsinang = MulScale14(daxscale, sprsinang); + + x = (dasprx - globalposx) - DMulScale18(piv_x, sprcosang, piv_y, -sprsinang); + y = (daspry - globalposy) - DMulScale18(piv_y, sprcosang, piv_x, sprsinang); + + cosang = FixedMul(cosang, dayscalerecip); + sinang = FixedMul(sinang, dayscalerecip); + + gxstart = y*cosang - x*sinang; + gystart = x*cosang + y*sinang; + gxinc = DMulScale10(sprsinang, cosang, sprcosang, -sinang); + gyinc = DMulScale10(sprcosang, cosang, sprsinang, sinang); + if ((abs(globalposz - dasprz) >> 10) >= abs(dazscale)) return; + + x = 0; y = 0; j = MAX(mip->SizeX, mip->SizeY); + fixed_t *ggxinc = (fixed_t *)alloca((j + 1) * sizeof(fixed_t) * 2); + fixed_t *ggyinc = ggxinc + (j + 1); + for (i = 0; i <= j; i++) + { + ggxinc[i] = x; x += gxinc; + ggyinc[i] = y; y += gyinc; + } + + syoff = DivScale21(globalposz - dasprz, FixedMul(dazscale, 0xE800)) + (piv_z << 7); + yoff = (abs(gxinc) + abs(gyinc)) >> 1; + + for (cnt = 0; cnt < 8; cnt++) + { + switch (cnt) + { + case 0: xs = 0; ys = 0; xi = 1; yi = 1; break; + case 1: xs = mip->SizeX-1; ys = 0; xi = -1; yi = 1; break; + case 2: xs = 0; ys = mip->SizeY-1; xi = 1; yi = -1; break; + case 3: xs = mip->SizeX-1; ys = mip->SizeY-1; xi = -1; yi = -1; break; + case 4: xs = 0; ys = cbacky; xi = 1; yi = 2; break; + case 5: xs = mip->SizeX-1; ys = cbacky; xi = -1; yi = 2; break; + case 6: xs = cbackx; ys = 0; xi = 2; yi = 1; break; + case 7: xs = cbackx; ys = mip->SizeY-1; xi = 2; yi = -1; break; + } + xe = cbackx; ye = cbacky; + if (cnt < 4) + { + if ((xi < 0) && (xe >= xs)) continue; + if ((xi > 0) && (xe <= xs)) continue; + if ((yi < 0) && (ye >= ys)) continue; + if ((yi > 0) && (ye <= ys)) continue; + } + else + { + if ((xi < 0) && (xe > xs)) continue; + if ((xi > 0) && (xe < xs)) continue; + if ((yi < 0) && (ye > ys)) continue; + if ((yi > 0) && (ye < ys)) continue; + xe += xi; ye += yi; + } + + i = sgn(ys - backy) + sgn(xs - backx) * 3 + 4; + switch(i) + { + case 6: case 7: x1 = 0; y1 = 0; break; + case 8: case 5: x1 = gxinc; y1 = gyinc; break; + case 0: case 3: x1 = gyinc; y1 = -gxinc; break; + case 2: case 1: x1 = gxinc+gyinc; y1 = gyinc-gxinc; break; + } + switch(i) + { + case 2: case 5: x2 = 0; y2 = 0; break; + case 0: case 1: x2 = gxinc; y2 = gyinc; break; + case 8: case 7: x2 = gyinc; y2 = -gxinc; break; + case 6: case 3: x2 = gxinc+gyinc; y2 = gyinc-gxinc; break; + } + uint8_t oand = (1 << int(xs 0) { dagxinc = gxinc; dagyinc = FixedMul(gyinc, viewport->viewingrangerecip); } + else { dagxinc = -gxinc; dagyinc = -FixedMul(gyinc, viewport->viewingrangerecip); } + + /* Fix for non 90 degree viewing ranges */ + nxoff = FixedMul(x2 - x1, viewport->viewingrangerecip); + x1 = FixedMul(x1, viewport->viewingrangerecip); + + ggxstart = gxstart + ggyinc[ys]; + ggystart = gystart - ggxinc[ys]; + + for (x = xs; x != xe; x += xi) + { + uint8_t *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; + short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)]; + + nx = FixedMul(ggxstart + ggxinc[x], viewport->viewingrangerecip) + x1; + ny = ggystart + ggyinc[x]; + for (y = ys; y != ye; y += yi, nx += dagyinc, ny -= dagxinc) + { + if ((ny <= nytooclose) || (ny >= nytoofar)) continue; + voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]); + voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]); + if (voxptr >= voxend) continue; + + lx = xs_RoundToInt(nx * centerxwide_f / (ny + y1)) + viewport->viewwindow.centerx; + if (lx < 0) lx = 0; + rx = xs_RoundToInt((nx + nxoff) * centerxwide_f / (ny + y2)) + viewport->viewwindow.centerx; + if (rx > viewwidth) rx = viewwidth; + if (rx <= lx) continue; + + if (flags & DVF_MIRRORED) + { + int t = viewwidth - lx; + lx = viewwidth - rx; + rx = t; + } + + fixed_t l1 = xs_RoundToInt(centerxwidebig_f / (ny - yoff)); + fixed_t l2 = xs_RoundToInt(centerxwidebig_f / (ny + yoff)); + for (; voxptr < voxend; voxptr = (kvxslab_t *)((uint8_t *)voxptr + voxptr->zleng + 3)) + { + const uint8_t *col = voxptr->col; + int zleng = voxptr->zleng; + int ztop = voxptr->ztop; + fixed_t z1, z2; + + if (ztop < minslabz) + { + int diff = minslabz - ztop; + ztop = minslabz; + col += diff; + zleng -= diff; + } + if (ztop + zleng > maxslabz) + { + int diff = ztop + zleng - maxslabz; + zleng -= diff; + } + if (zleng <= 0) continue; + + j = (ztop << 15) - syoff; + if (j < 0) + { + k = j + (zleng << 15); + if (k < 0) + { + if ((voxptr->backfacecull & oand32) == 0) continue; + z2 = MulScale32(l2, k) + viewport->viewwindow.centery; /* Below slab */ + } + else + { + if ((voxptr->backfacecull & oand) == 0) continue; /* Middle of slab */ + z2 = MulScale32(l1, k) + viewport->viewwindow.centery; + } + z1 = MulScale32(l1, j) + viewport->viewwindow.centery; + } + else + { + if ((voxptr->backfacecull & oand16) == 0) continue; + z1 = MulScale32(l2, j) + viewport->viewwindow.centery; /* Above slab */ + z2 = MulScale32(l1, j + (zleng << 15)) + viewport->viewwindow.centery; + } + + if (z2 <= z1) continue; + + if (zleng == 1) + { + yinc = 0; + } + else + { + if (z2-z1 >= 1024) yinc = FixedDiv(zleng, z2 - z1); + else yinc = (((1 << 24) - 1) / (z2 - z1)) * zleng >> 8; + } + // [RH] Clip each column separately, not just by the first one. + for (int stripwidth = MIN(countof(z1a), rx - lx), lxt = lx; + lxt < rx; + (lxt += countof(z1a)), stripwidth = MIN(countof(z1a), rx - lxt)) + { + // Calculate top and bottom pixels locations + for (int xxx = 0; xxx < stripwidth; ++xxx) + { + if (zleng == 1) + { + yplc[xxx] = 0; + z1a[xxx] = MAX(z1, daumost[lxt + xxx]); + } + else + { + if (z1 < daumost[lxt + xxx]) + { + yplc[xxx] = yinc * (daumost[lxt + xxx] - z1); + z1a[xxx] = daumost[lxt + xxx]; + } + else + { + yplc[xxx] = 0; + z1a[xxx] = z1; + } + } + z2a[xxx] = MIN(z2, dadmost[lxt + xxx]); + } + + const uint8_t *columnColors = col; + if (!drawerargs.DrawerNeedsPalInput() && viewport->RenderTarget->IsBgra()) + { + // The true color slab data array is identical, except its using uint32_t instead of uint8. + // + // We can find the same slab column by calculating the offset from the start of SlabData + // and use that to offset into the BGRA version of the same data. + columnColors = (const uint8_t *)(&mip->SlabDataBgra[0] + (ptrdiff_t)(col - mip->SlabData)); + } + + // Find top and bottom pixels that match and draw them as one strip + for (int xxl = 0, xxr; xxl < stripwidth; ) + { + if (z1a[xxl] >= z2a[xxl]) + { // No column here + xxl++; + continue; + } + int z1 = z1a[xxl]; + int z2 = z2a[xxl]; + // How many columns share the same extents? + for (xxr = xxl + 1; xxr < stripwidth; ++xxr) + { + if (z1a[xxr] != z1 || z2a[xxr] != z2) + break; + } + + for (int x = xxl; x < xxr; ++x) + { + drawerargs.SetDest(viewport, lxt + x, z1); + drawerargs.SetCount(z2 - z1); + drawerargs.DrawVoxelColumn(thread, yplc[xxl], yinc, columnColors, zleng); + } + + /* + if (!(flags & DVF_OFFSCREEN)) + { + // Draw directly to the screen. + R_DrawSlab(xxr - xxl, yplc[xxl], z2 - z1, yinc, col, (ylookup[z1] + lxt + xxl) * pixelsize + dc_destorg); + } + else + { + // Record the area covered and possibly draw to an offscreen buffer. + dc_yl = z1; + dc_yh = z2 - 1; + dc_count = z2 - z1; + dc_iscale = yinc; + for (int x = xxl; x < xxr; ++x) + { + OffscreenCoverageBuffer->InsertSpan(lxt + x, z1, z2); + if (!(flags & DVF_SPANSONLY)) + { + dc_x = lxt + x; + rt_initcols(OffscreenColorBuffer + (dc_x & ~3) * OffscreenBufferHeight); + dc_source = col; + dc_source2 = nullptr; + dc_texturefrac = yplc[xxl]; + hcolfunc_pre(); + } + } + } + */ + + xxl = xxr; + } + } + } + } + } + } + } + + kvxslab_t *RenderVoxel::GetSlabStart(const FVoxelMipLevel &mip, int x, int y) + { + return (kvxslab_t *)&mip.SlabData[mip.OffsetX[x] + (int)mip.OffsetXY[x * (mip.SizeY + 1) + y]]; + } + + kvxslab_t *RenderVoxel::GetSlabEnd(const FVoxelMipLevel &mip, int x, int y) + { + return GetSlabStart(mip, x, y + 1); + } + + kvxslab_t *RenderVoxel::NextSlab(kvxslab_t *slab) + { + return (kvxslab_t*)(((uint8_t*)slab) + 3 + slab->zleng); + } + + void RenderVoxel::Deinit() + { + // Free offscreen buffer + if (OffscreenColorBuffer != nullptr) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = nullptr; + } + if (OffscreenCoverageBuffer != nullptr) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = nullptr; + } + OffscreenBufferHeight = OffscreenBufferWidth = 0; + } + + void RenderVoxel::CheckOffscreenBuffer(int width, int height, bool spansonly) + { + // Allocates the offscreen coverage buffer and optionally the offscreen + // color buffer. If they already exist but are the wrong size, they will + // be reallocated. + + if (OffscreenCoverageBuffer == nullptr) + { + assert(OffscreenColorBuffer == nullptr && "The color buffer cannot exist without the coverage buffer"); + OffscreenCoverageBuffer = new FCoverageBuffer(width); + } + else if (OffscreenCoverageBuffer->NumLists != (unsigned)width) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = new FCoverageBuffer(width); + if (OffscreenColorBuffer != nullptr) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = nullptr; + } + } + else + { + OffscreenCoverageBuffer->Clear(); + } + + if (!spansonly) + { + if (OffscreenColorBuffer == nullptr) + { + OffscreenColorBuffer = new uint8_t[width * height * 4]; + } + else if (OffscreenBufferWidth != width || OffscreenBufferHeight != height) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = new uint8_t[width * height * 4]; + } + } + OffscreenBufferWidth = width; + OffscreenBufferHeight = height; + } + + FCoverageBuffer *RenderVoxel::OffscreenCoverageBuffer; + int RenderVoxel::OffscreenBufferWidth; + int RenderVoxel::OffscreenBufferHeight; + uint8_t *RenderVoxel::OffscreenColorBuffer; + + //////////////////////////////////////////////////////////////////////////// + + FCoverageBuffer::FCoverageBuffer(int lists) + : Spans(nullptr), FreeSpans(nullptr) + { + NumLists = lists; + Spans = new Span *[lists]; + memset(Spans, 0, sizeof(Span*)*lists); + } + + FCoverageBuffer::~FCoverageBuffer() + { + if (Spans != nullptr) + { + delete[] Spans; + } + } + + void FCoverageBuffer::Clear() + { + SpanArena.FreeAll(); + memset(Spans, 0, sizeof(Span*)*NumLists); + FreeSpans = nullptr; + } + + void FCoverageBuffer::InsertSpan(int listnum, int start, int stop) + { + // start is inclusive. + // stop is exclusive. + + assert(unsigned(listnum) < NumLists); + assert(start < stop); + + Span **span_p = &Spans[listnum]; + Span *span; + + if (*span_p == nullptr || (*span_p)->Start > stop) + { // This list is empty or the first entry is after this one, so we can just insert the span. + goto addspan; + } + + // Insert the new span in order, merging with existing ones. + while (*span_p != nullptr) + { + if ((*span_p)->Stop < start) // ===== (existing span) + { // Span ends before this one starts. // ++++ (new span) + span_p = &(*span_p)->NextSpan; + continue; + } + + // Does the new span overlap or abut the existing one? + if ((*span_p)->Start <= start) + { + if ((*span_p)->Stop >= stop) // ============= + { // The existing span completely covers this one. // +++++ + return; + } + extend: // Extend the existing span with the new one. // ====== + span = *span_p; // +++++++ + span->Stop = stop; // (or) +++++ + + // Free up any spans we just covered up. + span_p = &(*span_p)->NextSpan; + while (*span_p != nullptr && (*span_p)->Start <= stop && (*span_p)->Stop <= stop) + { + Span *span = *span_p; // ====== ====== + *span_p = span->NextSpan; // +++++++++++++ + span->NextSpan = FreeSpans; + FreeSpans = span; + } + if (*span_p != nullptr && (*span_p)->Start <= stop) // ======= ======== + { // Our new span connects two existing spans. // ++++++++++++++ + // They should all be collapsed into a single span. + span->Stop = (*span_p)->Stop; + span = *span_p; + *span_p = span->NextSpan; + span->NextSpan = FreeSpans; + FreeSpans = span; + } + goto check; + } + else if ((*span_p)->Start <= stop) // ===== + { // The new span extends the existing span from // ++++ + // the beginning. // (or) ++++ + (*span_p)->Start = start; + if ((*span_p)->Stop < stop) + { // The new span also extends the existing span // ====== + // at the bottom // ++++++++++++++ + goto extend; + } + goto check; + } + else // ====== + { // No overlap, so insert a new span. // +++++ + goto addspan; + } + } + // Append a new span to the end of the list. + addspan: + span = AllocSpan(); + span->NextSpan = *span_p; + span->Start = start; + span->Stop = stop; + *span_p = span; + check: +#ifdef _DEBUG + // Validate the span list: Spans must be in order, and there must be + // at least one pixel between spans. + for (span = Spans[listnum]; span != nullptr; span = span->NextSpan) + { + assert(span->Start < span->Stop); + if (span->NextSpan != nullptr) + { + assert(span->Stop < span->NextSpan->Start); + } + } +#endif + ; + } + + FCoverageBuffer::Span *FCoverageBuffer::AllocSpan() + { + Span *span; + + if (FreeSpans != nullptr) + { + span = FreeSpans; + FreeSpans = span->NextSpan; + } + else + { + span = (Span *)SpanArena.Alloc(sizeof(Span)); + } + return span; + } + +#if 0 + void RenderVoxel::Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) + { + auto sprite = this; + auto viewport = RenderViewport::Instance(); + + FDynamicColormap *basecolormap = static_cast(sprite->Light.BaseColormap); + + SpriteDrawerArgs drawerargs; + drawerargs.SetLight(sprite->Light.BaseColormap, 0, sprite->Light.ColormapNum << FRACBITS); + + bool visible = drawerargs.SetStyle(sprite->RenderStyle, sprite->Alpha, sprite->Translation, sprite->FillColor, basecolormap); + if (!visible) + return; + + DVector3 view_origin = { sprite->pa.vpos.X, sprite->pa.vpos.Y, sprite->pa.vpos.Z }; + FAngle view_angle = sprite->pa.vang; + DVector3 sprite_origin = { sprite->gpos.X, sprite->gpos.Y, sprite->gpos.Z }; + DAngle sprite_angle = sprite->Angle; + double sprite_xscale = FIXED2DBL(sprite->xscale); + double sprite_yscale = sprite->yscale; + FVoxel *voxel = sprite->voxel; + + // Select mipmap level: + + double viewSin = view_angle.Cos(); + double viewCos = view_angle.Sin(); + double logmip = fabs((view_origin.X - sprite_origin.X) * viewCos - (view_origin.Y - sprite_origin.Y) * viewSin); + int miplevel = 0; + while (miplevel < voxel->NumMips - 1 && logmip >= viewport->FocalLengthX) + { + logmip *= 0.5; + miplevel++; + } + + const FVoxelMipLevel &mip = voxel->Mips[miplevel]; + if (mip.SlabData == nullptr) + return; + + minZ >>= miplevel; + maxZ >>= miplevel; + sprite_xscale *= (1 << miplevel); + sprite_yscale *= (1 << miplevel); + + // Find voxel cube eigenvectors and origin in world space: + + double spriteSin = sprite_angle.Sin(); + double spriteCos = sprite_angle.Cos(); + + DVector2 dirX(spriteSin * sprite_xscale, -spriteCos * sprite_xscale); + DVector2 dirY(spriteCos * sprite_xscale, spriteSin * sprite_xscale); + double dirZ = -sprite_yscale; + + DVector3 voxel_origin = sprite_origin; + voxel_origin.X -= dirX.X * mip.Pivot.X + dirX.Y * mip.Pivot.Y; + voxel_origin.Y -= dirY.X * mip.Pivot.X + dirY.Y * mip.Pivot.Y; + voxel_origin.Z -= dirZ * mip.Pivot.Z; + + // Voxel cube walking directions: + + int startX[4] = { 0, mip.SizeX - 1, 0, mip.SizeX - 1 }; + int startY[4] = { 0, 0, mip.SizeY - 1, mip.SizeY - 1 }; + int stepX[4] = { 1, -1, 1, -1 }; + int stepY[4] = { 1, 1, -1, -1 }; + + // The point in cube mipmap local space where voxel sides change from front to backfacing: + + double dx = (view_origin.X - sprite_origin.X) / sprite_xscale; + double dy = (view_origin.Y - sprite_origin.Y) / sprite_xscale; + int backX = (int)(dx * spriteCos - dy * spriteSin + mip.Pivot.X); + int backY = (int)(dy * spriteCos + dx * spriteSin + mip.Pivot.Y); + //int endX = clamp(backX, 0, mip.SizeX - 1); + //int endY = clamp(backY, 0, mip.SizeY - 1); + int endX = mip.SizeX - 1;// clamp(backX, 0, mip.SizeX - 1); + int endY = mip.SizeY - 1;// clamp(backY, 0, mip.SizeY - 1); + + // Draw the voxel cube: + + for (int index = 0; index < 1; index++) + { + /*if ((stepX[index] < 0 && endX >= startX[index]) || + (stepX[index] > 0 && endX <= startX[index]) || + (stepY[index] < 0 && endY >= startY[index]) || + (stepY[index] > 0 && endY <= startY[index])) continue;*/ + + for (int x = startX[index]; x != endX; x += stepX[index]) + { + for (int y = startY[index]; y != endY; y += stepY[index]) + { + kvxslab_t *slab_start = GetSlabStart(mip, x, y); + kvxslab_t *slab_end = GetSlabEnd(mip, x, y); + + for (kvxslab_t *slab = slab_start; slab != slab_end; slab = NextSlab(slab)) + { + // To do: check slab->backfacecull + + int ztop = slab->ztop; + int zbottom = ztop + slab->zleng; + + //ztop = MAX(ztop, minZ); + //zbottom = MIN(zbottom, maxZ); + + for (int z = ztop; z < zbottom; z++) + { + uint8_t color = slab->col[z - slab->ztop]; + + DVector3 voxel_pos = voxel_origin; + voxel_pos.X += dirX.X * x + dirX.Y * y; + voxel_pos.Y += dirY.X * x + dirY.Y * y; + voxel_pos.Z += dirZ * z; + + FillBox(thread, drawerargs, voxel_pos, sprite_xscale, sprite_yscale, color, cliptop, clipbottom, false, false); + } + } + } + } + } + } + + void RenderVoxel::FillBox(RenderThread *thread, SpriteDrawerArgs &drawerargs, DVector3 origin, double extentX, double extentY, int color, short *cliptop, short *clipbottom, bool viewspace, bool pixelstretch) + { + auto viewport = RenderViewport::Instance(); + + DVector3 viewPos = viewport->PointWorldToView(origin); + + if (viewPos.Z < 0.01f) + return; + + DVector3 screenPos = viewport->PointViewToScreen(viewPos); + DVector2 screenExtent = viewport->ScaleViewToScreen({ extentX, extentY }, viewPos.Z, pixelstretch); + + int x1 = MAX((int)(screenPos.X - screenExtent.X), 0); + int x2 = MIN((int)(screenPos.X + screenExtent.X + 0.5f), viewwidth - 1); + int y1 = MAX((int)(screenPos.Y - screenExtent.Y), 0); + int y2 = MIN((int)(screenPos.Y + screenExtent.Y + 0.5f), viewheight - 1); + + int pixelsize = viewport->RenderTarget->IsBgra() ? 4 : 1; + + if (y1 < y2) + { + for (int x = x1; x < x2; x++) + { + int columnY1 = MAX(y1, (int)cliptop[x]); + int columnY2 = MIN(y2, (int)clipbottom[x]); + if (columnY1 < columnY2) + { + drawerargs.SetDest(x, columnY1); + drawerargs.SetSolidColor(color); + drawerargs.SetCount(columnY2 - columnY1); + drawerargs.FillColumn(thread); + } + } + } + } +#endif +} diff --git a/src/swrenderer/things/r_voxel.h b/src/swrenderer/things/r_voxel.h new file mode 100644 index 0000000000..e8bbe64cd3 --- /dev/null +++ b/src/swrenderer/things/r_voxel.h @@ -0,0 +1,109 @@ +// +//--------------------------------------------------------------------------- +// +// Voxel rendering +// Copyright(c) 1993 - 1997 Ken Silverman +// Copyright(c) 1998 - 2016 Randy Heit +// All rights reserved. +// +// 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 3 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, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#pragma once + +#include "r_visiblesprite.h" + +struct kvxslab_t; +struct FVoxelMipLevel; +struct FVoxel; + +namespace swrenderer +{ + class SpriteDrawerArgs; + + // [RH] A c-buffer. Used for keeping track of offscreen voxel spans. + struct FCoverageBuffer + { + struct Span + { + Span *NextSpan; + short Start, Stop; + }; + + FCoverageBuffer(int size); + ~FCoverageBuffer(); + + void Clear(); + void InsertSpan(int listnum, int start, int stop); + Span *AllocSpan(); + + FMemArena SpanArena; + Span **Spans; // [0..NumLists-1] span lists + Span *FreeSpans; + unsigned int NumLists; + }; + + class RenderVoxel : public VisibleSprite + { + public: + static void Project(RenderThread *thread, AActor *thing, DVector3 pos, FVoxelDef *voxel, const DVector2 &spriteScale, int renderflags, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade, bool foggy, FDynamicColormap *basecolormap); + + static void Deinit(); + + protected: + bool IsVoxel() const override { return true; } + void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) override; + + private: + struct posang + { + FVector3 vpos = { 0.0f, 0.0f, 0.0f }; // view origin + FAngle vang = { 0.0f }; // view angle + }; + + posang pa; + DAngle Angle = { 0.0 }; + fixed_t xscale = 0; + FVoxel *voxel = nullptr; + bool bInMirror = false; + + uint32_t Translation = 0; + uint32_t FillColor = 0; + + enum { DVF_OFFSCREEN = 1, DVF_SPANSONLY = 2, DVF_MIRRORED = 4 }; + + static kvxslab_t *GetSlabStart(const FVoxelMipLevel &mip, int x, int y); + static kvxslab_t *GetSlabEnd(const FVoxelMipLevel &mip, int x, int y); + static kvxslab_t *NextSlab(kvxslab_t *slab); + + static void CheckOffscreenBuffer(int width, int height, bool spansonly); + + static FCoverageBuffer *OffscreenCoverageBuffer; + static int OffscreenBufferWidth; + static int OffscreenBufferHeight; + static uint8_t *OffscreenColorBuffer; + + void DrawVoxel( + RenderThread *thread, SpriteDrawerArgs &drawerargs, + const FVector3 &globalpos, FAngle viewangle, const FVector3 &dasprpos, DAngle dasprang, fixed_t daxscale, fixed_t dayscale, + FVoxel *voxobj, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags); + + int sgn(int v) + { + return v < 0 ? -1 : v > 0 ? 1 : 0; + } + }; +} diff --git a/src/swrenderer/things/r_wallsprite.cpp b/src/swrenderer/things/r_wallsprite.cpp new file mode 100644 index 0000000000..1c6cdfa59f --- /dev/null +++ b/src/swrenderer/things/r_wallsprite.cpp @@ -0,0 +1,263 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "swrenderer/things/r_wallsprite.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "v_video.h" +#include "sc_man.h" +#include "s_sound.h" +#include "sbar.h" +#include "gi.h" +#include "r_sky.h" +#include "cmdlib.h" +#include "g_level.h" +#include "d_net.h" +#include "colormatcher.h" +#include "d_netinf.h" +#include "p_effect.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "v_palette.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "p_local.h" +#include "p_maputl.h" +#include "r_voxel.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + void RenderWallSprite::Project(RenderThread *thread, AActor *thing, const DVector3 &pos, FTextureID picnum, const DVector2 &scale, int renderflags, int spriteshade, bool foggy, FDynamicColormap *basecolormap) + { + FWallCoords wallc; + double x1, x2; + DVector2 left, right; + double gzb, gzt, tz; + FTexture *pic = TexMan(picnum, true); + DAngle ang = thing->Angles.Yaw + 90; + double angcos = ang.Cos(); + double angsin = ang.Sin(); + + // Determine left and right edges of sprite. The sprite's angle is its normal, + // so the edges are 90 degrees each side of it. + x2 = pic->GetScaledWidth(); + x1 = pic->GetScaledLeftOffset(); + + x1 *= scale.X; + x2 *= scale.X; + + left.X = pos.X - x1 * angcos - thread->Viewport->viewpoint.Pos.X; + left.Y = pos.Y - x1 * angsin - thread->Viewport->viewpoint.Pos.Y; + right.X = left.X + x2 * angcos; + right.Y = right.Y + x2 * angsin; + + // Is it off-screen? + if (wallc.Init(thread, left, right, TOO_CLOSE_Z)) + return; + + RenderPortal *renderportal = thread->Portal.get(); + + if (wallc.sx1 >= renderportal->WindowRight || wallc.sx2 <= renderportal->WindowLeft) + return; + + // Sprite sorting should probably treat these as walls, not sprites, + // but right now, I just want to get them drawing. + tz = (pos.X - thread->Viewport->viewpoint.Pos.X) * thread->Viewport->viewpoint.TanCos + (pos.Y - thread->Viewport->viewpoint.Pos.Y) * thread->Viewport->viewpoint.TanSin; + + int scaled_to = pic->GetScaledTopOffset(); + int scaled_bo = scaled_to - pic->GetScaledHeight(); + gzt = pos.Z + scale.Y * scaled_to; + gzb = pos.Z + scale.Y * scaled_bo; + + RenderWallSprite *vis = thread->FrameMemory->NewObject(); + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->x1 = wallc.sx1 < renderportal->WindowLeft ? renderportal->WindowLeft : wallc.sx1; + vis->x2 = wallc.sx2 >= renderportal->WindowRight ? renderportal->WindowRight : wallc.sx2; + vis->yscale = (float)scale.Y; + vis->idepth = float(1 / tz); + vis->depth = (float)tz; + vis->sector = thing->Sector; + vis->heightsec = NULL; + vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; + vis->gzb = (float)gzb; + vis->gzt = (float)gzt; + vis->deltax = float(pos.X - thread->Viewport->viewpoint.Pos.X); + vis->deltay = float(pos.Y - thread->Viewport->viewpoint.Pos.Y); + vis->renderflags = renderflags; + if (thing->flags5 & MF5_BRIGHT) vis->renderflags |= RF_FULLBRIGHT; // kg3D + vis->RenderStyle = thing->RenderStyle; + vis->FillColor = thing->fillcolor; + vis->Translation = thing->Translation; + vis->FakeFlatStat = WaterFakeSide::Center; + vis->Alpha = float(thing->Alpha); + vis->fakefloor = NULL; + vis->fakeceiling = NULL; + //vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP; + vis->pic = pic; + vis->wallc = wallc; + vis->foggy = foggy; + + vis->Light.SetColormap(LightVisibility::Instance()->SpriteGlobVis(foggy) / MAX(tz, MINZ), spriteshade, basecolormap, false, false, false); + + thread->SpriteList->Push(vis); + } + + void RenderWallSprite::Render(RenderThread *thread, short *mfloorclip, short *mceilingclip, int, int) + { + auto spr = this; + + int x1, x2; + double iyscale; + bool sprflipvert; + + x1 = MAX(spr->x1, spr->wallc.sx1); + x2 = MIN(spr->x2, spr->wallc.sx2); + if (x1 >= x2) + return; + + FWallTmapVals WallT; + WallT.InitFromWallCoords(thread, &spr->wallc); + + ProjectedWallTexcoords walltexcoords; + walltexcoords.Project(thread->Viewport.get(), spr->pic->GetWidth() << FRACBITS, x1, x2, WallT); + + iyscale = 1 / spr->yscale; + double texturemid = (spr->gzt - thread->Viewport->viewpoint.Pos.Z) * iyscale; + if (spr->renderflags & RF_XFLIP) + { + int right = (spr->pic->GetWidth() << FRACBITS) - 1; + + for (int i = x1; i < x2; i++) + { + walltexcoords.UPos[i] = right - walltexcoords.UPos[i]; + } + } + // Prepare lighting + bool calclighting = false; + FSWColormap *usecolormap = spr->Light.BaseColormap; + bool rereadcolormap = true; + + // Decals that are added to the scene must fade to black. + if (spr->RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) + { + usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); + rereadcolormap = false; + } + + SpriteDrawerArgs drawerargs; + + int shade = LightVisibility::LightLevelToShade(spr->sector->lightlevel + LightVisibility::ActualExtraLight(spr->foggy, thread->Viewport.get()), spr->foggy); + double GlobVis = LightVisibility::Instance()->WallGlobVis(foggy); + float lightleft = float(GlobVis / spr->wallc.sz1); + float lightstep = float((GlobVis / spr->wallc.sz2 - lightleft) / (spr->wallc.sx2 - spr->wallc.sx1)); + float light = lightleft + (x1 - spr->wallc.sx1) * lightstep; + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight(usecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != NULL) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + else if (!spr->foggy && (spr->renderflags & RF_FULLBRIGHT)) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, 0); + else + calclighting = true; + + // Draw it + FTexture *WallSpriteTile = spr->pic; + if (spr->renderflags & RF_YFLIP) + { + sprflipvert = true; + iyscale = -iyscale; + texturemid -= spr->pic->GetHeight(); + } + else + { + sprflipvert = false; + } + + float maskedScaleY = (float)iyscale; + + int x = x1; + + FDynamicColormap *basecolormap = static_cast(spr->Light.BaseColormap); + + bool visible = drawerargs.SetStyle(thread->Viewport.get(), spr->RenderStyle, spr->Alpha, spr->Translation, spr->FillColor, basecolormap); + + // R_SetPatchStyle can modify basecolormap. + if (rereadcolormap) + { + usecolormap = spr->Light.BaseColormap; + } + + if (!visible) + { + return; + } + else + { + RenderTranslucentPass *translucentPass = thread->TranslucentPass.get(); + + while (x < x2) + { + if (calclighting) + { // calculate lighting + drawerargs.SetLight(usecolormap, light, shade); + } + if (!translucentPass->ClipSpriteColumnWithPortals(x, spr)) + DrawColumn(thread, drawerargs, x, WallSpriteTile, walltexcoords, texturemid, maskedScaleY, sprflipvert, mfloorclip, mceilingclip); + light += lightstep; + x++; + } + } + } + + void RenderWallSprite::DrawColumn(RenderThread *thread, SpriteDrawerArgs &drawerargs, int x, FTexture *WallSpriteTile, const ProjectedWallTexcoords &walltexcoords, double texturemid, float maskedScaleY, bool sprflipvert, const short *mfloorclip, const short *mceilingclip) + { + auto viewport = thread->Viewport.get(); + + float iscale = walltexcoords.VStep[x] * maskedScaleY; + double spryscale = 1 / iscale; + double sprtopscreen; + if (sprflipvert) + sprtopscreen = viewport->CenterY + texturemid * spryscale; + else + sprtopscreen = viewport->CenterY - texturemid * spryscale; + + drawerargs.DrawMaskedColumn(thread, x, FLOAT2FIXED(iscale), WallSpriteTile, walltexcoords.UPos[x], spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip); + } +} diff --git a/src/swrenderer/things/r_wallsprite.h b/src/swrenderer/things/r_wallsprite.h new file mode 100644 index 0000000000..76bbdc6c03 --- /dev/null +++ b/src/swrenderer/things/r_wallsprite.h @@ -0,0 +1,39 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visiblesprite.h" + +namespace swrenderer +{ + class ProjectedWallTexcoords; + class SpriteDrawerArgs; + + class RenderWallSprite : public VisibleSprite + { + public: + static void Project(RenderThread *thread, AActor *thing, const DVector3 &pos, FTextureID picnum, const DVector2 &scale, int renderflags, int spriteshade, bool foggy, FDynamicColormap *basecolormap); + + protected: + bool IsWallSprite() const override { return true; } + void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) override; + + private: + static void DrawColumn(RenderThread *thread, SpriteDrawerArgs &drawerargs, int x, FTexture *WallSpriteTile, const ProjectedWallTexcoords &walltexcoords, double texturemid, float maskedScaleY, bool sprflipvert, const short *mfloorclip, const short *mceilingclip); + + FWallCoords wallc; + uint32_t Translation = 0; + uint32_t FillColor = 0; + }; +} diff --git a/src/swrenderer/viewport/r_drawerargs.cpp b/src/swrenderer/viewport/r_drawerargs.cpp new file mode 100644 index 0000000000..78cc3ea8ff --- /dev/null +++ b/src/swrenderer/viewport/r_drawerargs.cpp @@ -0,0 +1,77 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "r_drawerargs.h" + +namespace swrenderer +{ + void DrawerArgs::SetLight(FSWColormap *base_colormap, float light, int shade) + { + mBaseColormap = base_colormap; + mLight = light; + mShade = shade; + } + + void DrawerArgs::SetTranslationMap(lighttable_t *translation) + { + mTranslation = translation; + } + + uint8_t *DrawerArgs::Colormap(RenderViewport *viewport) const + { + if (mBaseColormap) + { + if (viewport->RenderTarget->IsBgra()) + return mBaseColormap->Maps; + else + return mBaseColormap->Maps + (GETPALOOKUP(mLight, mShade) << COLORMAPSHIFT); + } + else + { + return mTranslation; + } + } + + ShadeConstants DrawerArgs::ColormapConstants() const + { + ShadeConstants shadeConstants; + if (mBaseColormap) + { + shadeConstants.light_red = mBaseColormap->Color.r * 256 / 255; + shadeConstants.light_green = mBaseColormap->Color.g * 256 / 255; + shadeConstants.light_blue = mBaseColormap->Color.b * 256 / 255; + shadeConstants.light_alpha = mBaseColormap->Color.a * 256 / 255; + shadeConstants.fade_red = mBaseColormap->Fade.r; + shadeConstants.fade_green = mBaseColormap->Fade.g; + shadeConstants.fade_blue = mBaseColormap->Fade.b; + shadeConstants.fade_alpha = mBaseColormap->Fade.a; + shadeConstants.desaturate = MIN(abs(mBaseColormap->Desaturate), 255) * 255 / 256; + shadeConstants.simple_shade = (mBaseColormap->Color.d == 0x00ffffff && mBaseColormap->Fade.d == 0x00000000 && mBaseColormap->Desaturate == 0); + } + else + { + shadeConstants.light_red = 256; + shadeConstants.light_green = 256; + shadeConstants.light_blue = 256; + shadeConstants.light_alpha = 256; + shadeConstants.fade_red = 0; + shadeConstants.fade_green = 0; + shadeConstants.fade_blue = 0; + shadeConstants.fade_alpha = 256; + shadeConstants.desaturate = 0; + shadeConstants.simple_shade = true; + } + return shadeConstants; + } +} diff --git a/src/swrenderer/viewport/r_drawerargs.h b/src/swrenderer/viewport/r_drawerargs.h new file mode 100644 index 0000000000..f1c5fc556d --- /dev/null +++ b/src/swrenderer/viewport/r_drawerargs.h @@ -0,0 +1,62 @@ + +#pragma once + +#include "templates.h" +#include "doomtype.h" +#include "doomdef.h" +#include "r_defs.h" +#include "swrenderer/drawers/r_draw.h" +#include "v_video.h" +#include "r_data/colormaps.h" +#include "r_data/r_translate.h" +#include "swrenderer/scene/r_light.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class SWPixelFormatDrawers; + class DrawerArgs; + struct ShadeConstants; + + struct DrawerLight + { + uint32_t color; + float x, y, z; + float radius; + }; + + class DrawerArgs + { + public: + void SetLight(FSWColormap *base_colormap, float light, int shade); + void SetTranslationMap(lighttable_t *translation); + + uint8_t *Colormap(RenderViewport *viewport) const; + uint8_t *TranslationMap() const { return mTranslation; } + + ShadeConstants ColormapConstants() const; + fixed_t Light() const { return LIGHTSCALE(mLight, mShade); } + + private: + FSWColormap *mBaseColormap = nullptr; + float mLight = 0.0f; + int mShade = 0; + uint8_t *mTranslation = nullptr; + }; + + struct ShadeConstants + { + uint16_t light_alpha; + uint16_t light_red; + uint16_t light_green; + uint16_t light_blue; + uint16_t fade_alpha; + uint16_t fade_red; + uint16_t fade_green; + uint16_t fade_blue; + uint16_t desaturate; + bool simple_shade; + }; +} diff --git a/src/swrenderer/viewport/r_skydrawer.cpp b/src/swrenderer/viewport/r_skydrawer.cpp new file mode 100644 index 0000000000..df93c6eed7 --- /dev/null +++ b/src/swrenderer/viewport/r_skydrawer.cpp @@ -0,0 +1,69 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "r_skydrawer.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + void SkyDrawerArgs::DrawSingleSkyColumn(RenderThread *thread) + { + thread->Drawers(dc_viewport)->DrawSingleSkyColumn(*this); + } + + void SkyDrawerArgs::DrawDoubleSkyColumn(RenderThread *thread) + { + thread->Drawers(dc_viewport)->DrawDoubleSkyColumn(*this); + } + + void SkyDrawerArgs::SetDest(RenderViewport *viewport, int x, int y) + { + dc_dest = viewport->GetDest(x, y); + dc_dest_y = y; + dc_viewport = viewport; + } + + void SkyDrawerArgs::SetFrontTexture(RenderViewport *viewport, FTexture *texture, uint32_t column) + { + if (viewport->RenderTarget->IsBgra()) + { + dc_source = (const uint8_t *)texture->GetColumnBgra(column, nullptr); + dc_sourceheight = texture->GetHeight(); + } + else + { + dc_source = texture->GetColumn(column, nullptr); + dc_sourceheight = texture->GetHeight(); + } + } + + void SkyDrawerArgs::SetBackTexture(RenderViewport *viewport, FTexture *texture, uint32_t column) + { + if (texture == nullptr) + { + dc_source2 = nullptr; + dc_sourceheight2 = 1; + } + else if (viewport->RenderTarget->IsBgra()) + { + dc_source2 = (const uint8_t *)texture->GetColumnBgra(column, nullptr); + dc_sourceheight2 = texture->GetHeight(); + } + else + { + dc_source2 = texture->GetColumn(column, nullptr); + dc_sourceheight2 = texture->GetHeight(); + } + } +} diff --git a/src/swrenderer/viewport/r_skydrawer.h b/src/swrenderer/viewport/r_skydrawer.h new file mode 100644 index 0000000000..2f352e939d --- /dev/null +++ b/src/swrenderer/viewport/r_skydrawer.h @@ -0,0 +1,62 @@ + +#pragma once + +#include "r_drawerargs.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class RenderThread; + + class SkyDrawerArgs : public DrawerArgs + { + public: + void SetDest(RenderViewport *viewport, int x, int y); + void SetCount(int count) { dc_count = count; } + void SetFrontTexture(RenderViewport *viewport, FTexture *texture, uint32_t column); + void SetBackTexture(RenderViewport *viewport, FTexture *texture, uint32_t column); + void SetTextureVPos(uint32_t texturefrac) { dc_texturefrac = texturefrac; } + void SetTextureVStep(uint32_t iscale) { dc_iscale = iscale; } + void SetSolidTop(uint32_t color) { solid_top = color; } + void SetSolidBottom(uint32_t color) { solid_bottom = color; } + void SetFadeSky(bool enable) { fadeSky = enable; } + + uint8_t *Dest() const { return dc_dest; } + int DestY() const { return dc_dest_y; } + int Count() const { return dc_count; } + + uint32_t TextureVPos() const { return dc_texturefrac; } + uint32_t TextureVStep() const { return dc_iscale; } + + uint32_t SolidTopColor() const { return solid_top; } + uint32_t SolidBottomColor() const { return solid_bottom; } + bool FadeSky() const { return fadeSky; } + + const uint8_t *FrontTexturePixels() const { return dc_source; } + const uint8_t *BackTexturePixels() const { return dc_source2; } + int FrontTextureHeight() const { return dc_sourceheight; } + int BackTextureHeight() const { return dc_sourceheight2; } + + RenderViewport *Viewport() const { return dc_viewport; } + + void DrawSingleSkyColumn(RenderThread *thread); + void DrawDoubleSkyColumn(RenderThread *thread); + + private: + uint8_t *dc_dest = nullptr; + int dc_dest_y = 0; + int dc_count = 0; + const uint8_t *dc_source; + const uint8_t *dc_source2; + uint32_t dc_sourceheight; + uint32_t dc_sourceheight2; + uint32_t dc_texturefrac; + uint32_t dc_iscale; + uint32_t solid_top; + uint32_t solid_bottom; + bool fadeSky; + RenderViewport *dc_viewport = nullptr; + }; +} diff --git a/src/swrenderer/viewport/r_spandrawer.cpp b/src/swrenderer/viewport/r_spandrawer.cpp new file mode 100644 index 0000000000..afe1cbc66c --- /dev/null +++ b/src/swrenderer/viewport/r_spandrawer.cpp @@ -0,0 +1,127 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "r_spandrawer.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + SpanDrawerArgs::SpanDrawerArgs() + { + spanfunc = &SWPixelFormatDrawers::DrawSpan; + } + + void SpanDrawerArgs::SetTexture(RenderViewport *viewport, FTexture *tex) + { + tex->GetWidth(); + ds_xbits = tex->WidthBits; + ds_ybits = tex->HeightBits; + if ((1 << ds_xbits) > tex->GetWidth()) + { + ds_xbits--; + } + if ((1 << ds_ybits) > tex->GetHeight()) + { + ds_ybits--; + } + + ds_source = viewport->RenderTarget->IsBgra() ? (const uint8_t*)tex->GetPixelsBgra() : tex->GetPixels(); + ds_source_mipmapped = tex->Mipmapped() && tex->GetWidth() > 1 && tex->GetHeight() > 1; + } + + void SpanDrawerArgs::SetStyle(bool masked, bool additive, fixed_t alpha) + { + if (masked) + { + if (alpha < OPAQUE || additive) + { + if (!additive) + { + spanfunc = &SWPixelFormatDrawers::DrawSpanMaskedTranslucent; + dc_srcblend = Col2RGB8[alpha >> 10]; + dc_destblend = Col2RGB8[(OPAQUE - alpha) >> 10]; + dc_srcalpha = alpha; + dc_destalpha = OPAQUE - alpha; + } + else + { + spanfunc = &SWPixelFormatDrawers::DrawSpanMaskedAddClamp; + dc_srcblend = Col2RGB8_LessPrecision[alpha >> 10]; + dc_destblend = Col2RGB8_LessPrecision[FRACUNIT >> 10]; + dc_srcalpha = alpha; + dc_destalpha = FRACUNIT; + } + } + else + { + spanfunc = &SWPixelFormatDrawers::DrawSpanMasked; + } + } + else + { + if (alpha < OPAQUE || additive) + { + if (!additive) + { + spanfunc = &SWPixelFormatDrawers::DrawSpanTranslucent; + dc_srcblend = Col2RGB8[alpha >> 10]; + dc_destblend = Col2RGB8[(OPAQUE - alpha) >> 10]; + dc_srcalpha = alpha; + dc_destalpha = OPAQUE - alpha; + } + else + { + spanfunc = &SWPixelFormatDrawers::DrawSpanAddClamp; + dc_srcblend = Col2RGB8_LessPrecision[alpha >> 10]; + dc_destblend = Col2RGB8_LessPrecision[FRACUNIT >> 10]; + dc_srcalpha = alpha; + dc_destalpha = FRACUNIT; + } + } + else + { + spanfunc = &SWPixelFormatDrawers::DrawSpan; + } + } + } + + void SpanDrawerArgs::DrawSpan(RenderThread *thread) + { + (thread->Drawers(ds_viewport)->*spanfunc)(*this); + } + + void SpanDrawerArgs::DrawTiltedSpan(RenderThread *thread, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) + { + SetDestY(thread->Viewport.get(), y); + SetDestX1(x1); + SetDestX2(x2); + thread->Drawers(ds_viewport)->DrawTiltedSpan(*this, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy, basecolormap); + } + + void SpanDrawerArgs::DrawFogBoundaryLine(RenderThread *thread, int y, int x1, int x2) + { + SetDestY(thread->Viewport.get(), y); + SetDestX1(x1); + SetDestX2(x2); + thread->Drawers(ds_viewport)->DrawFogBoundaryLine(*this); + } + + void SpanDrawerArgs::DrawColoredSpan(RenderThread *thread, int y, int x1, int x2) + { + SetDestY(thread->Viewport.get(), y); + SetDestX1(x1); + SetDestX2(x2); + thread->Drawers(ds_viewport)->DrawColoredSpan(*this); + } +} diff --git a/src/swrenderer/viewport/r_spandrawer.h b/src/swrenderer/viewport/r_spandrawer.h new file mode 100644 index 0000000000..cba3ab0d77 --- /dev/null +++ b/src/swrenderer/viewport/r_spandrawer.h @@ -0,0 +1,83 @@ + +#pragma once + +#include "r_drawerargs.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class RenderThread; + + class SpanDrawerArgs : public DrawerArgs + { + public: + SpanDrawerArgs(); + + void SetStyle(bool masked, bool additive, fixed_t alpha); + void SetDestY(RenderViewport *viewport, int y) { ds_viewport = viewport; ds_y = y; } + void SetDestX1(int x) { ds_x1 = x; } + void SetDestX2(int x) { ds_x2 = x; } + void SetTexture(RenderViewport *viewport, FTexture *tex); + void SetTextureLOD(double lod) { ds_lod = lod; } + void SetTextureUPos(dsfixed_t xfrac) { ds_xfrac = xfrac; } + void SetTextureVPos(dsfixed_t yfrac) { ds_yfrac = yfrac; } + void SetTextureUStep(dsfixed_t xstep) { ds_xstep = xstep; } + void SetTextureVStep(dsfixed_t vstep) { ds_ystep = vstep; } + void SetSolidColor(int colorIndex) { ds_color = colorIndex; } + + void DrawSpan(RenderThread *thread); + void DrawTiltedSpan(RenderThread *thread, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap); + void DrawColoredSpan(RenderThread *thread, int y, int x1, int x2); + void DrawFogBoundaryLine(RenderThread *thread, int y, int x1, int x2); + + uint32_t *SrcBlend() const { return dc_srcblend; } + uint32_t *DestBlend() const { return dc_destblend; } + fixed_t SrcAlpha() const { return dc_srcalpha; } + fixed_t DestAlpha() const { return dc_destalpha; } + int DestY() const { return ds_y; } + int DestX1() const { return ds_x1; } + int DestX2() const { return ds_x2; } + dsfixed_t TextureUPos() const { return ds_xfrac; } + dsfixed_t TextureVPos() const { return ds_yfrac; } + dsfixed_t TextureUStep() const { return ds_xstep; } + dsfixed_t TextureVStep() const { return ds_ystep; } + int SolidColor() const { return ds_color; } + int TextureWidthBits() const { return ds_xbits; } + int TextureHeightBits() const { return ds_ybits; } + const uint8_t *TexturePixels() const { return ds_source; } + bool MipmappedTexture() const { return ds_source_mipmapped; } + double TextureLOD() const { return ds_lod; } + RenderViewport *Viewport() const { return ds_viewport; } + + FVector3 dc_normal; + FVector3 dc_viewpos; + FVector3 dc_viewpos_step; + DrawerLight *dc_lights = nullptr; + int dc_num_lights = 0; + + private: + typedef void(SWPixelFormatDrawers::*SpanDrawerFunc)(const SpanDrawerArgs &args); + SpanDrawerFunc spanfunc; + + int ds_y; + int ds_x1; + int ds_x2; + int ds_xbits; + int ds_ybits; + const uint8_t *ds_source; + bool ds_source_mipmapped; + dsfixed_t ds_xfrac; + dsfixed_t ds_yfrac; + dsfixed_t ds_xstep; + dsfixed_t ds_ystep; + uint32_t *dc_srcblend; + uint32_t *dc_destblend; + fixed_t dc_srcalpha; + fixed_t dc_destalpha; + int ds_color = 0; + double ds_lod; + RenderViewport *ds_viewport = nullptr; + }; +} diff --git a/src/swrenderer/viewport/r_spritedrawer.cpp b/src/swrenderer/viewport/r_spritedrawer.cpp new file mode 100644 index 0000000000..cd5bc44d7d --- /dev/null +++ b/src/swrenderer/viewport/r_spritedrawer.cpp @@ -0,0 +1,524 @@ +/* +** r_spritedrawer.cpp +** +**--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2016 Magnus Norddahl +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include "r_spritedrawer.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + SpriteDrawerArgs::SpriteDrawerArgs() + { + colfunc = &SWPixelFormatDrawers::DrawColumn; + } + + void SpriteDrawerArgs::DrawMaskedColumn(RenderThread *thread, int x, fixed_t iscale, FTexture *tex, fixed_t col, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked) + { + if (x < thread->X1 || x >= thread->X2) + return; + + auto viewport = thread->Viewport.get(); + + // Handle the linear filtered version in a different function to reduce chances of merge conflicts from zdoom. + if (viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input) // To do: add support to R_DrawColumnHoriz_rgba + { + DrawMaskedColumnBgra(thread, x, iscale, tex, col, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, unmasked); + return; + } + + dc_viewport = viewport; + dc_x = x; + dc_iscale = iscale; + dc_textureheight = tex->GetHeight(); + + const FTexture::Span *span; + const uint8_t *column; + if (viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input) + column = (const uint8_t *)tex->GetColumnBgra(col >> FRACBITS, &span); + else + column = tex->GetColumn(col >> FRACBITS, &span); + + FTexture::Span unmaskedSpan[2]; + if (unmasked) + { + span = unmaskedSpan; + unmaskedSpan[0].TopOffset = 0; + unmaskedSpan[0].Length = tex->GetHeight(); + unmaskedSpan[1].TopOffset = 0; + unmaskedSpan[1].Length = 0; + } + + int pixelsize = viewport->RenderTarget->IsBgra() ? 4 : 1; + + while (span->Length != 0) + { + const int length = span->Length; + const int top = span->TopOffset; + + // calculate unclipped screen coordinates for post + dc_yl = (int)(sprtopscreen + spryscale * top + 0.5); + dc_yh = (int)(sprtopscreen + spryscale * (top + length) + 0.5) - 1; + + if (sprflipvert) + { + swapvalues(dc_yl, dc_yh); + } + + if (dc_yh >= mfloorclip[dc_x]) + { + dc_yh = mfloorclip[dc_x] - 1; + } + if (dc_yl < mceilingclip[dc_x]) + { + dc_yl = mceilingclip[dc_x]; + } + + if (dc_yl <= dc_yh) + { + dc_texturefrac = FLOAT2FIXED((dc_yl + 0.5 - sprtopscreen) / spryscale); + dc_source = column; + dc_source2 = nullptr; + SetDest(viewport, dc_x, dc_yl); + dc_count = dc_yh - dc_yl + 1; + + fixed_t maxfrac = ((top + length) << FRACBITS) - 1; + dc_texturefrac = MAX(dc_texturefrac, 0); + dc_texturefrac = MIN(dc_texturefrac, maxfrac); + if (dc_iscale > 0) + dc_count = MIN(dc_count, (maxfrac - dc_texturefrac + dc_iscale - 1) / dc_iscale); + else if (dc_iscale < 0) + dc_count = MIN(dc_count, (dc_texturefrac - dc_iscale) / (-dc_iscale)); + + (thread->Drawers(dc_viewport)->*colfunc)(*this); + } + span++; + } + } + + void SpriteDrawerArgs::DrawMaskedColumnBgra(RenderThread *thread, int x, fixed_t iscale, FTexture *tex, fixed_t col, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked) + { + dc_viewport = thread->Viewport.get(); + dc_x = x; + dc_iscale = iscale; + + // Normalize to 0-1 range: + double uv_stepd = FIXED2DBL(dc_iscale); + double v_step = uv_stepd / tex->GetHeight(); + + // Convert to uint32_t: + dc_iscale = (uint32_t)(v_step * (1 << 30)); + + // Texture mipmap and filter selection: + fixed_t xoffset = col; + + double xmagnitude = 1.0; // To do: pass this into R_DrawMaskedColumn + double ymagnitude = fabs(uv_stepd); + double magnitude = MAX(ymagnitude, xmagnitude); + double min_lod = -1000.0; + double lod = MAX(log2(magnitude) + r_lod_bias, min_lod); + bool magnifying = lod < 0.0f; + + int mipmap_offset = 0; + int mip_width = tex->GetWidth(); + int mip_height = tex->GetHeight(); + uint32_t xpos = (uint32_t)((((uint64_t)xoffset) << FRACBITS) / mip_width); + if (r_mipmap && tex->Mipmapped() && mip_width > 1 && mip_height > 1) + { + int level = (int)lod; + while (level > 0 && mip_width > 1 && mip_height > 1) + { + mipmap_offset += mip_width * mip_height; + level--; + mip_width = MAX(mip_width >> 1, 1); + mip_height = MAX(mip_height >> 1, 1); + } + } + xoffset = (xpos >> FRACBITS) * mip_width; + + const uint32_t *pixels = tex->GetPixelsBgra() + mipmap_offset; + + bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter); + if (filter_nearest) + { + xoffset = MAX(MIN(xoffset, (mip_width << FRACBITS) - 1), 0); + + int tx = xoffset >> FRACBITS; + dc_source = (uint8_t*)(pixels + tx * mip_height); + dc_source2 = nullptr; + dc_textureheight = mip_height; + dc_texturefracx = 0; + } + else + { + xoffset = MAX(MIN(xoffset - (FRACUNIT / 2), (mip_width << FRACBITS) - 1), 0); + + int tx0 = xoffset >> FRACBITS; + int tx1 = MIN(tx0 + 1, mip_width - 1); + dc_source = (uint8_t*)(pixels + tx0 * mip_height); + dc_source2 = (uint8_t*)(pixels + tx1 * mip_height); + dc_textureheight = mip_height; + dc_texturefracx = (xoffset >> (FRACBITS - 4)) & 15; + } + + // Grab the posts we need to draw + const FTexture::Span *span; + tex->GetColumnBgra(col >> FRACBITS, &span); + FTexture::Span unmaskedSpan[2]; + if (unmasked) + { + span = unmaskedSpan; + unmaskedSpan[0].TopOffset = 0; + unmaskedSpan[0].Length = tex->GetHeight(); + unmaskedSpan[1].TopOffset = 0; + unmaskedSpan[1].Length = 0; + } + + // Draw each span post + while (span->Length != 0) + { + const int length = span->Length; + const int top = span->TopOffset; + + // calculate unclipped screen coordinates for post + dc_yl = (int)(sprtopscreen + spryscale * top + 0.5); + dc_yh = (int)(sprtopscreen + spryscale * (top + length) + 0.5) - 1; + + if (sprflipvert) + { + swapvalues(dc_yl, dc_yh); + } + + if (dc_yh >= mfloorclip[dc_x]) + { + dc_yh = mfloorclip[dc_x] - 1; + } + if (dc_yl < mceilingclip[dc_x]) + { + dc_yl = mceilingclip[dc_x]; + } + + if (dc_yl <= dc_yh) + { + SetDest(dc_viewport, dc_x, dc_yl); + dc_count = dc_yh - dc_yl + 1; + + double v = ((dc_yl + 0.5 - sprtopscreen) / spryscale) / tex->GetHeight(); + dc_texturefrac = (uint32_t)(v * (1 << 30)); + + (thread->Drawers(dc_viewport)->*colfunc)(*this); + } + span++; + } + } + + bool SpriteDrawerArgs::SetBlendFunc(int op, fixed_t fglevel, fixed_t bglevel, int flags) + { + // r_drawtrans is a seriously bad thing to turn off. I wonder if I should + // just remove it completely. + if (!r_drawtrans || (op == STYLEOP_Add && fglevel == FRACUNIT && bglevel == 0 && !(flags & STYLEF_InvertSource))) + { + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawTranslatedColumn; + drawer_needs_pal_input = true; + } + return true; + } + if (flags & STYLEF_InvertSource) + { + dc_srcblend = Col2RGB8_Inverse[fglevel >> 10]; + dc_destblend = Col2RGB8_LessPrecision[bglevel >> 10]; + dc_srcalpha = fglevel; + dc_destalpha = bglevel; + } + else if (op == STYLEOP_Add && fglevel + bglevel <= FRACUNIT) + { + dc_srcblend = Col2RGB8[fglevel >> 10]; + dc_destblend = Col2RGB8[bglevel >> 10]; + dc_srcalpha = fglevel; + dc_destalpha = bglevel; + } + else + { + dc_srcblend = Col2RGB8_LessPrecision[fglevel >> 10]; + dc_destblend = Col2RGB8_LessPrecision[bglevel >> 10]; + dc_srcalpha = fglevel; + dc_destalpha = bglevel; + } + switch (op) + { + case STYLEOP_Add: + if (fglevel == 0 && bglevel == FRACUNIT) + { + return false; + } + if (fglevel + bglevel <= FRACUNIT) + { // Colors won't overflow when added + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillAddColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawAddColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawTranslatedAddColumn; + drawer_needs_pal_input = true; + } + } + else + { // Colors might overflow when added + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillAddClampColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawAddClampColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawAddClampTranslatedColumn; + drawer_needs_pal_input = true; + } + } + return true; + + case STYLEOP_Sub: + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillSubClampColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawSubClampColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawSubClampTranslatedColumn; + drawer_needs_pal_input = true; + } + return true; + + case STYLEOP_RevSub: + if (fglevel == 0 && bglevel == FRACUNIT) + { + return false; + } + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillRevSubClampColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawRevSubClampColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawRevSubClampTranslatedColumn; + drawer_needs_pal_input = true; + } + return true; + + default: + return false; + } + } + + fixed_t SpriteDrawerArgs::GetAlpha(int type, fixed_t alpha) + { + switch (type) + { + case STYLEALPHA_Zero: return 0; + case STYLEALPHA_One: return OPAQUE; + case STYLEALPHA_Src: return alpha; + case STYLEALPHA_InvSrc: return OPAQUE - alpha; + default: return 0; + } + } + + bool SpriteDrawerArgs::SetStyle(RenderViewport *viewport, FRenderStyle style, fixed_t alpha, int translation, uint32_t color, FDynamicColormap *&basecolormap, fixed_t shadedlightshade) + { + fixed_t fglevel, bglevel; + + drawer_needs_pal_input = false; + + style.CheckFuzz(); + + if (style.BlendOp == STYLEOP_Shadow) + { + style = LegacyRenderStyles[STYLE_TranslucentStencil]; + alpha = OPAQUE / 3; + color = 0; + } + + if (style.Flags & STYLEF_ForceAlpha) + { + alpha = clamp(alpha, 0, OPAQUE); + } + else if (style.Flags & STYLEF_TransSoulsAlpha) + { + alpha = fixed_t(transsouls * OPAQUE); + } + else if (style.Flags & STYLEF_Alpha1) + { + alpha = OPAQUE; + } + else + { + alpha = clamp(alpha, 0, OPAQUE); + } + + if (translation != -1) + { + SetTranslationMap(nullptr); + if (translation != 0) + { + FRemapTable *table = TranslationToTable(translation); + if (table != NULL && !table->Inactive) + { + if (viewport->RenderTarget->IsBgra()) + SetTranslationMap((uint8_t*)table->Palette); + else + SetTranslationMap(table->Remap); + } + } + } + + // Check for special modes + if (style.BlendOp == STYLEOP_Fuzz) + { + colfunc = &SWPixelFormatDrawers::DrawFuzzColumn; + return true; + } + else if (style == LegacyRenderStyles[STYLE_Shaded]) + { + // Shaded drawer only gets 16 levels of alpha because it saves memory. + if ((alpha >>= 12) == 0 || basecolormap == nullptr) + return false; + colfunc = &SWPixelFormatDrawers::DrawShadedColumn; + drawer_needs_pal_input = true; + CameraLight *cameraLight = CameraLight::Instance(); + dc_color = cameraLight->FixedColormap() ? cameraLight->FixedColormap()->Maps[APART(color)] : basecolormap->Maps[APART(color)]; + basecolormap = &ShadeFakeColormap[16 - alpha]; + if (cameraLight->FixedLightLevel() >= 0 && !cameraLight->FixedColormap()) + { + fixed_t shade = shadedlightshade; + if (shade == 0) shade = cameraLight->FixedLightLevelShade(); + SetLight(basecolormap, 0, shade); + } + else + { + SetLight(basecolormap, 0, shadedlightshade); + } + return true; + } + + fglevel = GetAlpha(style.SrcAlpha, alpha); + bglevel = GetAlpha(style.DestAlpha, alpha); + + if (style.Flags & STYLEF_ColorIsFixed) + { + uint32_t x = fglevel >> 10; + uint32_t r = RPART(color); + uint32_t g = GPART(color); + uint32_t b = BPART(color); + // dc_color is used by the rt_* routines. It is indexed into dc_srcblend. + dc_color = RGB256k.RGB[r >> 2][g >> 2][b >> 2]; + if (style.Flags & STYLEF_InvertSource) + { + r = 255 - r; + g = 255 - g; + b = 255 - b; + } + uint32_t alpha = clamp(fglevel >> (FRACBITS - 8), 0, 255); + dc_srccolor_bgra = (alpha << 24) | (r << 16) | (g << 8) | b; + // dc_srccolor is used by the R_Fill* routines. It is premultiplied + // with the alpha. + dc_srccolor = ((((r*x) >> 4) << 20) | ((g*x) >> 4) | ((((b)*x) >> 4) << 10)) & 0x3feffbff; + SetLight(&identitycolormap, 0, 0); + } + + return SpriteDrawerArgs::SetBlendFunc(style.BlendOp, fglevel, bglevel, style.Flags); + } + + bool SpriteDrawerArgs::SetStyle(RenderViewport *viewport, FRenderStyle style, float alpha, int translation, uint32_t color, FDynamicColormap *&basecolormap, fixed_t shadedlightshade) + { + return SetStyle(viewport, style, FLOAT2FIXED(alpha), translation, color, basecolormap, shadedlightshade); + } + + void SpriteDrawerArgs::FillColumn(RenderThread *thread) + { + thread->Drawers(dc_viewport)->FillColumn(*this); + } + + void SpriteDrawerArgs::DrawVoxelColumn(RenderThread *thread, fixed_t vPos, fixed_t vStep, const uint8_t *voxels, int voxelsCount) + { + if (dc_viewport->RenderTarget->IsBgra()) + { + double v = vPos / (double)voxelsCount / FRACUNIT; + double vstep = vStep / (double)voxelsCount / FRACUNIT; + dc_texturefrac = (int)(v * (1 << 30)); + dc_iscale = (int)(vstep * (1 << 30)); + } + else + { + dc_texturefrac = vPos; + dc_iscale = vStep; + } + + dc_texturefracx = 0; + dc_source = voxels; + dc_source2 = 0; + dc_textureheight = voxelsCount; + (thread->Drawers(dc_viewport)->*colfunc)(*this); + } + + void SpriteDrawerArgs::SetDest(RenderViewport *viewport, int x, int y) + { + dc_dest = viewport->GetDest(x, y); + dc_dest_y = y; + dc_viewport = viewport; + } +} diff --git a/src/swrenderer/viewport/r_spritedrawer.h b/src/swrenderer/viewport/r_spritedrawer.h new file mode 100644 index 0000000000..34e80cf609 --- /dev/null +++ b/src/swrenderer/viewport/r_spritedrawer.h @@ -0,0 +1,97 @@ + +#pragma once + +#include "r_drawerargs.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class RenderThread; + + class SpriteDrawerArgs : public DrawerArgs + { + public: + SpriteDrawerArgs(); + + bool SetStyle(RenderViewport *viewport, FRenderStyle style, fixed_t alpha, int translation, uint32_t color, FDynamicColormap *&basecolormap, fixed_t shadedlightshade = 0); + bool SetStyle(RenderViewport *viewport, FRenderStyle style, float alpha, int translation, uint32_t color, FDynamicColormap *&basecolormap, fixed_t shadedlightshade = 0); + void SetDest(RenderViewport *viewport, int x, int y); + void SetCount(int count) { dc_count = count; } + void SetSolidColor(int color) { dc_color = color; } + void SetDynamicLight(uint32_t color) { dynlightcolor = color; } + + void DrawMaskedColumn(RenderThread *thread, int x, fixed_t iscale, FTexture *texture, fixed_t column, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked = false); + void FillColumn(RenderThread *thread); + void DrawVoxelColumn(RenderThread *thread, fixed_t vPos, fixed_t vStep, const uint8_t *voxels, int voxelsCount); + + uint8_t *Dest() const { return dc_dest; } + int DestY() const { return dc_dest_y; } + int Count() const { return dc_count; } + + int FuzzX() const { return dc_x; } + int FuzzY1() const { return dc_yl; } + int FuzzY2() const { return dc_yh; } + + uint32_t TextureUPos() const { return dc_texturefracx; } + fixed_t TextureVPos() const { return dc_texturefrac; } + fixed_t TextureVStep() const { return dc_iscale; } + + int SolidColor() const { return dc_color; } + uint32_t SrcColorIndex() const { return dc_srccolor; } + uint32_t SrcColorBgra() const { return dc_srccolor_bgra; } + + const uint8_t *TexturePixels() const { return dc_source; } + const uint8_t *TexturePixels2() const { return dc_source2; } + uint32_t TextureHeight() const { return dc_textureheight; } + + uint32_t *SrcBlend() const { return dc_srcblend; } + uint32_t *DestBlend() const { return dc_destblend; } + fixed_t SrcAlpha() const { return dc_srcalpha; } + fixed_t DestAlpha() const { return dc_destalpha; } + + uint32_t DynamicLight() const { return dynlightcolor; } + + bool DrawerNeedsPalInput() const { return drawer_needs_pal_input; } + RenderViewport *Viewport() const { return dc_viewport; } + + private: + bool SetBlendFunc(int op, fixed_t fglevel, fixed_t bglevel, int flags); + static fixed_t GetAlpha(int type, fixed_t alpha); + void DrawMaskedColumnBgra(RenderThread *thread, int x, fixed_t iscale, FTexture *tex, fixed_t column, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked); + + uint8_t *dc_dest = nullptr; + int dc_dest_y = 0; + int dc_count = 0; + + fixed_t dc_iscale; + fixed_t dc_texturefrac; + uint32_t dc_texturefracx; + + uint32_t dc_textureheight = 0; + const uint8_t *dc_source = nullptr; + const uint8_t *dc_source2 = nullptr; + bool drawer_needs_pal_input = false; + + uint32_t *dc_srcblend = nullptr; + uint32_t *dc_destblend = nullptr; + fixed_t dc_srcalpha = OPAQUE; + fixed_t dc_destalpha = 0; + + int dc_x = 0; + int dc_yl = 0; + int dc_yh = 0; + + int dc_color = 0; + uint32_t dc_srccolor = 0; + uint32_t dc_srccolor_bgra = 0; + + uint32_t dynlightcolor = 0; + + typedef void(SWPixelFormatDrawers::*SpriteDrawerFunc)(const SpriteDrawerArgs &args); + SpriteDrawerFunc colfunc; + + RenderViewport *dc_viewport = nullptr; + }; +} diff --git a/src/swrenderer/viewport/r_viewport.cpp b/src/swrenderer/viewport/r_viewport.cpp new file mode 100644 index 0000000000..47599d1f48 --- /dev/null +++ b/src/swrenderer/viewport/r_viewport.cpp @@ -0,0 +1,213 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "r_utility.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/things/r_playersprite.h" +#include "swrenderer/plane/r_flatplane.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "swrenderer/drawers/r_draw_rgba.h" + +CVAR(String, r_viewsize, "", CVAR_NOSET) + +namespace swrenderer +{ + RenderViewport::RenderViewport() + { + } + + RenderViewport::~RenderViewport() + { + } + + void RenderViewport::SetViewport(int fullWidth, int fullHeight, float trueratio) + { + int virtheight, virtwidth, virtwidth2, virtheight2; + + if (!RenderingToCanvas()) + { // Set r_viewsize cvar to reflect the current view size + UCVarValue value; + char temp[16]; + + mysnprintf(temp, countof(temp), "%d x %d", viewwidth, viewheight); + value.String = temp; + r_viewsize.ForceSet(value, CVAR_String); + } + + fuzzviewheight = viewheight - 2; // Maximum row the fuzzer can draw to + + CenterX = viewwindow.centerx; + CenterY = viewwindow.centery; + + virtwidth = virtwidth2 = fullWidth; + virtheight = virtheight2 = fullHeight; + + if (AspectTallerThanWide(trueratio)) + { + virtheight2 = virtheight2 * AspectMultiplier(trueratio) / 48; + } + else + { + virtwidth2 = virtwidth2 * AspectMultiplier(trueratio) / 48; + } + + if (AspectTallerThanWide(viewwindow.WidescreenRatio)) + { + virtheight = virtheight * AspectMultiplier(viewwindow.WidescreenRatio) / 48; + } + else + { + virtwidth = virtwidth * AspectMultiplier(viewwindow.WidescreenRatio) / 48; + } + + BaseYaspectMul = 320.0 * virtheight2 / (r_Yaspect * virtwidth2); + YaspectMul = 320.0 * virtheight / (r_Yaspect * virtwidth); + IYaspectMul = (double)virtwidth * r_Yaspect / 320.0 / virtheight; + InvZtoScale = YaspectMul * CenterX; + + WallTMapScale2 = IYaspectMul / CenterX; + + // thing clipping + fillshort(screenheightarray, viewwidth, (short)viewheight); + + InitTextureMapping(); + + // Reset r_*Visibility vars + LightVisibility *visibility = LightVisibility::Instance(); + visibility->SetVisibility(this, visibility->GetVisibility()); + + SetupBuffer(); + } + + void RenderViewport::SetupFreelook() + { + double dy; + + if (viewpoint.camera != NULL) + { + dy = FocalLengthY * (-viewpoint.Angles.Pitch).Tan(); + } + else + { + dy = 0; + } + + CenterY = (viewheight / 2.0) + dy; + viewwindow.centery = xs_ToInt(CenterY); + globaluclip = -CenterY / InvZtoScale; + globaldclip = (viewheight - CenterY) / InvZtoScale; + } + + void RenderViewport::SetupBuffer() + { + R_InitFuzzTable(RenderTarget->GetPitch()); + R_InitParticleTexture(); + } + + uint8_t *RenderViewport::GetDest(int x, int y) + { + x += viewwindowx; + y += viewwindowy; + + int pitch = RenderTarget->GetPitch(); + int pixelsize = RenderTarget->IsBgra() ? 4 : 1; + return RenderTarget->GetBuffer() + (x + y * pitch) * pixelsize; + } + + void RenderViewport::InitTextureMapping() + { + int i; + + // Calc focallength so FieldOfView angles cover viewwidth. + FocalLengthX = CenterX / viewwindow.FocalTangent; + FocalLengthY = FocalLengthX * YaspectMul; + + // This is 1/FocalTangent before the widescreen extension of FOV. + viewingrangerecip = FLOAT2FIXED(1. / tan(viewpoint.FieldOfView.Radians() / 2)); + + // Now generate xtoviewangle for sky texture mapping. + // [RH] Do not generate viewangletox, because texture mapping is no + // longer done with trig, so it's not needed. + const double slopestep = viewwindow.FocalTangent / viewwindow.centerx; + double slope; + + for (i = viewwindow.centerx, slope = 0; i <= viewwidth; i++, slope += slopestep) + { + xtoviewangle[i] = angle_t((2 * M_PI - atan(slope)) * (ANGLE_180 / M_PI)); + } + for (i = 0; i < viewwindow.centerx; i++) + { + xtoviewangle[i] = 0 - xtoviewangle[viewwidth - i - 1]; + } + } + + DVector2 RenderViewport::PointWorldToView(const DVector2 &worldPos) const + { + double translatedX = worldPos.X - viewpoint.Pos.X; + double translatedY = worldPos.Y - viewpoint.Pos.Y; + return { + translatedX * viewpoint.Sin - translatedY * viewpoint.Cos, + translatedX * viewpoint.TanCos + translatedY * viewpoint.TanSin + }; + } + + DVector3 RenderViewport::PointWorldToView(const DVector3 &worldPos) const + { + double translatedX = worldPos.X - viewpoint.Pos.X; + double translatedY = worldPos.Y - viewpoint.Pos.Y; + double translatedZ = worldPos.Z - viewpoint.Pos.Z; + return { + translatedX * viewpoint.Sin - translatedY * viewpoint.Cos, + translatedZ, + translatedX * viewpoint.TanCos + translatedY * viewpoint.TanSin + }; + } + + DVector3 RenderViewport::PointWorldToScreen(const DVector3 &worldPos) const + { + return PointViewToScreen(PointWorldToView(worldPos)); + } + + DVector3 RenderViewport::PointViewToScreen(const DVector3 &viewPos) const + { + double screenX = CenterX + viewPos.X / viewPos.Z * CenterX; + double screenY = CenterY - viewPos.Y / viewPos.Z * InvZtoScale; + return { screenX, screenY, viewPos.Z }; + } + + DVector2 RenderViewport::ScaleViewToScreen(const DVector2 &scale, double viewZ, bool pixelstretch) const + { + double screenScaleX = scale.X / viewZ * CenterX; + double screenScaleY = scale.Y / viewZ * InvZtoScale; + if (!pixelstretch) screenScaleY /= YaspectMul; + return { screenScaleX, screenScaleY }; + } +} diff --git a/src/swrenderer/viewport/r_viewport.h b/src/swrenderer/viewport/r_viewport.h new file mode 100644 index 0000000000..9898458a58 --- /dev/null +++ b/src/swrenderer/viewport/r_viewport.h @@ -0,0 +1,78 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include +#include "r_defs.h" + +namespace swrenderer +{ + class RenderViewport + { + public: + RenderViewport(); + ~RenderViewport(); + + void SetViewport(int width, int height, float trueratio); + void SetupFreelook(); + + DCanvas *RenderTarget = nullptr; + + FViewWindow viewwindow; + FRenderViewpoint viewpoint; + + double FocalLengthX = 0.0; + double FocalLengthY = 0.0; + double InvZtoScale = 0.0; + double WallTMapScale2 = 0.0; + double CenterX = 0.0; + double CenterY = 0.0; + double YaspectMul = 0.0; + double IYaspectMul = 0.0; + double globaluclip = 0.0; + double globaldclip = 0.0; + + fixed_t viewingrangerecip = 0; + double BaseYaspectMul = 0.0; // yaspectmul without a forced aspect ratio + + // The xtoviewangleangle[] table maps a screen pixel + // to the lowest viewangle that maps back to x ranges + // from clipangle to -clipangle. + angle_t xtoviewangle[MAXWIDTH + 1]; + + uint8_t *GetDest(int x, int y); + + bool RenderingToCanvas() const { return RenderTarget != screen; } + + DVector3 PointWorldToView(const DVector3 &worldPos) const; + DVector3 PointWorldToScreen(const DVector3 &worldPos) const; + DVector3 PointViewToScreen(const DVector3 &viewPos) const; + + DVector2 PointWorldToView(const DVector2 &worldPos) const; + DVector2 ScaleViewToScreen(const DVector2 &scale, double viewZ, bool pixelstretch = true) const; + + double PlaneDepth(int screenY, double planeHeight) const + { + if (screenY + 0.5 < CenterY) + return FocalLengthY / (CenterY - screenY - 0.5) * planeHeight; + else + return FocalLengthY / (screenY + 0.5 - CenterY) * planeHeight; + } + + private: + void InitTextureMapping(); + void SetupBuffer(); + }; +} diff --git a/src/swrenderer/viewport/r_walldrawer.cpp b/src/swrenderer/viewport/r_walldrawer.cpp new file mode 100644 index 0000000000..2ec85e675b --- /dev/null +++ b/src/swrenderer/viewport/r_walldrawer.cpp @@ -0,0 +1,67 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "r_walldrawer.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + void WallDrawerArgs::SetDest(RenderViewport *viewport, int x, int y) + { + dc_viewport = viewport; + dc_dest = viewport->GetDest(x, y); + dc_dest_y = y; + } + + void WallDrawerArgs::DrawColumn(RenderThread *thread) + { + (thread->Drawers(dc_viewport)->*wallfunc)(*this); + } + + void WallDrawerArgs::SetStyle(bool masked, bool additive, fixed_t alpha) + { + if (alpha < OPAQUE || additive) + { + if (!additive) + { + wallfunc = &SWPixelFormatDrawers::DrawWallAddColumn; + dc_srcblend = Col2RGB8[alpha >> 10]; + dc_destblend = Col2RGB8[(OPAQUE - alpha) >> 10]; + dc_srcalpha = alpha; + dc_destalpha = OPAQUE - alpha; + } + else + { + wallfunc = &SWPixelFormatDrawers::DrawWallAddClampColumn; + dc_srcblend = Col2RGB8_LessPrecision[alpha >> 10]; + dc_destblend = Col2RGB8_LessPrecision[FRACUNIT >> 10]; + dc_srcalpha = alpha; + dc_destalpha = FRACUNIT; + } + } + else if (masked) + { + wallfunc = &SWPixelFormatDrawers::DrawWallMaskedColumn; + } + else + { + wallfunc = &SWPixelFormatDrawers::DrawWallColumn; + } + } + + bool WallDrawerArgs::IsMaskedDrawer() const + { + return wallfunc == &SWPixelFormatDrawers::DrawWallMaskedColumn; + } +} diff --git a/src/swrenderer/viewport/r_walldrawer.h b/src/swrenderer/viewport/r_walldrawer.h new file mode 100644 index 0000000000..5dde5cc5c7 --- /dev/null +++ b/src/swrenderer/viewport/r_walldrawer.h @@ -0,0 +1,85 @@ + +#pragma once + +#include "r_drawerargs.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class RenderThread; + class RenderViewport; + + class WallDrawerArgs : public DrawerArgs + { + public: + void SetStyle(bool masked, bool additive, fixed_t alpha); + void SetDest(RenderViewport *viewport, int x, int y); + void SetCount(int count) { dc_count = count; } + void SetTexture(const uint8_t *pixels, const uint8_t *pixels2, int height) + { + dc_source = pixels; + dc_source2 = pixels2; + dc_textureheight = height; + } + void SetTextureFracBits(int bits) { dc_wall_fracbits = bits; } + void SetTextureUPos(uint32_t pos) { dc_texturefracx = pos; } + void SetTextureVPos(fixed_t pos) { dc_texturefrac = pos; } + void SetTextureVStep(fixed_t step) { dc_iscale = step; } + + bool IsMaskedDrawer() const; + + void DrawColumn(RenderThread *thread); + + uint8_t *Dest() const { return dc_dest; } + int DestY() const { return dc_dest_y; } + int Count() const { return dc_count; } + + uint32_t *SrcBlend() const { return dc_srcblend; } + uint32_t *DestBlend() const { return dc_destblend; } + fixed_t SrcAlpha() const { return dc_srcalpha; } + fixed_t DestAlpha() const { return dc_destalpha; } + + uint32_t TextureUPos() const { return dc_texturefracx; } + fixed_t TextureVPos() const { return dc_texturefrac; } + fixed_t TextureVStep() const { return dc_iscale; } + + const uint8_t *TexturePixels() const { return dc_source; } + const uint8_t *TexturePixels2() const { return dc_source2; } + uint32_t TextureHeight() const { return dc_textureheight; } + + int TextureFracBits() const { return dc_wall_fracbits; } + + FVector3 dc_normal = { 0,0,0 }; + FVector3 dc_viewpos = { 0,0,0 }; + FVector3 dc_viewpos_step = { 0,0,0 }; + DrawerLight *dc_lights = nullptr; + int dc_num_lights = 0; + + RenderViewport *Viewport() const { return dc_viewport; } + + private: + uint8_t *dc_dest = nullptr; + int dc_dest_y = 0; + int dc_count = 0; + + fixed_t dc_iscale = 0; + fixed_t dc_texturefrac = 0; + uint32_t dc_texturefracx = 0; + uint32_t dc_textureheight = 0; + const uint8_t *dc_source = nullptr; + const uint8_t *dc_source2 = nullptr; + int dc_wall_fracbits = 0; + + uint32_t *dc_srcblend = nullptr; + uint32_t *dc_destblend = nullptr; + fixed_t dc_srcalpha = 0; + fixed_t dc_destalpha = 0; + + typedef void(SWPixelFormatDrawers::*WallDrawerFunc)(const WallDrawerArgs &args); + WallDrawerFunc wallfunc = nullptr; + + RenderViewport *dc_viewport = nullptr; + }; +} diff --git a/src/tarray.h b/src/tarray.h index fe0d8c1e24..d6962816ca 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -1,3 +1,4 @@ +#pragma once /* ** tarray.h ** Templated, automatically resizing array @@ -32,8 +33,6 @@ ** */ -#ifndef __TARRAY_H__ -#define __TARRAY_H__ #include #include @@ -1250,4 +1249,3 @@ protected: hash_t Position; }; -#endif //__TARRAY_H__ diff --git a/src/textures/automaptexture.cpp b/src/textures/automaptexture.cpp index 31163ed6aa..3bd16f5bca 100644 --- a/src/textures/automaptexture.cpp +++ b/src/textures/automaptexture.cpp @@ -122,6 +122,7 @@ void FAutomapTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/buildtexture.cpp b/src/textures/buildtexture.cpp index bfcc6333d3..cb7786200c 100644 --- a/src/textures/buildtexture.cpp +++ b/src/textures/buildtexture.cpp @@ -51,15 +51,14 @@ class FBuildTexture : public FTexture { public: - FBuildTexture (int tilenum, const BYTE *pixels, int width, int height, int left, int top); + FBuildTexture (int tilenum, const uint8_t *pixels, int width, int height, int left, int top); ~FBuildTexture (); - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); - void Unload (); + const uint8_t *GetColumn (unsigned int column, const Span **spans_out); + const uint8_t *GetPixels (); protected: - const BYTE *Pixels; + const uint8_t *Pixels; Span **Spans; }; @@ -70,7 +69,7 @@ protected: // //========================================================================== -FBuildTexture::FBuildTexture (int tilenum, const BYTE *pixels, int width, int height, int left, int top) +FBuildTexture::FBuildTexture (int tilenum, const uint8_t *pixels, int width, int height, int left, int top) : Pixels (pixels), Spans (NULL) { Width = width; @@ -103,18 +102,7 @@ FBuildTexture::~FBuildTexture () // //========================================================================== -void FBuildTexture::Unload () -{ - // Nothing to do, since the pixels are accessed from memory-mapped files directly -} - -//========================================================================== -// -// -// -//========================================================================== - -const BYTE *FBuildTexture::GetPixels () +const uint8_t *FBuildTexture::GetPixels () { return Pixels; } @@ -125,7 +113,7 @@ const BYTE *FBuildTexture::GetPixels () // //========================================================================== -const BYTE *FBuildTexture::GetColumn (unsigned int column, const Span **spans_out) +const uint8_t *FBuildTexture::GetColumn (unsigned int column, const Span **spans_out) { if (column >= Width) { @@ -159,22 +147,22 @@ const BYTE *FBuildTexture::GetColumn (unsigned int column, const Span **spans_ou void FTextureManager::AddTiles (void *tiles) { -// int numtiles = LittleLong(((DWORD *)tiles)[1]); // This value is not reliable - int tilestart = LittleLong(((DWORD *)tiles)[2]); - int tileend = LittleLong(((DWORD *)tiles)[3]); - const WORD *tilesizx = &((const WORD *)tiles)[8]; - const WORD *tilesizy = &tilesizx[tileend - tilestart + 1]; - const DWORD *picanm = (const DWORD *)&tilesizy[tileend - tilestart + 1]; - BYTE *tiledata = (BYTE *)&picanm[tileend - tilestart + 1]; +// int numtiles = LittleLong(((uint32_t *)tiles)[1]); // This value is not reliable + int tilestart = LittleLong(((uint32_t *)tiles)[2]); + int tileend = LittleLong(((uint32_t *)tiles)[3]); + const uint16_t *tilesizx = &((const uint16_t *)tiles)[8]; + const uint16_t *tilesizy = &tilesizx[tileend - tilestart + 1]; + const uint32_t *picanm = (const uint32_t *)&tilesizy[tileend - tilestart + 1]; + uint8_t *tiledata = (uint8_t *)&picanm[tileend - tilestart + 1]; for (int i = tilestart; i <= tileend; ++i) { int pic = i - tilestart; int width = LittleShort(tilesizx[pic]); int height = LittleShort(tilesizy[pic]); - DWORD anm = LittleLong(picanm[pic]); - int xoffs = (SBYTE)((anm >> 8) & 255) + width/2; - int yoffs = (SBYTE)((anm >> 16) & 255) + height/2; + uint32_t anm = LittleLong(picanm[pic]); + int xoffs = (int8_t)((anm >> 8) & 255) + width/2; + int yoffs = (int8_t)((anm >> 16) & 255) + height/2; int size = width*height; FTextureID texnum; FTexture *tex; @@ -265,14 +253,14 @@ void FTextureManager::AddTiles (void *tiles) int FTextureManager::CountTiles (void *tiles) { - int version = LittleLong(*(DWORD *)tiles); + int version = LittleLong(*(uint32_t *)tiles); if (version != 1) { return 0; } - int tilestart = LittleLong(((DWORD *)tiles)[2]); - int tileend = LittleLong(((DWORD *)tiles)[3]); + int tilestart = LittleLong(((uint32_t *)tiles)[2]); + int tileend = LittleLong(((uint32_t *)tiles)[3]); return tileend >= tilestart ? tileend - tilestart + 1 : 0; } @@ -325,7 +313,7 @@ int FTextureManager::CountBuildTiles () } size_t len = Q_filelength (f); - BYTE *art = new BYTE[len]; + uint8_t *art = new uint8_t[len]; if (fread (art, 1, len, f) != len || (numtiles = CountTiles(art)) == 0) { delete[] art; @@ -350,7 +338,7 @@ int FTextureManager::CountBuildTiles () break; } - BYTE *art = new BYTE[Wads.LumpLength (lumpnum)]; + uint8_t *art = new uint8_t[Wads.LumpLength (lumpnum)]; Wads.ReadLump (lumpnum, art); if ((numtiles = CountTiles(art)) == 0) diff --git a/src/textures/canvastexture.cpp b/src/textures/canvastexture.cpp index 062c3af1d3..ed72b0eee3 100644 --- a/src/textures/canvastexture.cpp +++ b/src/textures/canvastexture.cpp @@ -53,7 +53,6 @@ FCanvasTexture::FCanvasTexture (const char *name, int width, int height) DummySpans[1].TopOffset = 0; DummySpans[1].Length = 0; UseType = TEX_Wall; - Canvas = NULL; bNeedsUpdate = true; bDidUpdate = false; bHasCanvas = true; @@ -66,7 +65,7 @@ FCanvasTexture::~FCanvasTexture () Unload (); } -const BYTE *FCanvasTexture::GetColumn (unsigned int column, const Span **spans_out) +const uint8_t *FCanvasTexture::GetColumn (unsigned int column, const Span **spans_out) { bNeedsUpdate = true; if (Canvas == NULL) @@ -91,7 +90,7 @@ const BYTE *FCanvasTexture::GetColumn (unsigned int column, const Span **spans_o return Pixels + column*Height; } -const BYTE *FCanvasTexture::GetPixels () +const uint8_t *FCanvasTexture::GetPixels () { bNeedsUpdate = true; if (Canvas == NULL) @@ -101,41 +100,91 @@ const BYTE *FCanvasTexture::GetPixels () return Pixels; } +const uint32_t *FCanvasTexture::GetPixelsBgra() +{ + bNeedsUpdate = true; + if (CanvasBgra == NULL) + { + MakeTextureBgra(); + } + return PixelsBgra; +} + void FCanvasTexture::MakeTexture () { - Canvas = new DSimpleCanvas (Width, Height); + Canvas = new DSimpleCanvas (Width, Height, false); Canvas->Lock (); GC::AddSoftRoot(Canvas); + if (Width != Height || Width != Canvas->GetPitch()) { - Pixels = new BYTE[Width*Height]; + Pixels = new uint8_t[Width*Height]; bPixelsAllocated = true; } else { - Pixels = Canvas->GetBuffer(); + Pixels = (uint8_t*)Canvas->GetBuffer(); bPixelsAllocated = false; } + // Draw a special "unrendered" initial texture into the buffer. memset (Pixels, 0, Width*Height/2); memset (Pixels+Width*Height/2, 255, Width*Height/2); } +void FCanvasTexture::MakeTextureBgra() +{ + CanvasBgra = new DSimpleCanvas(Width, Height, true); + CanvasBgra->Lock(); + GC::AddSoftRoot(CanvasBgra); + + if (Width != Height || Width != CanvasBgra->GetPitch()) + { + PixelsBgra = new uint32_t[Width*Height]; + bPixelsAllocatedBgra = true; + } + else + { + PixelsBgra = (uint32_t*)CanvasBgra->GetBuffer(); + bPixelsAllocatedBgra = false; + } + + // Draw a special "unrendered" initial texture into the buffer. + memset(PixelsBgra, 0, Width*Height / 2 * 4); + memset(PixelsBgra + Width*Height / 2, 255, Width*Height / 2 * 4); +} + void FCanvasTexture::Unload () { if (bPixelsAllocated) { - if (Pixels != NULL) delete [] Pixels; + if (Pixels != NULL) delete[] Pixels; bPixelsAllocated = false; Pixels = NULL; } + if (bPixelsAllocatedBgra) + { + if (PixelsBgra != NULL) delete[] PixelsBgra; + bPixelsAllocatedBgra = false; + PixelsBgra = NULL; + } + if (Canvas != NULL) { GC::DelSoftRoot(Canvas); Canvas->Destroy(); Canvas = NULL; } + + if (CanvasBgra != NULL) + { + GC::DelSoftRoot(CanvasBgra); + CanvasBgra->Destroy(); + CanvasBgra = NULL; + } + + FTexture::Unload(); } bool FCanvasTexture::CheckModified () diff --git a/src/textures/ddstexture.cpp b/src/textures/ddstexture.cpp index f4fa03e2fe..e340d4e81a 100644 --- a/src/textures/ddstexture.cpp +++ b/src/textures/ddstexture.cpp @@ -59,42 +59,45 @@ // Since we want this to compile under Linux too, we need to define this // stuff ourselves instead of including a DirectX header. -#define ID_DDS MAKE_ID('D','D','S',' ') -#define ID_DXT1 MAKE_ID('D','X','T','1') -#define ID_DXT2 MAKE_ID('D','X','T','2') -#define ID_DXT3 MAKE_ID('D','X','T','3') -#define ID_DXT4 MAKE_ID('D','X','T','4') -#define ID_DXT5 MAKE_ID('D','X','T','5') +enum +{ + ID_DDS = MAKE_ID('D', 'D', 'S', ' '), + ID_DXT1 = MAKE_ID('D', 'X', 'T', '1'), + ID_DXT2 = MAKE_ID('D', 'X', 'T', '2'), + ID_DXT3 = MAKE_ID('D', 'X', 'T', '3'), + ID_DXT4 = MAKE_ID('D', 'X', 'T', '4'), + ID_DXT5 = MAKE_ID('D', 'X', 'T', '5'), -// Bits in dwFlags -#define DDSD_CAPS 0x00000001 -#define DDSD_HEIGHT 0x00000002 -#define DDSD_WIDTH 0x00000004 -#define DDSD_PITCH 0x00000008 -#define DDSD_PIXELFORMAT 0x00001000 -#define DDSD_MIPMAPCOUNT 0x00020000 -#define DDSD_LINEARSIZE 0x00080000 -#define DDSD_DEPTH 0x00800000 + // Bits in dwFlags + DDSD_CAPS = 0x00000001, + DDSD_HEIGHT = 0x00000002, + DDSD_WIDTH = 0x00000004, + DDSD_PITCH = 0x00000008, + DDSD_PIXELFORMAT = 0x00001000, + DDSD_MIPMAPCOUNT = 0x00020000, + DDSD_LINEARSIZE = 0x00080000, + DDSD_DEPTH = 0x00800000, -// Bits in ddpfPixelFormat -#define DDPF_ALPHAPIXELS 0x00000001 -#define DDPF_FOURCC 0x00000004 -#define DDPF_RGB 0x00000040 + // Bits in ddpfPixelFormat + DDPF_ALPHAPIXELS = 0x00000001, + DDPF_FOURCC = 0x00000004, + DDPF_RGB = 0x00000040, -// Bits in DDSCAPS2.dwCaps1 -#define DDSCAPS_COMPLEX 0x00000008 -#define DDSCAPS_TEXTURE 0x00001000 -#define DDSCAPS_MIPMAP 0x00400000 + // Bits in DDSCAPS2.dwCaps1 + DDSCAPS_COMPLEX = 0x00000008, + DDSCAPS_TEXTURE = 0x00001000, + DDSCAPS_MIPMAP = 0x00400000, -// Bits in DDSCAPS2.dwCaps2 -#define DDSCAPS2_CUBEMAP 0x00000200 -#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400 -#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800 -#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000 -#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000 -#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000 -#define DDSCAPS2_CUBEMAP_NEGATIZEZ 0x00008000 -#define DDSCAPS2_VOLUME 0x00200000 + // Bits in DDSCAPS2.dwCaps2 + DDSCAPS2_CUBEMAP = 0x00000200, + DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400, + DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800, + DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000, + DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000, + DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000, + DDSCAPS2_CUBEMAP_NEGATIZEZ = 0x00008000, + DDSCAPS2_VOLUME = 0x00200000, +}; //========================================================================== // @@ -132,15 +135,15 @@ struct DDSURFACEDESC2 uint32_t Depth; uint32_t MipMapCount; uint32_t Reserved1[11]; - DDPIXELFORMAT PixelFormat; - DDCAPS2 Caps; + DDPIXELFORMAT PixelFormat; + DDCAPS2 Caps; uint32_t Reserved2; }; struct DDSFileHeader { uint32_t Magic; - DDSURFACEDESC2 Desc; + DDSURFACEDESC2 Desc; }; @@ -401,6 +404,7 @@ void FDDSTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/flattexture.cpp b/src/textures/flattexture.cpp index 2526db9734..6e584f6948 100644 --- a/src/textures/flattexture.cpp +++ b/src/textures/flattexture.cpp @@ -138,6 +138,7 @@ void FFlatTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/imgztexture.cpp b/src/textures/imgztexture.cpp index e1b573d02b..1689289d09 100644 --- a/src/textures/imgztexture.cpp +++ b/src/textures/imgztexture.cpp @@ -142,6 +142,7 @@ void FIMGZTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/jpegtexture.cpp b/src/textures/jpegtexture.cpp index 404d5c7be8..02bb828294 100644 --- a/src/textures/jpegtexture.cpp +++ b/src/textures/jpegtexture.cpp @@ -295,11 +295,9 @@ FJPEGTexture::~FJPEGTexture () void FJPEGTexture::Unload () { - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } + delete[] Pixels; + Pixels = NULL; + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index 04dd6ffedf..63b86d7a94 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -359,6 +359,7 @@ void FMultiPatchTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/patchtexture.cpp b/src/textures/patchtexture.cpp index 3bdc3402c6..2393357bd5 100644 --- a/src/textures/patchtexture.cpp +++ b/src/textures/patchtexture.cpp @@ -184,6 +184,7 @@ void FPatchTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/pcxtexture.cpp b/src/textures/pcxtexture.cpp index c5be4bdce0..757877b035 100644 --- a/src/textures/pcxtexture.cpp +++ b/src/textures/pcxtexture.cpp @@ -191,6 +191,7 @@ void FPCXTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/pngtexture.cpp b/src/textures/pngtexture.cpp index 82de8b74d9..63948bceaa 100644 --- a/src/textures/pngtexture.cpp +++ b/src/textures/pngtexture.cpp @@ -373,11 +373,9 @@ FPNGTexture::~FPNGTexture () void FPNGTexture::Unload () { - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } + delete[] Pixels; + Pixels = NULL; + FTexture::Unload(); } //========================================================================== @@ -450,6 +448,7 @@ const uint8_t *FPNGTexture::GetPixels () return Pixels; } + //========================================================================== // // diff --git a/src/textures/rawpagetexture.cpp b/src/textures/rawpagetexture.cpp index 956b2a6a90..bdcebbbafb 100644 --- a/src/textures/rawpagetexture.cpp +++ b/src/textures/rawpagetexture.cpp @@ -206,6 +206,7 @@ void FRawPageTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/texture.cpp b/src/textures/texture.cpp index 9c1b5fdb42..74f9cd04be 100644 --- a/src/textures/texture.cpp +++ b/src/textures/texture.cpp @@ -56,7 +56,7 @@ struct TexCreateInfo int usetype; }; -BYTE FTexture::GrayMap[256]; +uint8_t FTexture::GrayMap[256]; void FTexture::InitGrayMap() { @@ -176,6 +176,37 @@ FTexture::~FTexture () KillNative(); } +void FTexture::Unload() +{ + PixelsBgra = std::vector(); +} + +const uint32_t *FTexture::GetColumnBgra(unsigned int column, const Span **spans_out) +{ + const uint32_t *pixels = GetPixelsBgra(); + + column %= Width; + + if (spans_out != nullptr) + GetColumn(column, spans_out); + return pixels + column * Height; +} + +const uint32_t *FTexture::GetPixelsBgra() +{ + if (PixelsBgra.empty() || CheckModified()) + { + if (!GetColumn(0, nullptr)) + return nullptr; + + FBitmap bitmap; + bitmap.Create(GetWidth(), GetHeight()); + CopyTrueColorPixels(&bitmap, 0, 0); + GenerateBgraFromBitmap(bitmap); + } + return PixelsBgra.data(); +} + bool FTexture::CheckModified () { return false; @@ -222,7 +253,7 @@ void FTexture::HackHack (int newheight) { } -FTexture::Span **FTexture::CreateSpans (const BYTE *pixels) const +FTexture::Span **FTexture::CreateSpans (const uint8_t *pixels) const { Span **spans, *span; @@ -244,7 +275,7 @@ FTexture::Span **FTexture::CreateSpans (const BYTE *pixels) const int numcols = Width; int numrows = Height; int numspans = numcols; // One span to terminate each column - const BYTE *data_p; + const uint8_t *data_p; bool newspan; int x, y; @@ -320,9 +351,213 @@ void FTexture::FreeSpans (Span **spans) const M_Free (spans); } -void FTexture::CopyToBlock (BYTE *dest, int dwidth, int dheight, int xpos, int ypos, int rotate, const BYTE *translation) +void FTexture::GenerateBgraFromBitmap(const FBitmap &bitmap) { - const BYTE *pixels = GetPixels(); + CreatePixelsBgraWithMipmaps(); + + // Transpose + const uint32_t *src = (const uint32_t *)bitmap.GetPixels(); + uint32_t *dest = PixelsBgra.data(); + for (int x = 0; x < Width; x++) + { + for (int y = 0; y < Height; y++) + { + dest[y + x * Height] = src[x + y * Width]; + } + } + + GenerateBgraMipmaps(); +} + +void FTexture::CreatePixelsBgraWithMipmaps() +{ + int levels = MipmapLevels(); + int buffersize = 0; + for (int i = 0; i < levels; i++) + { + int w = MAX(Width >> i, 1); + int h = MAX(Height >> i, 1); + buffersize += w * h; + } + PixelsBgra.resize(buffersize, 0xffff0000); +} + +int FTexture::MipmapLevels() const +{ + int widthbits = 0; + while ((Width >> widthbits) != 0) widthbits++; + + int heightbits = 0; + while ((Height >> heightbits) != 0) heightbits++; + + return MAX(widthbits, heightbits); +} + +void FTexture::GenerateBgraMipmaps() +{ + struct Color4f + { + float a, r, g, b; + Color4f operator*(const Color4f &v) const { return Color4f{ a * v.a, r * v.r, g * v.g, b * v.b }; } + Color4f operator/(const Color4f &v) const { return Color4f{ a / v.a, r / v.r, g / v.g, b / v.b }; } + Color4f operator+(const Color4f &v) const { return Color4f{ a + v.a, r + v.r, g + v.g, b + v.b }; } + Color4f operator-(const Color4f &v) const { return Color4f{ a - v.a, r - v.r, g - v.g, b - v.b }; } + Color4f operator*(float s) const { return Color4f{ a * s, r * s, g * s, b * s }; } + Color4f operator/(float s) const { return Color4f{ a / s, r / s, g / s, b / s }; } + Color4f operator+(float s) const { return Color4f{ a + s, r + s, g + s, b + s }; } + Color4f operator-(float s) const { return Color4f{ a - s, r - s, g - s, b - s }; } + }; + + int levels = MipmapLevels(); + std::vector image(PixelsBgra.size()); + + // Convert to normalized linear colorspace + { + for (int x = 0; x < Width; x++) + { + for (int y = 0; y < Height; y++) + { + uint32_t c8 = PixelsBgra[x * Height + y]; + Color4f c; + c.a = powf(APART(c8) * (1.0f / 255.0f), 2.2f); + c.r = powf(RPART(c8) * (1.0f / 255.0f), 2.2f); + c.g = powf(GPART(c8) * (1.0f / 255.0f), 2.2f); + c.b = powf(BPART(c8) * (1.0f / 255.0f), 2.2f); + image[x * Height + y] = c; + } + } + } + + // Generate mipmaps + { + std::vector smoothed(Width * Height); + Color4f *src = image.data(); + Color4f *dest = src + Width * Height; + for (int i = 1; i < levels; i++) + { + int srcw = MAX(Width >> (i - 1), 1); + int srch = MAX(Height >> (i - 1), 1); + int w = MAX(Width >> i, 1); + int h = MAX(Height >> i, 1); + + // Downscale + for (int x = 0; x < w; x++) + { + int sx0 = x * 2; + int sx1 = MIN((x + 1) * 2, srcw - 1); + for (int y = 0; y < h; y++) + { + int sy0 = y * 2; + int sy1 = MIN((y + 1) * 2, srch - 1); + + Color4f src00 = src[sy0 + sx0 * srch]; + Color4f src01 = src[sy1 + sx0 * srch]; + Color4f src10 = src[sy0 + sx1 * srch]; + Color4f src11 = src[sy1 + sx1 * srch]; + Color4f c = (src00 + src01 + src10 + src11) * 0.25f; + + dest[y + x * h] = c; + } + } + + // Sharpen filter with a 3x3 kernel: + for (int x = 0; x < w; x++) + { + for (int y = 0; y < h; y++) + { + Color4f c = { 0.0f, 0.0f, 0.0f, 0.0f }; + for (int kx = -1; kx < 2; kx++) + { + for (int ky = -1; ky < 2; ky++) + { + int a = y + ky; + int b = x + kx; + if (a < 0) a = h - 1; + if (a == h) a = 0; + if (b < 0) b = w - 1; + if (b == w) b = 0; + c = c + dest[a + b * h]; + } + } + c = c * (1.0f / 9.0f); + smoothed[y + x * h] = c; + } + } + float k = 0.08f; + for (int j = 0; j < w * h; j++) + dest[j] = dest[j] + (dest[j] - smoothed[j]) * k; + + src = dest; + dest += w * h; + } + } + + // Convert to bgra8 sRGB colorspace + { + Color4f *src = image.data() + Width * Height; + uint32_t *dest = PixelsBgra.data() + Width * Height; + for (int i = 1; i < levels; i++) + { + int w = MAX(Width >> i, 1); + int h = MAX(Height >> i, 1); + for (int j = 0; j < w * h; j++) + { + uint32_t a = (uint32_t)clamp(powf(MAX(src[j].a, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f); + uint32_t r = (uint32_t)clamp(powf(MAX(src[j].r, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f); + uint32_t g = (uint32_t)clamp(powf(MAX(src[j].g, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f); + uint32_t b = (uint32_t)clamp(powf(MAX(src[j].b, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f); + dest[j] = (a << 24) | (r << 16) | (g << 8) | b; + } + src += w * h; + dest += w * h; + } + } +} + +void FTexture::GenerateBgraMipmapsFast() +{ + uint32_t *src = PixelsBgra.data(); + uint32_t *dest = src + Width * Height; + int levels = MipmapLevels(); + for (int i = 1; i < levels; i++) + { + int srcw = MAX(Width >> (i - 1), 1); + int srch = MAX(Height >> (i - 1), 1); + int w = MAX(Width >> i, 1); + int h = MAX(Height >> i, 1); + + for (int x = 0; x < w; x++) + { + int sx0 = x * 2; + int sx1 = MIN((x + 1) * 2, srcw - 1); + + for (int y = 0; y < h; y++) + { + int sy0 = y * 2; + int sy1 = MIN((y + 1) * 2, srch - 1); + + uint32_t src00 = src[sy0 + sx0 * srch]; + uint32_t src01 = src[sy1 + sx0 * srch]; + uint32_t src10 = src[sy0 + sx1 * srch]; + uint32_t src11 = src[sy1 + sx1 * srch]; + + uint32_t alpha = (APART(src00) + APART(src01) + APART(src10) + APART(src11) + 2) / 4; + uint32_t red = (RPART(src00) + RPART(src01) + RPART(src10) + RPART(src11) + 2) / 4; + uint32_t green = (GPART(src00) + GPART(src01) + GPART(src10) + GPART(src11) + 2) / 4; + uint32_t blue = (BPART(src00) + BPART(src01) + BPART(src10) + BPART(src11) + 2) / 4; + + dest[y + x * h] = (alpha << 24) | (red << 16) | (green << 8) | blue; + } + } + + src = dest; + dest += w * h; + } +} + +void FTexture::CopyToBlock (uint8_t *dest, int dwidth, int dheight, int xpos, int ypos, int rotate, const uint8_t *translation) +{ + const uint8_t *pixels = GetPixels(); int srcwidth = Width; int srcheight = Height; int step_x = Height; @@ -340,7 +575,7 @@ void FTexture::CopyToBlock (BYTE *dest, int dwidth, int dheight, int xpos, int y for (int y = 0; y < srcheight; y++, pos++) { // the optimizer is doing a good enough job here so there's no need to optimize this by hand - BYTE v = pixels[y * step_y + x * step_x]; + uint8_t v = pixels[y * step_y + x * step_x]; if (v != 0) dest[pos] = v; } } @@ -352,7 +587,7 @@ void FTexture::CopyToBlock (BYTE *dest, int dwidth, int dheight, int xpos, int y int pos = x * dheight; for (int y = 0; y < srcheight; y++, pos++) { - BYTE v = pixels[y * step_y + x * step_x]; + uint8_t v = pixels[y * step_y + x * step_x]; if (v != 0) dest[pos] = translation[v]; } } @@ -363,7 +598,7 @@ void FTexture::CopyToBlock (BYTE *dest, int dwidth, int dheight, int xpos, int y // Converts a texture between row-major and column-major format // by flipping it about the X=Y axis. -void FTexture::FlipSquareBlock (BYTE *block, int x, int y) +void FTexture::FlipSquareBlock (uint8_t *block, int x, int y) { int i, j; @@ -371,31 +606,54 @@ void FTexture::FlipSquareBlock (BYTE *block, int x, int y) for (i = 0; i < x; ++i) { - BYTE *corner = block + x*i + i; + uint8_t *corner = block + x*i + i; int count = x - i; if (count & 1) { count--; - swapvalues (corner[count], corner[count*x]); + swapvalues (corner[count], corner[count*x]); } for (j = 0; j < count; j += 2) { - swapvalues (corner[j], corner[j*x]); - swapvalues (corner[j+1], corner[(j+1)*x]); + swapvalues (corner[j], corner[j*x]); + swapvalues (corner[j+1], corner[(j+1)*x]); } } } -void FTexture::FlipSquareBlockRemap (BYTE *block, int x, int y, const BYTE *remap) +void FTexture::FlipSquareBlockBgra(uint32_t *block, int x, int y) { int i, j; - BYTE t; if (x != y) return; for (i = 0; i < x; ++i) { - BYTE *corner = block + x*i + i; + uint32_t *corner = block + x*i + i; + int count = x - i; + if (count & 1) + { + count--; + swapvalues(corner[count], corner[count*x]); + } + for (j = 0; j < count; j += 2) + { + swapvalues(corner[j], corner[j*x]); + swapvalues(corner[j + 1], corner[(j + 1)*x]); + } + } +} + +void FTexture::FlipSquareBlockRemap (uint8_t *block, int x, int y, const uint8_t *remap) +{ + int i, j; + uint8_t t; + + if (x != y) return; + + for (i = 0; i < x; ++i) + { + uint8_t *corner = block + x*i + i; int count = x - i; if (count & 1) { @@ -416,7 +674,7 @@ void FTexture::FlipSquareBlockRemap (BYTE *block, int x, int y, const BYTE *rema } } -void FTexture::FlipNonSquareBlock (BYTE *dst, const BYTE *src, int x, int y, int srcpitch) +void FTexture::FlipNonSquareBlock (uint8_t *dst, const uint8_t *src, int x, int y, int srcpitch) { int i, j; @@ -429,7 +687,20 @@ void FTexture::FlipNonSquareBlock (BYTE *dst, const BYTE *src, int x, int y, int } } -void FTexture::FlipNonSquareBlockRemap (BYTE *dst, const BYTE *src, int x, int y, int srcpitch, const BYTE *remap) +void FTexture::FlipNonSquareBlockBgra(uint32_t *dst, const uint32_t *src, int x, int y, int srcpitch) +{ + int i, j; + + for (i = 0; i < x; ++i) + { + for (j = 0; j < y; ++j) + { + dst[i*y + j] = src[i + j*srcpitch]; + } + } +} + +void FTexture::FlipNonSquareBlockRemap (uint8_t *dst, const uint8_t *src, int x, int y, int srcpitch, const uint8_t *remap) { int i, j; @@ -479,9 +750,9 @@ void FTexture::KillNative() // color data. Note that the buffer expects row-major data, since that's // generally more convenient for any non-Doom image formats, and it doesn't // need to be used by any of Doom's column drawing routines. -void FTexture::FillBuffer(BYTE *buff, int pitch, int height, FTextureFormat fmt) +void FTexture::FillBuffer(uint8_t *buff, int pitch, int height, FTextureFormat fmt) { - const BYTE *pix; + const uint8_t *pix; int x, y, w, h, stride; w = GetWidth(); @@ -495,7 +766,7 @@ void FTexture::FillBuffer(BYTE *buff, int pitch, int height, FTextureFormat fmt) stride = pitch - w; for (y = 0; y < h; ++y) { - const BYTE *pix2 = pix; + const uint8_t *pix2 = pix; for (x = 0; x < w; ++x) { *buff++ = *pix2; @@ -579,7 +850,7 @@ void FTexture::SetScaledSize(int fitwidth, int fitheight) namespace { - PalEntry averageColor(const DWORD *data, int size, int maxout) + PalEntry averageColor(const uint32_t *data, int size, int maxout) { int i; unsigned int r, g, b; @@ -630,10 +901,10 @@ PalEntry FTexture::GetSkyCapColor(bool bottom) const uint32_t *buffer = (const uint32_t *)bitmap.GetPixels(); if (buffer) { - CeilingSkyColor = averageColor((DWORD *)buffer, w * MIN(30, h), 0); + CeilingSkyColor = averageColor((uint32_t *)buffer, w * MIN(30, h), 0); if (h>30) { - FloorSkyColor = averageColor(((DWORD *)buffer) + (h - 30)*w, w * 30, 0); + FloorSkyColor = averageColor(((uint32_t *)buffer) + (h - 30)*w, w * 30, 0); } else FloorSkyColor = CeilingSkyColor; } @@ -652,10 +923,6 @@ FDummyTexture::FDummyTexture () UseType = TEX_Null; } -void FDummyTexture::Unload () -{ -} - void FDummyTexture::SetSize (int width, int height) { Width = width; @@ -664,13 +931,13 @@ void FDummyTexture::SetSize (int width, int height) } // This must never be called -const BYTE *FDummyTexture::GetColumn (unsigned int column, const Span **spans_out) +const uint8_t *FDummyTexture::GetColumn (unsigned int column, const Span **spans_out) { return NULL; } // And this also must never be called -const BYTE *FDummyTexture::GetPixels () +const uint8_t *FDummyTexture::GetPixels () { return NULL; } diff --git a/src/textures/textures.h b/src/textures/textures.h index 3e441b7bc2..c5a7a9819c 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -3,6 +3,7 @@ #include "doomtype.h" #include "vectors.h" +#include struct FloatRect { @@ -52,15 +53,15 @@ public: struct FAnimDef { FTextureID BasePic; - WORD NumFrames; - WORD CurFrame; - BYTE AnimType; + uint16_t NumFrames; + uint16_t CurFrame; + uint8_t AnimType; bool bDiscrete; // taken out of AnimType to have better control - DWORD SwitchTime; // Time to advance to next frame + uint32_t SwitchTime; // Time to advance to next frame struct FAnimFrame { - DWORD SpeedMin; // Speeds are in ms, not tics - DWORD SpeedRange; + uint32_t SpeedMin; // Speeds are in ms, not tics + uint32_t SpeedRange; FTextureID FramePic; } Frames[1]; enum @@ -72,20 +73,20 @@ struct FAnimDef ANIM_Random }; - void SetSwitchTime (DWORD mstime); + void SetSwitchTime (uint32_t mstime); }; struct FSwitchDef { FTextureID PreTexture; // texture to switch from FSwitchDef *PairDef; // switch def to use to return to PreTexture - WORD NumFrames; // # of animation frames + uint16_t NumFrames; // # of animation frames bool QuestPanel; // Special texture for Strife mission int Sound; // sound to play at start of animation. Changed to int to avoiud having to include s_sound here. struct frame // Array of times followed by array of textures { // actual length of each array is - WORD TimeMin; - WORD TimeRnd; + uint16_t TimeMin; + uint16_t TimeRnd; FTextureID Texture; } frames[1]; }; @@ -105,11 +106,11 @@ struct FDoorAnimation // textures from the TEXTURE1/2 lists of patches. struct patch_t { - SWORD width; // bounding box size - SWORD height; - SWORD leftoffset; // pixels to the left of origin - SWORD topoffset; // pixels below the origin - DWORD columnofs[]; // only [width] used + int16_t width; // bounding box size + int16_t height; + int16_t leftoffset; // pixels to the left of origin + int16_t topoffset; // pixels below the origin + uint32_t columnofs[]; // only [width] used // the [0] is &columnofs[width] }; @@ -139,9 +140,9 @@ public: static FTexture *CreateTexture(int lumpnum, int usetype); virtual ~FTexture (); - SWORD LeftOffset, TopOffset; + int16_t LeftOffset, TopOffset; - BYTE WidthBits, HeightBits; + uint8_t WidthBits, HeightBits; DVector2 Scale; @@ -149,23 +150,23 @@ public: FTextureID id; FString Name; - BYTE UseType; // This texture's primary purpose + uint8_t UseType; // This texture's primary purpose - BYTE bNoDecals:1; // Decals should not stick to texture - BYTE bNoRemap0:1; // Do not remap color 0 (used by front layer of parallax skies) - BYTE bWorldPanning:1; // Texture is panned in world units rather than texels - BYTE bMasked:1; // Texture (might) have holes - BYTE bAlphaTexture:1; // Texture is an alpha channel without color information - BYTE bHasCanvas:1; // Texture is based off FCanvasTexture - BYTE bWarped:2; // This is a warped texture. Used to avoid multiple warps on one texture - BYTE bComplex:1; // Will be used to mark extended MultipatchTextures that have to be + uint8_t bNoDecals:1; // Decals should not stick to texture + uint8_t bNoRemap0:1; // Do not remap color 0 (used by front layer of parallax skies) + uint8_t bWorldPanning:1; // Texture is panned in world units rather than texels + uint8_t bMasked:1; // Texture (might) have holes + uint8_t bAlphaTexture:1; // Texture is an alpha channel without color information + uint8_t bHasCanvas:1; // Texture is based off FCanvasTexture + uint8_t bWarped:2; // This is a warped texture. Used to avoid multiple warps on one texture + uint8_t bComplex:1; // Will be used to mark extended MultipatchTextures that have to be // fully composited before subjected to any kind of postprocessing instead of // doing it per patch. - BYTE bMultiPatch:1; // This is a multipatch texture (we really could use real type info for textures...) - BYTE bKeepAround:1; // This texture was used as part of a multi-patch texture. Do not free it. + uint8_t bMultiPatch:1; // This is a multipatch texture (we really could use real type info for textures...) + uint8_t bKeepAround:1; // This texture was used as part of a multi-patch texture. Do not free it. - WORD Rotations; - SWORD SkyOffset; + uint16_t Rotations; + int16_t SkyOffset; enum // UseTypes { @@ -188,16 +189,25 @@ public: struct Span { - WORD TopOffset; - WORD Length; // A length of 0 terminates this column + uint16_t TopOffset; + uint16_t Length; // A length of 0 terminates this column }; // Returns a single column of the texture - virtual const BYTE *GetColumn (unsigned int column, const Span **spans_out) = 0; + virtual const uint8_t *GetColumn (unsigned int column, const Span **spans_out) = 0; + + // Returns a single column of the texture, in BGRA8 format + virtual const uint32_t *GetColumnBgra(unsigned int column, const Span **spans_out); // Returns the whole texture, stored in column-major order - virtual const BYTE *GetPixels () = 0; - + virtual const uint8_t *GetPixels () = 0; + + // Returns the whole texture, stored in column-major order, in BGRA8 format + virtual const uint32_t *GetPixelsBgra(); + + // Returns true if GetPixelsBgra includes mipmaps + virtual bool Mipmapped() { return true; } + virtual int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate=0, FCopyInfo *inf = NULL); int CopyTrueColorTranslated(FBitmap *bmp, int x, int y, int rotate, FRemapTable *remap, FCopyInfo *inf = NULL); virtual bool UseBasePalette(); @@ -205,7 +215,7 @@ public: virtual FTexture *GetRedirect(bool wantwarped); virtual FTexture *GetRawTexture(); // for FMultiPatchTexture to override - virtual void Unload () = 0; + virtual void Unload (); // Returns the native pixel format for this image virtual FTextureFormat GetFormat(); @@ -217,7 +227,7 @@ public: void KillNative(); // Fill the native texture buffer with pixel data for this image - virtual void FillBuffer(BYTE *buff, int pitch, int height, FTextureFormat fmt); + virtual void FillBuffer(uint8_t *buff, int pitch, int height, FTextureFormat fmt); int GetWidth () { return Width; } int GetHeight () { return Height; } @@ -236,12 +246,12 @@ public: virtual void SetFrontSkyLayer(); - void CopyToBlock (BYTE *dest, int dwidth, int dheight, int x, int y, const BYTE *translation=NULL) + void CopyToBlock (uint8_t *dest, int dwidth, int dheight, int x, int y, const uint8_t *translation=NULL) { CopyToBlock(dest, dwidth, dheight, x, y, 0, translation); } - void CopyToBlock (BYTE *dest, int dwidth, int dheight, int x, int y, int rotate, const BYTE *translation=NULL); + void CopyToBlock (uint8_t *dest, int dwidth, int dheight, int x, int y, int rotate, const uint8_t *translation=NULL); // Returns true if the next call to GetPixels() will return an image different from the // last call to GetPixels(). This should be considered valid only if a call to CheckModified() @@ -268,13 +278,13 @@ public: virtual void HackHack (int newheight); // called by FMultipatchTexture to discover corrupt patches. protected: - WORD Width, Height, WidthMask; - static BYTE GrayMap[256]; + uint16_t Width, Height, WidthMask; + static uint8_t GrayMap[256]; FNativeTexture *Native; FTexture (const char *name = NULL, int lumpnum = -1); - Span **CreateSpans (const BYTE *pixels) const; + Span **CreateSpans (const uint8_t *pixels) const; void FreeSpans (Span **spans) const; void CalcBitSize (); void CopyInfo(FTexture *other) @@ -287,18 +297,29 @@ protected: gl_info.areas = NULL; } + std::vector PixelsBgra; + + void GenerateBgraFromBitmap(const FBitmap &bitmap); + void CreatePixelsBgraWithMipmaps(); + void GenerateBgraMipmaps(); + void GenerateBgraMipmapsFast(); + int MipmapLevels() const; + private: bool bSWSkyColorDone = false; PalEntry FloorSkyColor; PalEntry CeilingSkyColor; public: - static void FlipSquareBlock (BYTE *block, int x, int y); - static void FlipSquareBlockRemap (BYTE *block, int x, int y, const BYTE *remap); - static void FlipNonSquareBlock (BYTE *blockto, const BYTE *blockfrom, int x, int y, int srcpitch); - static void FlipNonSquareBlockRemap (BYTE *blockto, const BYTE *blockfrom, int x, int y, int srcpitch, const BYTE *remap); + static void FlipSquareBlock (uint8_t *block, int x, int y); + static void FlipSquareBlockBgra (uint32_t *block, int x, int y); + static void FlipSquareBlockRemap (uint8_t *block, int x, int y, const uint8_t *remap); + static void FlipNonSquareBlock (uint8_t *blockto, const uint8_t *blockfrom, int x, int y, int srcpitch); + static void FlipNonSquareBlockBgra (uint32_t *blockto, const uint32_t *blockfrom, int x, int y, int srcpitch); + static void FlipNonSquareBlockRemap (uint8_t *blockto, const uint8_t *blockfrom, int x, int y, int srcpitch, const uint8_t *remap); friend class D3DTex; + friend class OpenGLSWFrameBuffer; public: @@ -449,7 +470,7 @@ public: int NumTextures () const { return (int)Textures.Size(); } - void UpdateAnimations (DWORD mstime); + void UpdateAnimations (uint32_t mstime); int GuesstimateNumTextures (); FSwitchDef *FindSwitch (FTextureID texture); @@ -472,7 +493,7 @@ private: void FixAnimations (); void InitAnimated (); void InitAnimDefs (); - FAnimDef *AddSimpleAnim (FTextureID picnum, int animcount, DWORD speedmin, DWORD speedrange=0); + FAnimDef *AddSimpleAnim (FTextureID picnum, int animcount, uint32_t speedmin, uint32_t speedrange=0); FAnimDef *AddComplexAnim (FTextureID picnum, const TArray &frames); void ParseAnim (FScanner &sc, int usetype); FAnimDef *ParseRangeAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing); @@ -480,7 +501,7 @@ private: void ParseWarp(FScanner &sc); void ParseCameraTexture(FScanner &sc); FTextureID ParseFramenum (FScanner &sc, FTextureID basepicnum, int usetype, bool allowMissing); - void ParseTime (FScanner &sc, DWORD &min, DWORD &max); + void ParseTime (FScanner &sc, uint32_t &min, uint32_t &max); FTexture *Texture(FTextureID id) { return Textures[id.GetIndex()].Texture; } void SetTranslation (FTextureID fromtexnum, FTextureID totexnum); void ParseAnimatedDoor(FScanner &sc); @@ -510,7 +531,7 @@ private: TArray mAnimations; TArray mSwitchDefs; TArray mAnimatedDoors; - TArray BuildTileFiles; + TArray BuildTileFiles; public: short sintable[2048]; // for texture warping enum @@ -524,9 +545,8 @@ class FDummyTexture : public FTexture { public: FDummyTexture (); - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); - void Unload (); + const uint8_t *GetColumn (unsigned int column, const Span **spans_out); + const uint8_t *GetPixels (); void SetSize (int width, int height); }; @@ -538,8 +558,9 @@ public: ~FWarpTexture (); virtual int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate=0, FCopyInfo *inf = NULL); - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); + const uint8_t *GetColumn (unsigned int column, const Span **spans_out); + const uint8_t *GetPixels (); + const uint32_t *GetPixelsBgra() override; void Unload (); bool CheckModified (); @@ -548,15 +569,15 @@ public: void SetSpeed(float fac) { Speed = fac; } FTexture *GetRedirect(bool wantwarped); - DWORD GenTime; + uint32_t GenTime; float Speed; int WidthOffsetMultiplier, HeightOffsetMultiplier; // [mxd] protected: FTexture *SourcePic; - BYTE *Pixels; + uint8_t *Pixels; Span **Spans; - virtual void MakeTexture (DWORD time); + virtual void MakeTexture (uint32_t time); int NextPo2 (int v); // [mxd] void SetupMultipliers (int width, int height); // [mxd] }; @@ -571,23 +592,30 @@ public: FCanvasTexture (const char *name, int width, int height); ~FCanvasTexture (); - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); + const uint8_t *GetColumn (unsigned int column, const Span **spans_out); + const uint8_t *GetPixels (); + const uint32_t *GetPixelsBgra() override; void Unload (); bool CheckModified (); void NeedUpdate() { bNeedsUpdate=true; } void SetUpdated() { bNeedsUpdate = false; bDidUpdate = true; bFirstUpdate = false; } DSimpleCanvas *GetCanvas() { return Canvas; } + DSimpleCanvas *GetCanvasBgra() { return CanvasBgra; } + bool Mipmapped() override { return false; } void MakeTexture (); + void MakeTextureBgra (); protected: - DSimpleCanvas *Canvas; - BYTE *Pixels; + DSimpleCanvas *Canvas = nullptr; + DSimpleCanvas *CanvasBgra = nullptr; + uint8_t *Pixels = nullptr; + uint32_t *PixelsBgra = nullptr; Span DummySpans[2]; - bool bNeedsUpdate; - bool bDidUpdate; - bool bPixelsAllocated; + bool bNeedsUpdate = true; + bool bDidUpdate = false; + bool bPixelsAllocated = false; + bool bPixelsAllocatedBgra = false; public: bool bFirstUpdate; diff --git a/src/textures/tgatexture.cpp b/src/textures/tgatexture.cpp index 746d8ad967..702da82b1e 100644 --- a/src/textures/tgatexture.cpp +++ b/src/textures/tgatexture.cpp @@ -181,6 +181,7 @@ void FTGATexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/warpbuffer.h b/src/textures/warpbuffer.h index 2516a838ae..673ba09467 100644 --- a/src/textures/warpbuffer.h +++ b/src/textures/warpbuffer.h @@ -1,3 +1,4 @@ +#include "textures/textures.h" template void WarpBuffer(TYPE *Pixels, const TYPE *source, int width, int height, int xmul, int ymul, unsigned time, float Speed, int warptype) diff --git a/src/textures/warptexture.cpp b/src/textures/warptexture.cpp index ee08148d20..08ce3103d6 100644 --- a/src/textures/warptexture.cpp +++ b/src/textures/warptexture.cpp @@ -39,6 +39,7 @@ #include "r_utility.h" #include "textures/textures.h" #include "warpbuffer.h" +#include "v_palette.h" FWarpTexture::FWarpTexture (FTexture *source, int warptype) @@ -74,16 +75,17 @@ void FWarpTexture::Unload () Spans = NULL; } SourcePic->Unload (); + FTexture::Unload(); } bool FWarpTexture::CheckModified () { - return r_FrameTime != GenTime; + return r_viewpoint.FrameTime != GenTime; } -const BYTE *FWarpTexture::GetPixels () +const uint8_t *FWarpTexture::GetPixels () { - DWORD time = r_FrameTime; + uint32_t time = r_viewpoint.FrameTime; if (Pixels == NULL || time != GenTime) { @@ -92,9 +94,28 @@ const BYTE *FWarpTexture::GetPixels () return Pixels; } -const BYTE *FWarpTexture::GetColumn (unsigned int column, const Span **spans_out) +const uint32_t *FWarpTexture::GetPixelsBgra() { - DWORD time = r_FrameTime; + uint32_t time = r_viewpoint.FrameTime; + if (Pixels == NULL || time != GenTime) + { + MakeTexture(time); + CreatePixelsBgraWithMipmaps(); + for (int i = 0; i < Width * Height; i++) + { + if (Pixels[i] != 0) + PixelsBgra[i] = 0xff000000 | GPalette.BaseColors[Pixels[i]].d; + else + PixelsBgra[i] = 0; + } + GenerateBgraMipmapsFast(); + } + return PixelsBgra.data(); +} + +const uint8_t *FWarpTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + uint32_t time = r_viewpoint.FrameTime; if (Pixels == NULL || time != GenTime) { @@ -123,13 +144,13 @@ const BYTE *FWarpTexture::GetColumn (unsigned int column, const Span **spans_out } -void FWarpTexture::MakeTexture(DWORD time) +void FWarpTexture::MakeTexture(uint32_t time) { - const BYTE *otherpix = SourcePic->GetPixels(); + const uint8_t *otherpix = SourcePic->GetPixels(); if (Pixels == NULL) { - Pixels = new BYTE[Width * Height]; + Pixels = new uint8_t[Width * Height]; } if (Spans != NULL) { diff --git a/src/tflags.h b/src/tflags.h index 203fa70e69..96ba53fb55 100644 --- a/src/tflags.h +++ b/src/tflags.h @@ -42,7 +42,7 @@ * T is the enum type of individual flags, * TT is the underlying integer type used (defaults to uint32_t) */ -template +template class TFlags { struct ZeroDummy {}; diff --git a/src/v_collection.cpp b/src/v_collection.cpp index b7e395e6ae..a9e1772a3a 100644 --- a/src/v_collection.cpp +++ b/src/v_collection.cpp @@ -37,6 +37,7 @@ #include "v_video.h" #include "m_swap.h" #include "w_wad.h" +#include "textures.h" FImageCollection::FImageCollection () { diff --git a/src/v_collection.h b/src/v_collection.h index 9c0e8676b0..9e2800319e 100644 --- a/src/v_collection.h +++ b/src/v_collection.h @@ -37,6 +37,8 @@ #include "doomtype.h" #include "r_defs.h" +class FTexture; + class FImageCollection { public: diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 0a9b75b19f..3f3cae3a27 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -32,7 +32,7 @@ ** */ -// #define NO_SWRENDER // set this if you want to exclude the software renderer. Without software renderer the base implementations of DrawTextureV and FillSimplePoly need to be disabled because they depend on it. +// #define NO_SWRENDER // set this if you want to exclude the software renderer. Without the software renderer software canvas drawing does nothing. #include #include @@ -42,10 +42,9 @@ #include "m_swap.h" #include "r_defs.h" #include "r_utility.h" +#include "r_renderer.h" #ifndef NO_SWRENDER -#include "r_draw.h" -#include "r_main.h" -#include "r_things.h" +#include "swrenderer/r_swcanvas.h" #endif #include "r_data/r_translate.h" #include "doomstat.h" @@ -62,6 +61,7 @@ #include "colormatcher.h" #include "r_data/colormaps.h" #include "g_levellocals.h" +#include "textures.h" CUSTOM_CVAR(Int, uiscale, 2, CVAR_ARCHIVE | CVAR_NOINITCALL) { @@ -81,16 +81,8 @@ int CleanWidth, CleanHeight; // Above minus 1 (or 1, if they are already 1) int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1; -// FillSimplePoly uses this -extern "C" short spanend[MAXHEIGHT]; - CVAR (Bool, hud_scale, true, CVAR_ARCHIVE); -// For routines that take RGB colors, cache the previous lookup in case there -// are several repetitions with the same color. -static int LastPal = -1; -static uint32 LastRGB; - DEFINE_ACTION_FUNCTION(_Screen, GetWidth) { PARAM_PROLOGUE; @@ -112,30 +104,6 @@ DEFINE_ACTION_FUNCTION(_Screen, PaletteColor) ACTION_RETURN_INT(index); } -static int PalFromRGB(uint32 rgb) -{ - if (LastPal >= 0 && LastRGB == rgb) - { - return LastPal; - } - // Quick check for black and white. - if (rgb == MAKEARGB(255,0,0,0)) - { - LastPal = GPalette.BlackIndex; - } - else if (rgb == MAKEARGB(255,255,255,255)) - { - LastPal = GPalette.WhiteIndex; - } - else - { - LastPal = ColorMatcher.Pick(RPART(rgb), GPART(rgb), BPART(rgb)); - } - LastRGB = rgb; - return LastPal; -} - - void DCanvas::DrawTexture (FTexture *img, double x, double y, int tags_first, ...) { Va_List tags; @@ -189,189 +157,13 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawHUDTexture) void DCanvas::DrawTextureParms(FTexture *img, DrawParms &parms) { #ifndef NO_SWRENDER - using namespace swrenderer; - using namespace drawerargs; - - static short bottomclipper[MAXWIDTH], topclipper[MAXWIDTH]; - const BYTE *translation = NULL; - - if (APART(parms.colorOverlay) != 0) - { - // The software renderer cannot invert the source without inverting the overlay - // too. That means if the source is inverted, we need to do the reverse of what - // the invert overlay flag says to do. - INTBOOL invertoverlay = (parms.style.Flags & STYLEF_InvertOverlay); - - if (parms.style.Flags & STYLEF_InvertSource) - { - invertoverlay = !invertoverlay; - } - if (invertoverlay) - { - parms.colorOverlay = PalEntry(parms.colorOverlay).InverseColor(); - } - // Note that this overrides the translation in software, but not in hardware. - FDynamicColormap *colormap = GetSpecialLights(MAKERGB(255,255,255), - parms.colorOverlay & MAKEARGB(0,255,255,255), 0); - translation = &colormap->Maps[(APART(parms.colorOverlay)*NUMCOLORMAPS/255)*256]; - } - else if (parms.remap != NULL) - { - translation = parms.remap->Remap; - } - - if (translation != NULL) - { - dc_colormap = (lighttable_t *)translation; - } - else - { - dc_colormap = identitymap; - } - - fixedcolormap = dc_colormap; - ESPSResult mode = R_SetPatchStyle (parms.style, parms.Alpha, 0, parms.fillcolor); - - BYTE *destorgsave = dc_destorg; - dc_destorg = screen->GetBuffer(); - if (dc_destorg == NULL) - { - I_FatalError("Attempt to write to buffer of hardware canvas"); - } - - double x0 = parms.x - parms.left * parms.destwidth / parms.texwidth; - double y0 = parms.y - parms.top * parms.destheight / parms.texheight; - - if (mode != DontDraw) - { - int stop4; - - double centeryback = CenterY; - CenterY = 0; - - // There is not enough precision in the drawing routines to keep the full - // precision for y0. :( - modf(y0, &sprtopscreen); - - double yscale = parms.destheight / img->GetHeight(); - double iyscale = 1 / yscale; - - spryscale = yscale; - assert(spryscale > 0); - - sprflipvert = false; - //dc_iscale = FLOAT2FIXED(iyscale); - //dc_texturemid = (-y0) * iyscale; - //dc_iscale = 0xffffffffu / (unsigned)spryscale; - dc_iscale = FLOAT2FIXED(1 / spryscale); - dc_texturemid = (CenterY - 1 - sprtopscreen) * dc_iscale / 65536; - fixed_t frac = 0; - double xiscale = img->GetWidth() / parms.destwidth; - double x2 = x0 + parms.destwidth; - - if (bottomclipper[0] != parms.dclip) - { - fillshort(bottomclipper, screen->GetWidth(), (short)parms.dclip); - } - if (parms.uclip != 0) - { - if (topclipper[0] != parms.uclip) - { - fillshort(topclipper, screen->GetWidth(), (short)parms.uclip); - } - mceilingclip = topclipper; - } - else - { - mceilingclip = zeroarray; - } - mfloorclip = bottomclipper; - - if (parms.flipX) - { - frac = (img->GetWidth() << FRACBITS) - 1; - xiscale = -xiscale; - } - - if (parms.windowleft > 0 || parms.windowright < parms.texwidth) - { - double wi = MIN(parms.windowright, parms.texwidth); - double xscale = parms.destwidth / parms.texwidth; - x0 += parms.windowleft * xscale; - frac += FLOAT2FIXED(parms.windowleft); - x2 -= (parms.texwidth - wi) * xscale; - } - if (x0 < parms.lclip) - { - frac += FLOAT2FIXED((parms.lclip - x0) * xiscale); - x0 = parms.lclip; - } - if (x2 > parms.rclip) - { - x2 = parms.rclip; - } - - // Drawing short output ought to fit in the data cache well enough - // if we draw one column at a time, so do that, since it's simpler. - if (parms.destheight < 32 || (parms.dclip - parms.uclip) < 32) - { - mode = DoDraw0; - } - - dc_x = int(x0); - int x2_i = int(x2); - fixed_t xiscale_i = FLOAT2FIXED(xiscale); - - if (mode == DoDraw0) - { - // One column at a time - stop4 = dc_x; - } - else // DoDraw1` - { - // Up to four columns at a time - stop4 = x2_i & ~3; - } - - if (dc_x < x2_i) - { - while ((dc_x < stop4) && (dc_x & 3)) - { - R_DrawMaskedColumn(img, frac, false, !parms.masked); - dc_x++; - frac += xiscale_i; - } - - while (dc_x < stop4) - { - rt_initcols(); - for (int zz = 4; zz; --zz) - { - R_DrawMaskedColumn(img, frac, true, !parms.masked); - dc_x++; - frac += xiscale_i; - } - rt_draw4cols(dc_x - 4); - } - - while (dc_x < x2_i) - { - R_DrawMaskedColumn(img, frac, false, !parms.masked); - dc_x++; - frac += xiscale_i; - } - } - CenterY = centeryback; - } - R_FinishSetPatchStyle (); - - dc_destorg = destorgsave; + SWCanvas::DrawTexture(this, img, parms); +#endif if (ticdup != 0 && menuactive == MENU_Off) { NetUpdate(); } -#endif } bool DCanvas::SetTextureParms(DrawParms *parms, FTexture *img, double xx, double yy) const @@ -532,7 +324,7 @@ static inline FColormapStyle * ListGetColormapStyle(VMVa_List &tags) } template -bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, DWORD tag, T& tags, DrawParms *parms, bool fortext) const +bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, uint32_t tag, T& tags, DrawParms *parms, bool fortext) const { INTBOOL boolval; int intval; @@ -958,8 +750,8 @@ bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, DWORD tag, } // explicitly instantiate both versions for v_text.cpp. -template bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, DWORD tag, Va_List& tags, DrawParms *parms, bool fortext) const; -template bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, DWORD tag, VMVa_List& tags, DrawParms *parms, bool fortext) const; +template bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, uint32_t tag, Va_List& tags, DrawParms *parms, bool fortext) const; +template bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, uint32_t tag, VMVa_List& tags, DrawParms *parms, bool fortext) const; void DCanvas::VirtualToRealCoords(double &x, double &y, double &w, double &h, double vwidth, double vheight, bool vbottom, bool handleaspect) const @@ -1082,225 +874,18 @@ void DCanvas::FillBorder (FTexture *img) } } -void DCanvas::PUTTRANSDOT (int xx, int yy, int basecolor, int level) +void DCanvas::DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32_t realcolor) { - static int oldyy; - static int oldyyshifted; - -#if 0 - if(xx < 32) - cc += 7-(xx>>2); - else if(xx > (finit_width - 32)) - cc += 7-((finit_width-xx) >> 2); -// if(cc==oldcc) //make sure that we don't double fade the corners. -// { - if(yy < 32) - cc += 7-(yy>>2); - else if(yy > (finit_height - 32)) - cc += 7-((finit_height-yy) >> 2); -// } - if(cc > cm && cm != NULL) - { - cc = cm; - } - else if(cc > oldcc+6) // don't let the color escape from the fade table... - { - cc=oldcc+6; - } +#ifndef NO_SWRENDER + SWCanvas::DrawLine(this, x0, y0, x1, y1, palColor, realcolor); #endif - if (yy == oldyy+1) - { - oldyy++; - oldyyshifted += GetPitch(); - } - else if (yy == oldyy-1) - { - oldyy--; - oldyyshifted -= GetPitch(); - } - else if (yy != oldyy) - { - oldyy = yy; - oldyyshifted = yy * GetPitch(); - } - - BYTE *spot = GetBuffer() + oldyyshifted + xx; - DWORD *bg2rgb = Col2RGB8[1+level]; - DWORD *fg2rgb = Col2RGB8[63-level]; - DWORD fg = fg2rgb[basecolor]; - DWORD bg = bg2rgb[*spot]; - bg = (fg+bg) | 0x1f07c1f; - *spot = RGB32k.All[bg&(bg>>15)]; } -void DCanvas::DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 realcolor) -//void DrawTransWuLine (int x0, int y0, int x1, int y1, BYTE palColor) +void DCanvas::DrawPixel(int x, int y, int palColor, uint32_t realcolor) { - const int WeightingScale = 0; - const int WEIGHTBITS = 6; - const int WEIGHTSHIFT = 16-WEIGHTBITS; - const int NUMWEIGHTS = (1< y1) - { - int temp = y0; y0 = y1; y1 = temp; - temp = x0; x0 = x1; x1 = temp; - } - - PUTTRANSDOT (x0, y0, palColor, 0); - - if ((deltaX = x1 - x0) >= 0) - { - xDir = 1; - } - else - { - xDir = -1; - deltaX = -deltaX; - } - - if ((deltaY = y1 - y0) == 0) - { // horizontal line - if (x0 > x1) - { - swapvalues (x0, x1); - } - memset (GetBuffer() + y0*GetPitch() + x0, palColor, deltaX+1); - } - else if (deltaX == 0) - { // vertical line - BYTE *spot = GetBuffer() + y0*GetPitch() + x0; - int pitch = GetPitch (); - do - { - *spot = palColor; - spot += pitch; - } while (--deltaY != 0); - } - else if (deltaX == deltaY) - { // diagonal line. - BYTE *spot = GetBuffer() + y0*GetPitch() + x0; - int advance = GetPitch() + xDir; - do - { - *spot = palColor; - spot += advance; - } while (--deltaY != 0); - } - else - { - // line is not horizontal, diagonal, or vertical - fixed_t errorAcc = 0; - - if (deltaY > deltaX) - { // y-major line - fixed_t errorAdj = (((unsigned)deltaX << 16) / (unsigned)deltaY) & 0xffff; - if (xDir < 0) - { - if (WeightingScale == 0) - { - while (--deltaY) - { - errorAcc += errorAdj; - y0++; - int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; - PUTTRANSDOT (x0 - (errorAcc >> 16), y0, palColor, weighting); - PUTTRANSDOT (x0 - (errorAcc >> 16) - 1, y0, - palColor, WEIGHTMASK - weighting); - } - } - else - { - while (--deltaY) - { - errorAcc += errorAdj; - y0++; - int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT+8)) & WEIGHTMASK; - PUTTRANSDOT (x0 - (errorAcc >> 16), y0, palColor, weighting); - PUTTRANSDOT (x0 - (errorAcc >> 16) - 1, y0, - palColor, WEIGHTMASK - weighting); - } - } - } - else - { - if (WeightingScale == 0) - { - while (--deltaY) - { - errorAcc += errorAdj; - y0++; - int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; - PUTTRANSDOT (x0 + (errorAcc >> 16), y0, palColor, weighting); - PUTTRANSDOT (x0 + (errorAcc >> 16) + xDir, y0, - palColor, WEIGHTMASK - weighting); - } - } - else - { - while (--deltaY) - { - errorAcc += errorAdj; - y0++; - int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT+8)) & WEIGHTMASK; - PUTTRANSDOT (x0 + (errorAcc >> 16), y0, palColor, weighting); - PUTTRANSDOT (x0 + (errorAcc >> 16) + xDir, y0, - palColor, WEIGHTMASK - weighting); - } - } - } - } - else - { // x-major line - fixed_t errorAdj = (((DWORD) deltaY << 16) / (DWORD) deltaX) & 0xffff; - - if (WeightingScale == 0) - { - while (--deltaX) - { - errorAcc += errorAdj; - x0 += xDir; - int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; - PUTTRANSDOT (x0, y0 + (errorAcc >> 16), palColor, weighting); - PUTTRANSDOT (x0, y0 + (errorAcc >> 16) + 1, - palColor, WEIGHTMASK - weighting); - } - } - else - { - while (--deltaX) - { - errorAcc += errorAdj; - x0 += xDir; - int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT+8)) & WEIGHTMASK; - PUTTRANSDOT (x0, y0 + (errorAcc >> 16), palColor, weighting); - PUTTRANSDOT (x0, y0 + (errorAcc >> 16) + 1, - palColor, WEIGHTMASK - weighting); - } - } - } - PUTTRANSDOT (x1, y1, palColor, 0); - } - Unlock(); -} - -void DCanvas::DrawPixel(int x, int y, int palColor, uint32 realcolor) -{ - if (palColor < 0) - { - palColor = PalFromRGB(realcolor); - } - - Buffer[Pitch * y + x] = (BYTE)palColor; +#ifndef NO_SWRENDER + SWCanvas::DrawPixel(this, x, y, palColor, realcolor); +#endif } //========================================================================== @@ -1311,46 +896,18 @@ void DCanvas::DrawPixel(int x, int y, int palColor, uint32 realcolor) // //========================================================================== -void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color) +void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uint32_t color) { - int x, y; - BYTE *dest; - - if (left == right || top == bottom) +#ifndef NO_SWRENDER + if (palcolor < 0 && APART(color) != 255) { - return; + Dim(color, APART(color) / 255.f, left, top, right - left, bottom - top); } - - assert(left < right); - assert(top < bottom); - - if (left >= Width || right <= 0 || top >= Height || bottom <= 0) + else { - return; - } - left = MAX(0,left); - right = MIN(Width,right); - top = MAX(0,top); - bottom = MIN(Height,bottom); - - if (palcolor < 0) - { - if (APART(color) != 255) - { - Dim(color, APART(color)/255.f, left, top, right - left, bottom - top); - return; - } - - palcolor = PalFromRGB(color); - } - - dest = Buffer + top * Pitch + left; - x = right - left; - for (y = top; y < bottom; y++) - { - memset(dest, palcolor, x); - dest += Pitch; + SWCanvas::Clear(this, left, top, right, bottom, palcolor, color); } +#endif } DEFINE_ACTION_FUNCTION(_Screen, Clear) @@ -1366,6 +923,21 @@ DEFINE_ACTION_FUNCTION(_Screen, Clear) return 0; } +//========================================================================== +// +// DCanvas :: Dim +// +// Applies a colored overlay to an area of the screen. +// +//========================================================================== + +void DCanvas::Dim(PalEntry color, float damount, int x1, int y1, int w, int h) +{ +#ifndef NO_SWRENDER + SWCanvas::Dim(this, color, damount, x1, y1, w, h); +#endif +} + //========================================================================== // // DCanvas :: FillSimplePoly @@ -1386,185 +958,7 @@ void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) { #ifndef NO_SWRENDER - using namespace swrenderer; - using namespace drawerargs; - - // Use an equation similar to player sprites to determine shade - fixed_t shade = LIGHT2SHADE(lightlevel) - 12*FRACUNIT; - float topy, boty, leftx, rightx; - int toppt, botpt, pt1, pt2; - int i; - int y1, y2, y; - fixed_t x; - bool dorotate = rotation != 0.; - double cosrot, sinrot; - - if (--npoints < 2 || Buffer == NULL) - { // not a polygon or we're not locked - return; - } - - if (bottomclip <= 0) - { - bottomclip = Height; - } - - // Find the extents of the polygon, in particular the highest and lowest points. - for (botpt = toppt = 0, boty = topy = points[0].Y, leftx = rightx = points[0].X, i = 1; i <= npoints; ++i) - { - if (points[i].Y < topy) - { - topy = points[i].Y; - toppt = i; - } - if (points[i].Y > boty) - { - boty = points[i].Y; - botpt = i; - } - if (points[i].X < leftx) - { - leftx = points[i].X; - } - if (points[i].X > rightx) - { - rightx = points[i].X; - } - } - if (topy >= bottomclip || // off the bottom of the screen - boty <= 0 || // off the top of the screen - leftx >= Width || // off the right of the screen - rightx <= 0) // off the left of the screen - { - return; - } - - BYTE *destorgsave = dc_destorg; - dc_destorg = screen->GetBuffer(); - if (dc_destorg == NULL) - { - I_FatalError("Attempt to write to buffer of hardware canvas"); - } - - scalex /= tex->Scale.X; - scaley /= tex->Scale.Y; - - // Use the CRT's functions here. - cosrot = cos(rotation.Radians()); - sinrot = sin(rotation.Radians()); - - // Setup constant texture mapping parameters. - R_SetupSpanBits(tex); - R_SetSpanColormap(colormap != NULL ? &colormap->Maps[clamp(shade >> FRACBITS, 0, NUMCOLORMAPS-1) * 256] : identitymap); - R_SetSpanSource(tex); - if (ds_xbits != 0) - { - scalex = double(1u << (32 - ds_xbits)) / scalex; - ds_xstep = xs_RoundToInt(cosrot * scalex); - } - else - { // Texture is one pixel wide. - scalex = 0; - ds_xstep = 0; - } - if (ds_ybits != 0) - { - scaley = double(1u << (32 - ds_ybits)) / scaley; - ds_ystep = xs_RoundToInt(sinrot * scaley); - } - else - { // Texture is one pixel tall. - scaley = 0; - ds_ystep = 0; - } - - // Travel down the right edge and create an outline of that edge. - pt1 = toppt; - pt2 = toppt + 1; if (pt2 > npoints) pt2 = 0; - y1 = xs_RoundToInt(points[pt1].Y + 0.5f); - do - { - x = FLOAT2FIXED(points[pt1].X + 0.5f); - y2 = xs_RoundToInt(points[pt2].Y + 0.5f); - if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) - { - } - else - { - fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); - int y3 = MIN(y2, bottomclip); - if (y1 < 0) - { - x += xinc * -y1; - y1 = 0; - } - for (y = y1; y < y3; ++y) - { - spanend[y] = clamp(x >> FRACBITS, -1, Width); - x += xinc; - } - } - y1 = y2; - pt1 = pt2; - pt2++; if (pt2 > npoints) pt2 = 0; - } while (pt1 != botpt); - - // Travel down the left edge and fill it in. - pt1 = toppt; - pt2 = toppt - 1; if (pt2 < 0) pt2 = npoints; - y1 = xs_RoundToInt(points[pt1].Y + 0.5f); - do - { - x = FLOAT2FIXED(points[pt1].X + 0.5f); - y2 = xs_RoundToInt(points[pt2].Y + 0.5f); - if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) - { - } - else - { - fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); - int y3 = MIN(y2, bottomclip); - if (y1 < 0) - { - x += xinc * -y1; - y1 = 0; - } - for (y = y1; y < y3; ++y) - { - int x1 = x >> FRACBITS; - int x2 = spanend[y]; - if (x2 > x1 && x2 > 0 && x1 < Width) - { - x1 = MAX(x1, 0); - x2 = MIN(x2, Width); -#if 0 - memset(this->Buffer + y * this->Pitch + x1, (int)tex, x2 - x1); -#else - ds_y = y; - ds_x1 = x1; - ds_x2 = x2 - 1; - - DVector2 tex(x1 - originx, y - originy); - if (dorotate) - { - double t = tex.X; - tex.X = t * cosrot - tex.Y * sinrot; - tex.Y = tex.Y * cosrot + t * sinrot; - } - ds_xfrac = xs_RoundToInt(tex.X * scalex); - ds_yfrac = xs_RoundToInt(tex.Y * scaley); - - R_DrawSpan(); -#endif - } - x += xinc; - } - } - y1 = y2; - pt1 = pt2; - pt2--; if (pt2 < 0) pt2 = npoints; - } while (pt1 != botpt); - dc_destorg = destorgsave; + SWCanvas::FillSimplePoly(this, tex, points, npoints, originx, originy, scalex, scaley, rotation, colormap, flatcolor, lightlevel, bottomclip); #endif } @@ -1580,11 +974,14 @@ void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, // V_DrawBlock // Draw a linear block of pixels into the view buffer. // -void DCanvas::DrawBlock (int x, int y, int _width, int _height, const BYTE *src) const +void DCanvas::DrawBlock (int x, int y, int _width, int _height, const uint8_t *src) const { + if (IsBgra()) + return; + int srcpitch = _width; int destpitch; - BYTE *dest; + uint8_t *dest; if (ClipBox (x, y, _width, _height, src, srcpitch)) { @@ -1606,9 +1003,12 @@ void DCanvas::DrawBlock (int x, int y, int _width, int _height, const BYTE *src) // V_GetBlock // Gets a linear block of pixels from the view buffer. // -void DCanvas::GetBlock (int x, int y, int _width, int _height, BYTE *dest) const +void DCanvas::GetBlock (int x, int y, int _width, int _height, uint8_t *dest) const { - const BYTE *src; + if (IsBgra()) + return; + + const uint8_t *src; #ifdef RANGECHECK if (x<0 @@ -1631,7 +1031,7 @@ void DCanvas::GetBlock (int x, int y, int _width, int _height, BYTE *dest) const } // Returns true if the box was completely clipped. False otherwise. -bool DCanvas::ClipBox (int &x, int &y, int &w, int &h, const BYTE *&src, const int srcpitch) const +bool DCanvas::ClipBox (int &x, int &y, int &w, int &h, const uint8_t *&src, const int srcpitch) const { if (x >= Width || y >= Height || x+w <= 0 || y+h <= 0) { // Completely clipped off screen diff --git a/src/v_font.cpp b/src/v_font.cpp index 51ac6052f5..7f1f496d58 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -318,7 +318,7 @@ FFont *V_GetFont(const char *name) if (lump != -1) { - uint32 head; + uint32_t head; { FWadLump lumpy = Wads.OpenLumpNum (lump); lumpy.Read (&head, 4); @@ -1716,6 +1716,7 @@ void FFontChar1::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== @@ -1777,6 +1778,7 @@ void FFontChar2::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/v_palette.cpp b/src/v_palette.cpp index 5873de10ba..1c13446a85 100644 --- a/src/v_palette.cpp +++ b/src/v_palette.cpp @@ -32,6 +32,8 @@ ** */ +#include "g_level.h" + #include #include #include @@ -50,7 +52,6 @@ #include "w_wad.h" #include "i_video.h" #include "c_dispatch.h" -#include "g_level.h" #include "st_stuff.h" #include "gi.h" #include "x86.h" @@ -107,7 +108,7 @@ CCMD (bumpgamma) /* Palette management stuff */ /****************************/ -int BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num) +int BestColor (const uint32_t *pal_in, int r, int g, int b, int first, int num) { const PalEntry *pal = (const PalEntry *)pal_in; int bestcolor = first; diff --git a/src/v_palette.h b/src/v_palette.h index 62628eb5cb..28ced55f3f 100644 --- a/src/v_palette.h +++ b/src/v_palette.h @@ -70,7 +70,7 @@ extern FPalette GPalette; // The color overlay to use for depleted items #define DIM_OVERLAY MAKEARGB(170,0,0,0) -int BestColor (const uint32 *pal, int r, int g, int b, int first=1, int num=255); +int BestColor (const uint32_t *pal, int r, int g, int b, int first=1, int num=255); void DoBlending (const PalEntry *from, PalEntry *to, int count, int r, int g, int b, int a); void InitPalette (); diff --git a/src/v_pfx.cpp b/src/v_pfx.cpp index 260e87944c..292ede574b 100644 --- a/src/v_pfx.cpp +++ b/src/v_pfx.cpp @@ -37,11 +37,8 @@ #include "v_palette.h" #include "v_pfx.h" -extern "C" -{ - PfxUnion GPfxPal; - PfxState GPfx; -} +PfxUnion GPfxPal; +PfxState GPfx; static bool AnalyzeMask (uint32_t mask, uint8_t *shift); @@ -68,7 +65,7 @@ static void Convert32 (uint8_t *src, int srcpitch, void *destin, int destpitch, int destwidth, int destheight, fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac); -void PfxState::SetFormat (int bits, uint32 redMask, uint32 greenMask, uint32 blueMask) +void PfxState::SetFormat (int bits, uint32_t redMask, uint32_t greenMask, uint32_t blueMask) { switch (bits) { diff --git a/src/v_pfx.h b/src/v_pfx.h index 230b30c8da..5ae1af0659 100644 --- a/src/v_pfx.h +++ b/src/v_pfx.h @@ -77,10 +77,7 @@ struct PfxState fixed_t xstep, fixed_t ystep, fixed_t xfrac, fixed_t yfrac); }; -extern "C" -{ - extern PfxUnion GPfxPal; - extern PfxState GPfx; -} +extern PfxUnion GPfxPal; +extern PfxState GPfx; #endif //__V_PFX_H__ diff --git a/src/v_video.cpp b/src/v_video.cpp index d993204a27..4fcce944cf 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -66,12 +66,15 @@ #include "menu/menu.h" #include "r_data/voxels.h" +EXTERN_CVAR(Bool, r_blendmethod) + int active_con_scale(); FRenderer *Renderer; IMPLEMENT_CLASS(DCanvas, true, false) IMPLEMENT_CLASS(DFrameBuffer, true, false) +EXTERN_CVAR (Bool, swtruecolor) EXTERN_CVAR (Bool, fullscreen) #if defined(_DEBUG) && defined(_M_IX86) && !defined(__MINGW32__) @@ -85,7 +88,7 @@ class DDummyFrameBuffer : public DFrameBuffer DECLARE_CLASS (DDummyFrameBuffer, DFrameBuffer); public: DDummyFrameBuffer (int width, int height) - : DFrameBuffer (0, 0) + : DFrameBuffer (0, 0, false) { Width = width; Height = height; @@ -119,14 +122,13 @@ class FPaletteTester : public FTexture public: FPaletteTester (); - const BYTE *GetColumn(unsigned int column, const Span **spans_out); - const BYTE *GetPixels(); - void Unload(); + const uint8_t *GetColumn(unsigned int column, const Span **spans_out); + const uint8_t *GetPixels(); bool CheckModified(); void SetTranslation(int num); protected: - BYTE Pixels[16*16]; + uint8_t Pixels[16*16]; int CurTranslation; int WantTranslation; static const Span DummySpan[2]; @@ -140,16 +142,14 @@ int DisplayWidth, DisplayHeight, DisplayBits; FFont *SmallFont, *SmallFont2, *BigFont, *ConFont, *IntermissionFont; -extern "C" { -DWORD Col2RGB8[65][256]; -DWORD *Col2RGB8_LessPrecision[65]; -DWORD Col2RGB8_Inverse[65][256]; +uint32_t Col2RGB8[65][256]; +uint32_t *Col2RGB8_LessPrecision[65]; +uint32_t Col2RGB8_Inverse[65][256]; ColorTable32k RGB32k; ColorTable256k RGB256k; -} -static DWORD Col2RGB8_2[63][256]; +static uint32_t Col2RGB8_2[63][256]; // [RH] The framebuffer is no longer a mere byte array. // There's also only one, not four. @@ -212,13 +212,14 @@ DCanvas *DCanvas::CanvasChain = NULL; // //========================================================================== -DCanvas::DCanvas (int _width, int _height) +DCanvas::DCanvas (int _width, int _height, bool _bgra) { // Init member vars Buffer = NULL; LockCount = 0; Width = _width; Height = _height; + Bgra = _bgra; // Add to list of active canvases Next = CanvasChain; @@ -329,76 +330,12 @@ void DCanvas::Dim (PalEntry color) { float dim[4] = { color.r/255.f, color.g/255.f, color.b/255.f, color.a/255.f }; V_AddBlend (dimmer.r/255.f, dimmer.g/255.f, dimmer.b/255.f, amount, dim); - dimmer = PalEntry (BYTE(dim[0]*255), BYTE(dim[1]*255), BYTE(dim[2]*255)); + dimmer = PalEntry (uint8_t(dim[0]*255), uint8_t(dim[1]*255), uint8_t(dim[2]*255)); amount = dim[3]; } Dim (dimmer, amount, 0, 0, Width, Height); } -//========================================================================== -// -// DCanvas :: Dim -// -// Applies a colored overlay to an area of the screen. -// -//========================================================================== - -void DCanvas::Dim (PalEntry color, float damount, int x1, int y1, int w, int h) -{ - if (damount == 0.f) - return; - - DWORD *bg2rgb; - DWORD fg; - int gap; - BYTE *spot; - int x, y; - - if (x1 >= Width || y1 >= Height) - { - return; - } - if (x1 + w > Width) - { - w = Width - x1; - } - if (y1 + h > Height) - { - h = Height - y1; - } - if (w <= 0 || h <= 0) - { - return; - } - - { - int amount; - - amount = (int)(damount * 64); - bg2rgb = Col2RGB8[64-amount]; - - fg = (((color.r * amount) >> 4) << 20) | - ((color.g * amount) >> 4) | - (((color.b * amount) >> 4) << 10); - } - - spot = Buffer + x1 + y1*Pitch; - gap = Pitch - w; - for (y = h; y != 0; y--) - { - for (x = w; x != 0; x--) - { - DWORD bg; - - bg = bg2rgb[(*spot)&0xff]; - bg = (fg+bg) | 0x1f07c1f; - *spot = RGB32k.All[bg&(bg>>15)]; - spot++; - } - spot += gap; - } -} - DEFINE_ACTION_FUNCTION(_Screen, Dim) { PARAM_PROLOGUE; @@ -422,12 +359,12 @@ DEFINE_ACTION_FUNCTION(_Screen, Dim) // //========================================================================== -void DCanvas::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type) +void DCanvas::GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) { Lock(true); buffer = GetBuffer(); - pitch = GetPitch(); - color_type = SS_PAL; + pitch = IsBgra() ? GetPitch() * 4 : GetPitch(); + color_type = IsBgra() ? SS_BGRA : SS_PAL; } //========================================================================== @@ -455,7 +392,7 @@ void DCanvas::ReleaseScreenshotBuffer() // //========================================================================== -int V_GetColorFromString (const DWORD *palette, const char *cstr, FScriptPosition *sc) +int V_GetColorFromString (const uint32_t *palette, const char *cstr, FScriptPosition *sc) { int c[3], i, p; char val[3]; @@ -647,7 +584,7 @@ FString V_GetColorStringByName (const char *name, FScriptPosition *sc) // //========================================================================== -int V_GetColor (const DWORD *palette, const char *str, FScriptPosition *sc) +int V_GetColor (const uint32_t *palette, const char *str, FScriptPosition *sc) { FString string = V_GetColorStringByName (str, sc); int res; @@ -663,7 +600,7 @@ int V_GetColor (const DWORD *palette, const char *str, FScriptPosition *sc) return res; } -int V_GetColor(const DWORD *palette, FScanner &sc) +int V_GetColor(const uint32_t *palette, FScanner &sc) { FScriptPosition scc = sc; return V_GetColor(palette, sc.String, &scc); @@ -730,18 +667,17 @@ static void BuildTransTable (const PalEntry *palette) // //========================================================================== -void DCanvas::CalcGamma (float gamma, BYTE gammalookup[256]) +void DCanvas::CalcGamma (float gamma, uint8_t gammalookup[256]) { // I found this formula on the web at // , // but that page no longer exits. - double invgamma = 1.f / gamma; int i; for (i = 0; i < 256; i++) { - gammalookup[i] = (BYTE)(255.0 * pow (i / 255.0, invgamma)); + gammalookup[i] = (uint8_t)(255.0 * pow (i / 255.0, invgamma) + 0.5); } } @@ -753,8 +689,8 @@ void DCanvas::CalcGamma (float gamma, BYTE gammalookup[256]) // //========================================================================== -DSimpleCanvas::DSimpleCanvas (int width, int height) - : DCanvas (width, height) +DSimpleCanvas::DSimpleCanvas (int width, int height, bool bgra) + : DCanvas (width, height, bgra) { MemBuffer = nullptr; Resize(width, height); @@ -806,8 +742,9 @@ void DSimpleCanvas::Resize(int width, int height) Pitch = width + MAX(0, CPU.DataL1LineSize - 8); } } - MemBuffer = new BYTE[Pitch * height]; - memset(MemBuffer, 0, Pitch * height); + int bytes_per_pixel = Bgra ? 4 : 1; + MemBuffer = new uint8_t[Pitch * height * bytes_per_pixel]; + memset (MemBuffer, 0, Pitch * height * bytes_per_pixel); } //========================================================================== @@ -876,8 +813,8 @@ void DSimpleCanvas::Unlock () // //========================================================================== -DFrameBuffer::DFrameBuffer (int width, int height) - : DSimpleCanvas (width, height) +DFrameBuffer::DFrameBuffer (int width, int height, bool bgra) + : DSimpleCanvas (width, height, bgra) { LastMS = LastSec = FrameCount = LastCount = LastTic = 0; Accel2D = false; @@ -886,6 +823,70 @@ DFrameBuffer::DFrameBuffer (int width, int height) VideoHeight = height; } +//========================================================================== +// +// DFrameBuffer :: PostprocessBgra +// +// Copies data to destination buffer while performing gamma and flash. +// This is only needed if a target cannot do this with shaders. +// +//========================================================================== + +void DFrameBuffer::CopyWithGammaBgra(void *output, int pitch, const uint8_t *gammared, const uint8_t *gammagreen, const uint8_t *gammablue, PalEntry flash, int flash_amount) +{ + const uint8_t *gammatables[3] = { gammared, gammagreen, gammablue }; + + if (flash_amount > 0) + { + uint16_t inv_flash_amount = 256 - flash_amount; + uint16_t flash_red = flash.r * flash_amount; + uint16_t flash_green = flash.g * flash_amount; + uint16_t flash_blue = flash.b * flash_amount; + + for (int y = 0; y < Height; y++) + { + uint8_t *dest = (uint8_t*)output + y * pitch; + uint8_t *src = MemBuffer + y * Pitch * 4; + for (int x = 0; x < Width; x++) + { + uint16_t fg_red = src[2]; + uint16_t fg_green = src[1]; + uint16_t fg_blue = src[0]; + uint16_t red = (fg_red * inv_flash_amount + flash_red) >> 8; + uint16_t green = (fg_green * inv_flash_amount + flash_green) >> 8; + uint16_t blue = (fg_blue * inv_flash_amount + flash_blue) >> 8; + + dest[0] = gammatables[2][blue]; + dest[1] = gammatables[1][green]; + dest[2] = gammatables[0][red]; + dest[3] = 0xff; + + dest += 4; + src += 4; + } + } + } + else + { + for (int y = 0; y < Height; y++) + { + uint8_t *dest = (uint8_t*)output + y * pitch; + uint8_t *src = MemBuffer + y * Pitch * 4; + for (int x = 0; x < Width; x++) + { + dest[0] = gammatables[2][src[0]]; + dest[1] = gammatables[1][src[1]]; + dest[2] = gammatables[0][src[2]]; + dest[3] = 0xff; + + dest += 4; + src += 4; + } + } + } +} + + //========================================================================== // // DFrameBuffer :: DrawRateStuff @@ -899,8 +900,8 @@ void DFrameBuffer::DrawRateStuff () // Draws frame time and cumulative fps if (vid_fps) { - DWORD ms = I_FPSTime(); - DWORD howlong = ms - LastMS; + uint32_t ms = I_FPSTime(); + uint32_t howlong = ms - LastMS; if ((signed)howlong >= 0) { char fpsbuff[40]; @@ -920,7 +921,7 @@ void DFrameBuffer::DrawRateStuff () DTA_VirtualHeight, screen->GetHeight() / textScale, DTA_KeepRatio, true, TAG_DONE); - DWORD thisSec = ms/1000; + uint32_t thisSec = ms/1000; if (LastSec < thisSec) { LastCount = FrameCount / (thisSec - LastSec); @@ -937,7 +938,7 @@ void DFrameBuffer::DrawRateStuff () { int i = I_GetTime(false); int tics = i - LastTic; - BYTE *buffer = GetBuffer(); + uint8_t *buffer = GetBuffer(); LastTic = i; if (tics > 20) tics = 20; @@ -945,10 +946,21 @@ void DFrameBuffer::DrawRateStuff () // Buffer can be NULL if we're doing hardware accelerated 2D if (buffer != NULL) { - buffer += (GetHeight()-1) * GetPitch(); - - for (i = 0; i < tics*2; i += 2) buffer[i] = 0xff; - for ( ; i < 20*2; i += 2) buffer[i] = 0x00; + if (IsBgra()) + { + uint32_t *buffer32 = (uint32_t*)buffer; + buffer32 += (GetHeight() - 1) * GetPitch(); + + for (i = 0; i < tics * 2; i += 2) buffer32[i] = 0xffffffff; + for (; i < 20 * 2; i += 2) buffer32[i] = 0xff000000; + } + else + { + buffer += (GetHeight() - 1) * GetPitch(); + + for (i = 0; i < tics * 2; i += 2) buffer[i] = 0xff; + for (; i < 20 * 2; i += 2) buffer[i] = 0x00; + } } else { @@ -1021,23 +1033,13 @@ void FPaletteTester::SetTranslation(int num) } } -//========================================================================== -// -// FPaletteTester :: Unload -// -//========================================================================== - -void FPaletteTester::Unload() -{ -} - //========================================================================== // // FPaletteTester :: GetColumn // //========================================================================== -const BYTE *FPaletteTester::GetColumn (unsigned int column, const Span **spans_out) +const uint8_t *FPaletteTester::GetColumn (unsigned int column, const Span **spans_out) { if (CurTranslation != WantTranslation) { @@ -1057,7 +1059,7 @@ const BYTE *FPaletteTester::GetColumn (unsigned int column, const Span **spans_o // //========================================================================== -const BYTE *FPaletteTester::GetPixels () +const uint8_t *FPaletteTester::GetPixels () { if (CurTranslation != WantTranslation) { @@ -1075,7 +1077,7 @@ const BYTE *FPaletteTester::GetPixels () void FPaletteTester::MakeTexture() { int i, j, k, t; - BYTE *p; + uint8_t *p; t = WantTranslation; p = Pixels; @@ -1101,7 +1103,7 @@ void FPaletteTester::MakeTexture() // //========================================================================== -void DFrameBuffer::CopyFromBuff (BYTE *src, int srcPitch, int width, int height, BYTE *dest) +void DFrameBuffer::CopyFromBuff (uint8_t *src, int srcPitch, int width, int height, uint8_t *dest) { if (Pitch == width && Pitch == Width && srcPitch == width) { diff --git a/src/v_video.h b/src/v_video.h index 8618f95ba7..891174d8eb 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -59,15 +59,15 @@ class FTexture; // Think of TagItems as an array of the following structure: // // struct TagItem { -// DWORD ti_Tag; -// DWORD ti_Data; +// uint32_t ti_Tag; +// uint32_t ti_Data; // }; #define TAG_DONE (0) /* Used to indicate the end of the Tag list */ #define TAG_END (0) /* Ditto */ /* list pointed to in ti_Data */ -#define TAG_USER ((DWORD)(1u<<30)) +#define TAG_USER ((uint32_t)(1u<<30)) enum { @@ -104,7 +104,7 @@ enum DTA_HUDRulesC, // only used internally for marking HUD_HorizCenter DTA_KeepRatio, // doesn't adjust screen size for DTA_Virtual* if the aspect ratio is not 4:3 DTA_RenderStyle, // same as render style for actors - DTA_ColorOverlay, // DWORD: ARGB to overlay on top of image; limited to black for software + DTA_ColorOverlay, // uint32_t: ARGB to overlay on top of image; limited to black for software DTA_BilinearFilter, // bool: apply bilinear filtering to the image DTA_SpecialColormap,// pointer to FSpecialColormapParameters (likely to be forever hardware-only) DTA_ColormapStyle, // pointer to FColormapStyle (hardware-only) @@ -136,7 +136,7 @@ enum class FFont; struct FRemapTable; class player_t; -typedef uint32 angle_t; +typedef uint32_t angle_t; struct DrawParms { @@ -157,9 +157,9 @@ struct DrawParms double top; double left; float Alpha; - uint32 fillcolor; + uint32_t fillcolor; FRemapTable *remap; - uint32 colorOverlay; + uint32_t colorOverlay; INTBOOL alphaChannel; INTBOOL flipX; //float shadowAlpha; @@ -197,14 +197,15 @@ class DCanvas : public DObject { DECLARE_ABSTRACT_CLASS (DCanvas, DObject) public: - DCanvas (int width, int height); + DCanvas (int width, int height, bool bgra); virtual ~DCanvas (); // Member variable access - inline BYTE *GetBuffer () const { return Buffer; } + inline uint8_t *GetBuffer () const { return Buffer; } inline int GetWidth () const { return Width; } inline int GetHeight () const { return Height; } inline int GetPitch () const { return Pitch; } + inline bool IsBgra() const { return Bgra; } virtual bool IsValid (); @@ -214,10 +215,10 @@ public: virtual bool IsLocked () { return Buffer != NULL; } // Returns true if the surface is locked // Draw a linear block of pixels into the canvas - virtual void DrawBlock (int x, int y, int width, int height, const BYTE *src) const; + virtual void DrawBlock (int x, int y, int width, int height, const uint8_t *src) const; // Reads a linear block of pixels into the view buffer. - virtual void GetBlock (int x, int y, int width, int height, BYTE *dest) const; + virtual void GetBlock (int x, int y, int width, int height, uint8_t *dest) const; // Dim the entire canvas for the menus virtual void Dim (PalEntry color = 0); @@ -234,22 +235,22 @@ public: struct FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip); // Set an area to a specified color - virtual void Clear (int left, int top, int right, int bottom, int palcolor, uint32 color); + virtual void Clear (int left, int top, int right, int bottom, int palcolor, uint32_t color); // Draws a line - virtual void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 realcolor); + virtual void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32_t realcolor); // Draws a single pixel - virtual void DrawPixel(int x, int y, int palcolor, uint32 rgbcolor); + virtual void DrawPixel(int x, int y, int palcolor, uint32_t rgbcolor); // Calculate gamma table - void CalcGamma (float gamma, BYTE gammalookup[256]); + void CalcGamma (float gamma, uint8_t gammalookup[256]); // 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); + virtual void GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type); // Releases the screenshot buffer. virtual void ReleaseScreenshotBuffer(); @@ -277,20 +278,21 @@ public: void DrawChar(FFont *font, int normalcolor, double x, double y, int character, VMVa_List &args); protected: - BYTE *Buffer; + uint8_t *Buffer; int Width; int Height; int Pitch; int LockCount; + bool Bgra; void DrawTextCommon(FFont *font, int normalcolor, double x, double y, const char *string, DrawParms &parms); - bool ClipBox (int &left, int &top, int &width, int &height, const BYTE *&src, const int srcpitch) const; - void DrawTextureV(FTexture *img, double x, double y, uint32 tag, va_list tags) = delete; + bool ClipBox (int &left, int &top, int &width, int &height, const uint8_t *&src, const int srcpitch) const; + void DrawTextureV(FTexture *img, double x, double y, uint32_t tag, va_list tags) = delete; virtual void DrawTextureParms(FTexture *img, DrawParms &parms); template - bool ParseDrawTextureTags(FTexture *img, double x, double y, DWORD tag, T& tags, DrawParms *parms, bool fortext) const; + bool ParseDrawTextureTags(FTexture *img, double x, double y, uint32_t tag, T& tags, DrawParms *parms, bool fortext) const; DCanvas() {} @@ -298,8 +300,6 @@ private: // Keep track of canvases, for automatic destruction at exit DCanvas *Next; static DCanvas *CanvasChain; - - void PUTTRANSDOT (int xx, int yy, int basecolor, int level); }; // A canvas in system memory. @@ -308,7 +308,7 @@ class DSimpleCanvas : public DCanvas { DECLARE_CLASS (DSimpleCanvas, DCanvas) public: - DSimpleCanvas (int width, int height); + DSimpleCanvas (int width, int height, bool bgra); ~DSimpleCanvas (); bool IsValid (); @@ -318,7 +318,7 @@ public: protected: void Resize(int width, int height); - BYTE *MemBuffer; + uint8_t *MemBuffer; DSimpleCanvas() {} }; @@ -348,7 +348,7 @@ class DFrameBuffer : public DSimpleCanvas { DECLARE_ABSTRACT_CLASS (DFrameBuffer, DSimpleCanvas) public: - DFrameBuffer (int width, int height); + DFrameBuffer (int width, int height, bool bgra); // Force the surface to use buffered output if true is passed. virtual bool Lock (bool buffered) = 0; @@ -426,9 +426,9 @@ public: virtual bool WipeDo(int ticks); virtual void WipeCleanup(); - virtual void ScaleCoordsFromWindow(SWORD &x, SWORD &y) {} + virtual void ScaleCoordsFromWindow(int16_t &x, int16_t &y) {} - uint32 GetLastFPS() const { return LastCount; } + uint32_t GetLastFPS() const { return LastCount; } #ifdef _WIN32 virtual void PaletteChanged () = 0; @@ -442,12 +442,13 @@ public: protected: void DrawRateStuff (); - void CopyFromBuff (BYTE *src, int srcPitch, int width, int height, BYTE *dest); + void CopyFromBuff (uint8_t *src, int srcPitch, int width, int height, uint8_t *dest); + void CopyWithGammaBgra(void *output, int pitch, const uint8_t *gammared, const uint8_t *gammagreen, const uint8_t *gammablue, PalEntry flash, int flash_amount); DFrameBuffer () {} private: - uint32 LastMS, LastSec, FrameCount, LastCount, LastTic; + uint32_t LastMS, LastSec, FrameCount, LastCount, LastTic; }; @@ -467,39 +468,39 @@ EXTERN_CVAR (Float, Gamma) // Use a union so we can "overflow" without warnings. // Otherwise, we get stuff like this from Clang (when compiled // with -fsanitize=bounds) while running: -// src/v_video.cpp:390:12: runtime error: index 1068 out of bounds for type 'BYTE [32]' -// src/r_draw.cpp:273:11: runtime error: index 1057 out of bounds for type 'BYTE [32]' +// src/v_video.cpp:390:12: runtime error: index 1068 out of bounds for type 'uint8_t [32]' +// src/r_draw.cpp:273:11: runtime error: index 1057 out of bounds for type 'uint8_t [32]' union ColorTable32k { - BYTE RGB[32][32][32]; - BYTE All[32 *32 *32]; + uint8_t RGB[32][32][32]; + uint8_t All[32 *32 *32]; }; -extern "C" ColorTable32k RGB32k; +extern ColorTable32k RGB32k; // [SP] RGB666 support union ColorTable256k { - BYTE RGB[64][64][64]; - BYTE All[64 *64 *64]; + uint8_t RGB[64][64][64]; + uint8_t All[64 *64 *64]; }; -extern "C" ColorTable256k RGB256k; +extern ColorTable256k RGB256k; // Col2RGB8 is a pre-multiplied palette for color lookup. It is stored in a // special R10B10G10 format for efficient blending computation. // --RRRRRrrr--BBBBBbbb--GGGGGggg-- at level 64 // --------rrrr------bbbb------gggg at level 1 -extern "C" DWORD Col2RGB8[65][256]; +extern uint32_t Col2RGB8[65][256]; // Col2RGB8_LessPrecision is the same as Col2RGB8, but the LSB for red // and blue are forced to zero, so if the blend overflows, it won't spill // over into the next component's value. // --RRRRRrrr-#BBBBBbbb-#GGGGGggg-- at level 64 // --------rrr#------bbb#------gggg at level 1 -extern "C" DWORD *Col2RGB8_LessPrecision[65]; +extern uint32_t *Col2RGB8_LessPrecision[65]; // Col2RGB8_Inverse is the same as Col2RGB8_LessPrecision, except the source // palette has been inverted. -extern "C" DWORD Col2RGB8_Inverse[65][256]; +extern uint32_t Col2RGB8_Inverse[65][256]; // "Magic" numbers used during the blending: // --000001111100000111110000011111 = 0x01f07c1f @@ -522,14 +523,14 @@ void V_MarkRect (int x, int y, int width, int height); class FScanner; // Returns the closest color to the one desired. String // should be of the form "rr gg bb". -int V_GetColorFromString (const DWORD *palette, const char *colorstring, FScriptPosition *sc = nullptr); +int V_GetColorFromString (const uint32_t *palette, const char *colorstring, FScriptPosition *sc = nullptr); // Scans through the X11R6RGB lump for a matching color // and returns a color string suitable for V_GetColorFromString. FString V_GetColorStringByName (const char *name, FScriptPosition *sc = nullptr); // Tries to get color by name, then by string -int V_GetColor (const DWORD *palette, const char *str, FScriptPosition *sc = nullptr); -int V_GetColor(const DWORD *palette, FScanner &sc); +int V_GetColor (const uint32_t *palette, const char *str, FScriptPosition *sc = nullptr); +int V_GetColor(const uint32_t *palette, FScanner &sc); void V_DrawFrame (int left, int top, int width, int height); // If the view size is not full screen, draws a border around it. diff --git a/src/vectors.h b/src/vectors.h index 1852609f2a..0178e28bbc 100644 --- a/src/vectors.h +++ b/src/vectors.h @@ -325,6 +325,11 @@ struct TVector3 { } + TVector3(vec_t *o) + : X(o[0]), Y(o[1]), Z(o[2]) + { + } + TVector3 (const TVector3 &other) : X(other.X), Y(other.Y), Z(other.Z) { @@ -520,6 +525,53 @@ struct TVector3 return Vector2(v2.X - v3.X, v2.Y - v3.Y); } + void GetRightUp(TVector3 &right, TVector3 &up) + { + TVector3 n(X, Y, Z); + TVector3 fn(fabs(n.X), fabs(n.Y), fabs(n.Z)); + int major = 0; + + if (fn[1] > fn[major]) major = 1; + if (fn[2] > fn[major]) major = 2; + + // build right vector by hand + if (fabs(fn[0] - 1.0f) < FLT_EPSILON || fabs(fn[1] - 1.0f) < FLT_EPSILON || fabs(fn[2] - 1.0f) < FLT_EPSILON) + { + if (major == 0 && n[0] > 0.f) + { + right = { 0.f, 0.f, -1.f }; + } + else if (major == 0) + { + right = { 0.f, 0.f, 1.f }; + } + + if (major == 1 || (major == 2 && n[2] > 0.f)) + { + right = { 1.f, 0.f, 0.f }; + } + + if (major == 2 && n[2] < 0.0f) + { + right = { -1.f, 0.f, 0.f }; + } + } + else + { + static TVector3 axis[3] = + { + { 1.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f } + }; + + right = axis[major] ^ n; + } + + up = n ^right; + right.MakeUnit();; + up.MakeUnit(); + } // Returns the angle (in radians) that the ray (0,0)-(X,Y) faces @@ -542,7 +594,7 @@ struct TVector3 { double len = Length(); if (len != 0) len = 1 / len; - return *this * len; + return *this * (vec_t)len; } // Scales this vector into a unit vector @@ -550,7 +602,7 @@ struct TVector3 { double len = Length(); if (len != 0) len = 1 / len; - *this *= len; + *this *= (vec_t)len; } // Resizes this vector to be the specified length (if it is not 0) @@ -582,7 +634,7 @@ struct TVector3 } // Dot product - double operator | (const TVector3 &other) const + vec_t operator | (const TVector3 &other) const { return X*other.X + Y*other.Y + Z*other.Z; } diff --git a/src/w_wad.cpp b/src/w_wad.cpp index c9230f5842..1f62b15c58 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -429,7 +429,7 @@ int FWadCollection::CheckNumForName (const char *name, int space) union { char uname[8]; - QWORD qname; + uint64_t qname; }; uint32_t i; @@ -475,7 +475,7 @@ int FWadCollection::CheckNumForName (const char *name, int space, int wadnum, bo union { char uname[8]; - QWORD qname; + uint64_t qname; }; uint32_t i; @@ -1060,7 +1060,7 @@ int FWadCollection::FindLump (const char *name, int *lastlump, bool anyns) union { char name8[8]; - QWORD qname; + uint64_t qname; }; LumpRecord *lump_p; diff --git a/src/weightedlist.h b/src/weightedlist.h index 55bcc95a88..4582f6e0f7 100644 --- a/src/weightedlist.h +++ b/src/weightedlist.h @@ -48,7 +48,7 @@ class TWeightedList Choice *Next; uint16_t Weight; - BYTE RandomVal; // 0 (never) - 255 (always) + uint8_t RandomVal; // 0 (never) - 255 (always) T Value; }; @@ -104,7 +104,7 @@ void TWeightedList::AddEntry (T value, uint16_t weight) template T TWeightedList::PickEntry () const { - BYTE randomnum = RandomClass(); + uint8_t randomnum = RandomClass(); Choice *choice = Choices; while (choice != NULL && randomnum > choice->RandomVal) @@ -147,7 +147,7 @@ void TWeightedList::RecalcRandomVals () for (choice = Choices; choice->Next != NULL; choice = choice->Next) { randVal += (double)choice->Weight * weightDenom; - choice->RandomVal = (BYTE)(randVal * 255.0); + choice->RandomVal = (uint8_t)(randVal * 255.0); } } diff --git a/src/win32/critsec.h b/src/win32/critsec.cpp similarity index 56% rename from src/win32/critsec.h rename to src/win32/critsec.cpp index e95bf1b052..4fcd085a9a 100644 --- a/src/win32/critsec.h +++ b/src/win32/critsec.cpp @@ -1,22 +1,18 @@ // Wraps a Windows critical section object. -#ifndef CRITSEC_H -#define CRITSEC_H - #ifndef _WINNT_ #define WIN32_LEAN_AND_MEAN #include -#define USE_WINDOWS_DWORD #endif -class FCriticalSection +class FInternalCriticalSection { public: - FCriticalSection() + FInternalCriticalSection() { InitializeCriticalSection(&CritSec); } - ~FCriticalSection() + ~FInternalCriticalSection() { DeleteCriticalSection(&CritSec); } @@ -39,4 +35,23 @@ private: CRITICAL_SECTION CritSec; }; -#endif + +FInternalCriticalSection *CreateCriticalSection() +{ + return new FInternalCriticalSection(); +} + +void DeleteCriticalSection(FInternalCriticalSection *c) +{ + delete c; +} + +void EnterCriticalSection(FInternalCriticalSection *c) +{ + c->Enter(); +} + +void LeaveCriticalSection(FInternalCriticalSection *c) +{ + c->Leave(); +} diff --git a/src/win32/eaxedit.cpp b/src/win32/eaxedit.cpp index c28603aa61..09344a46b4 100644 --- a/src/win32/eaxedit.cpp +++ b/src/win32/eaxedit.cpp @@ -9,7 +9,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "resource.h" #include "s_sound.h" #include "templates.h" diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index 3f4bb3c130..3c32555155 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -51,7 +51,6 @@ #include -#define USE_WINDOWS_DWORD #include "doomtype.h" #include "c_dispatch.h" @@ -63,7 +62,6 @@ #include "v_pfx.h" #include "stats.h" #include "doomerrors.h" -#include "r_main.h" #include "r_data/r_translate.h" #include "f_wipe.h" #include "sbar.h" @@ -72,8 +70,10 @@ #include "doomstat.h" #include "v_palette.h" #include "w_wad.h" +#include "textures.h" #include "r_data/colormaps.h" #include "SkylineBinPack.h" +#include "swrenderer/scene/r_light.h" // MACROS ------------------------------------------------------------------ @@ -243,8 +243,8 @@ CVAR(Bool, vid_hwaalines, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // //========================================================================== -D3DFB::D3DFB (UINT adapter, int width, int height, bool fullscreen) - : BaseWinFB (width, height) +D3DFB::D3DFB (UINT adapter, int width, int height, bool bgra, bool fullscreen) + : BaseWinFB (width, height, bgra) { D3DPRESENT_PARAMETERS d3dpp; @@ -766,14 +766,16 @@ void D3DFB::KillNativeTexs() bool D3DFB::CreateFBTexture () { - if (FAILED(D3DDevice->CreateTexture(Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) + FBFormat = IsBgra() ? D3DFMT_A8R8G8B8 : D3DFMT_L8; + + if (FAILED(D3DDevice->CreateTexture(Width, Height, 1, D3DUSAGE_DYNAMIC, FBFormat, D3DPOOL_DEFAULT, &FBTexture, NULL))) { int pow2width, pow2height, i; for (i = 1; i < Width; i <<= 1) {} pow2width = i; for (i = 1; i < Height; i <<= 1) {} pow2height = i; - if (FAILED(D3DDevice->CreateTexture(pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) + if (FAILED(D3DDevice->CreateTexture(pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, FBFormat, D3DPOOL_DEFAULT, &FBTexture, NULL))) { return false; } @@ -1321,20 +1323,45 @@ void D3DFB::Draw3DPart(bool copy3d) SUCCEEDED(FBTexture->LockRect (0, &lockrect, NULL, D3DLOCK_DISCARD))) || SUCCEEDED(FBTexture->LockRect (0, &lockrect, &texrect, 0))) { - if (lockrect.Pitch == Pitch && Pitch == Width) + if (IsBgra() && FBFormat == D3DFMT_A8R8G8B8) { - memcpy (lockrect.pBits, MemBuffer, Width * Height); + if (lockrect.Pitch == Pitch * sizeof(uint32_t) && Pitch == Width) + { + memcpy(lockrect.pBits, MemBuffer, Width * Height * sizeof(uint32_t)); + } + else + { + uint32_t *dest = (uint32_t *)lockrect.pBits; + uint32_t *src = (uint32_t*)MemBuffer; + for (int y = 0; y < Height; y++) + { + memcpy(dest, src, Width * sizeof(uint32_t)); + dest = reinterpret_cast(reinterpret_cast(dest) + lockrect.Pitch); + src += Pitch; + } + } + } + else if (!IsBgra() && FBFormat == D3DFMT_L8) + { + if (lockrect.Pitch == Pitch && Pitch == Width) + { + memcpy(lockrect.pBits, MemBuffer, Width * Height); + } + else + { + uint8_t *dest = (uint8_t *)lockrect.pBits; + uint8_t *src = (uint8_t *)MemBuffer; + for (int y = 0; y < Height; y++) + { + memcpy(dest, src, Width); + dest = reinterpret_cast(reinterpret_cast(dest) + lockrect.Pitch); + src += Pitch; + } + } } else { - BYTE *dest = (BYTE *)lockrect.pBits; - BYTE *src = MemBuffer; - for (int y = 0; y < Height; y++) - { - memcpy (dest, src, Width); - dest += lockrect.Pitch; - src += Pitch; - } + memset(lockrect.pBits, 0, lockrect.Pitch * Height); } FBTexture->UnlockRect (0); } @@ -1366,14 +1393,17 @@ void D3DFB::Draw3DPart(bool copy3d) memset(Constant, 0, sizeof(Constant)); SetAlphaBlend(D3DBLENDOP(0)); EnableAlphaTest(FALSE); - SetPixelShader(Shaders[SHADER_NormalColorPal]); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_NormalColor]); + else + SetPixelShader(Shaders[SHADER_NormalColorPal]); if (copy3d) { FBVERTEX verts[4]; D3DCOLOR color0, color1; if (Accel2D) { - auto &map = swrenderer::realfixedcolormap; + auto map = swrenderer::CameraLight::Instance()->ShaderColormap(); if (map == NULL) { color0 = 0; @@ -1381,9 +1411,12 @@ void D3DFB::Draw3DPart(bool copy3d) } else { - color0 = D3DCOLOR_COLORVALUE(map->ColorizeStart[0] / 2, map->ColorizeStart[1] / 2, map->ColorizeStart[2] / 2, 0); - color1 = D3DCOLOR_COLORVALUE(map->ColorizeEnd[0] / 2, map->ColorizeEnd[1] / 2, map->ColorizeEnd[2] / 2, 1); - SetPixelShader(Shaders[SHADER_SpecialColormapPal]); + color0 = D3DCOLOR_COLORVALUE(map->ColorizeStart[0]/2, map->ColorizeStart[1]/2, map->ColorizeStart[2]/2, 0); + color1 = D3DCOLOR_COLORVALUE(map->ColorizeEnd[0]/2, map->ColorizeEnd[1]/2, map->ColorizeEnd[2]/2, 1); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_SpecialColormap]); + else + SetPixelShader(Shaders[SHADER_SpecialColormapPal]); } } else @@ -1394,7 +1427,10 @@ void D3DFB::Draw3DPart(bool copy3d) CalcFullscreenCoords(verts, Accel2D, false, color0, color1); D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX)); } - SetPixelShader(Shaders[SHADER_NormalColorPal]); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_NormalColor]); + else + SetPixelShader(Shaders[SHADER_NormalColorPal]); } //========================================================================== @@ -1471,10 +1507,10 @@ void D3DFB::UpdateGammaTexture(float igamma) if (GammaTexture != NULL && SUCCEEDED(GammaTexture->LockRect(0, &lockrect, NULL, 0))) { - BYTE *pix = (BYTE *)lockrect.pBits; + uint8_t *pix = (uint8_t *)lockrect.pBits; for (int i = 0; i <= 128; ++i) { - pix[i*4+2] = pix[i*4+1] = pix[i*4] = BYTE(255.f * powf(i / 128.f, igamma)); + pix[i*4+2] = pix[i*4+1] = pix[i*4] = uint8_t(255.f * powf(i / 128.f, igamma)); pix[i*4+3] = 255; } GammaTexture->UnlockRect(0); @@ -1518,7 +1554,7 @@ void D3DFB::DoOffByOneCheck () // Create an easily recognizable R3G3B2 palette. if (SUCCEEDED(PaletteTexture->LockRect(0, &lockrect, NULL, 0))) { - BYTE *pal = (BYTE *)(lockrect.pBits); + uint8_t *pal = (uint8_t *)(lockrect.pBits); for (i = 0; i < 256; ++i) { pal[i*4+0] = (i & 0x03) << 6; // blue @@ -1537,7 +1573,7 @@ void D3DFB::DoOffByOneCheck () { for (i = 0; i < 256; ++i) { - ((BYTE *)lockrect.pBits)[i] = i; + ((uint8_t *)lockrect.pBits)[i] = i; } FBTexture->UnlockRect(0); } @@ -1580,7 +1616,7 @@ void D3DFB::DoOffByOneCheck () if (SUCCEEDED(D3DDevice->GetRenderTargetData(testsurf, readsurf)) && SUCCEEDED(readsurf->LockRect(&lockrect, &testrect, D3DLOCK_READONLY))) { - const BYTE *pix = (const BYTE *)lockrect.pBits; + const uint8_t *pix = (const uint8_t *)lockrect.pBits; for (i = 0; i < 256; ++i, pix += 4) { c = (pix[0] >> 6) | // blue @@ -1615,7 +1651,7 @@ void D3DFB::UploadPalette () } if (SUCCEEDED(PaletteTexture->LockRect(0, &lockrect, NULL, 0))) { - BYTE *pix = (BYTE *)lockrect.pBits; + uint8_t *pix = (uint8_t *)lockrect.pBits; int i; for (i = 0; i < SkipAt; ++i, pix += 4) @@ -1722,7 +1758,7 @@ void D3DFB::SetBlendingRect(int x1, int y1, int x2, int y2) // //========================================================================== -void D3DFB::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type) +void D3DFB::GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) { D3DLOCKED_RECT lrect; @@ -1748,7 +1784,7 @@ void D3DFB::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_ } else { - buffer = (const BYTE *)lrect.pBits; + buffer = (const uint8_t *)lrect.pBits; pitch = lrect.Pitch; color_type = SS_BGRA; } @@ -2283,7 +2319,7 @@ bool D3DTex::Update() D3DSURFACE_DESC desc; D3DLOCKED_RECT lrect; RECT rect; - BYTE *dest; + uint8_t *dest; assert(Box != NULL); assert(Box->Owner != NULL); @@ -2299,7 +2335,7 @@ bool D3DTex::Update() { return false; } - dest = (BYTE *)lrect.pBits; + dest = (uint8_t *)lrect.pBits; if (Box->Padded) { dest += lrect.Pitch + (desc.Format == D3DFMT_L8 ? 1 : 4); @@ -2308,7 +2344,7 @@ bool D3DTex::Update() if (Box->Padded) { // Clear top padding row. - dest = (BYTE *)lrect.pBits; + dest = (uint8_t *)lrect.pBits; int numbytes = GameTex->GetWidth() + 2; if (desc.Format != D3DFMT_L8) { @@ -2600,7 +2636,7 @@ FNativePalette *D3DFB::CreatePalette(FRemapTable *remap) // //========================================================================== -void D3DFB::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color) +void D3DFB::Clear (int left, int top, int right, int bottom, int palcolor, uint32_t color) { if (In2D < 2) { @@ -2699,7 +2735,7 @@ void D3DFB::EndLineBatch() // //========================================================================== -void D3DFB::DrawLine(int x0, int y0, int x1, int y1, int palcolor, uint32 color) +void D3DFB::DrawLine(int x0, int y0, int x1, int y1, int palcolor, uint32_t color) { if (In2D < 2) { @@ -2747,7 +2783,7 @@ void D3DFB::DrawLine(int x0, int y0, int x1, int y1, int palcolor, uint32 color) // //========================================================================== -void D3DFB::DrawPixel(int x, int y, int palcolor, uint32 color) +void D3DFB::DrawPixel(int x, int y, int palcolor, uint32_t color) { if (In2D < 2) { @@ -3092,7 +3128,7 @@ void D3DFB::FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, DAngle rotation, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) { // Use an equation similar to player sprites to determine shade - double fadelevel = clamp((LIGHT2SHADE(lightlevel)/65536. - 12) / NUMCOLORMAPS, 0.0, 1.0); + double fadelevel = clamp((swrenderer::LightVisibility::LightLevelToShade(lightlevel, true)/65536. - 12) / NUMCOLORMAPS, 0.0, 1.0); BufferedTris *quad; FBVERTEX *verts; diff --git a/src/win32/fb_d3d9_wipe.cpp b/src/win32/fb_d3d9_wipe.cpp index f8fd50273c..62533899ac 100644 --- a/src/win32/fb_d3d9_wipe.cpp +++ b/src/win32/fb_d3d9_wipe.cpp @@ -44,7 +44,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "doomtype.h" #include "f_wipe.h" #include "win32iface.h" @@ -87,7 +86,7 @@ public: private: static const int WIDTH = 64, HEIGHT = 64; - BYTE BurnArray[WIDTH * (HEIGHT + 5)]; + uint8_t BurnArray[WIDTH * (HEIGHT + 5)]; IDirect3DTexture9 *BurnTexture; int Density; int BurnTime; @@ -602,8 +601,8 @@ bool D3DFB::Wiper_Burn::Run(int ticks, D3DFB *fb) D3DLOCKED_RECT lrect; if (SUCCEEDED(BurnTexture->LockRect(0, &lrect, NULL, D3DLOCK_DISCARD))) { - const BYTE *src = BurnArray; - BYTE *dest = (BYTE *)lrect.pBits; + const uint8_t *src = BurnArray; + uint8_t *dest = (uint8_t *)lrect.pBits; for (int y = HEIGHT; y != 0; --y) { for (int x = WIDTH; x != 0; --x) diff --git a/src/win32/fb_ddraw.cpp b/src/win32/fb_ddraw.cpp index ec4fe6ce02..b7f314608f 100644 --- a/src/win32/fb_ddraw.cpp +++ b/src/win32/fb_ddraw.cpp @@ -32,7 +32,6 @@ ** */ - // HEADER FILES ------------------------------------------------------------ #define DIRECTDRAW_VERSION 0x0300 @@ -42,7 +41,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "doomtype.h" #include "c_dispatch.h" @@ -121,7 +119,7 @@ cycle_t BlitCycles; // CODE -------------------------------------------------------------------- DDrawFB::DDrawFB (int width, int height, bool fullscreen) - : BaseWinFB (width, height) + : BaseWinFB (width, height, false) { int i; @@ -164,7 +162,7 @@ DDrawFB::DDrawFB (int width, int height, bool fullscreen) PalEntries[i].peRed = GPalette.BaseColors[i].r; PalEntries[i].peGreen = GPalette.BaseColors[i].g; PalEntries[i].peBlue = GPalette.BaseColors[i].b; - GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = (BYTE)i; + GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = (uint8_t)i; } memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); @@ -782,7 +780,7 @@ void DDrawFB::RebuildColorTable () } for (i = 0; i < 256; i++) { - GPfxPal.Pal8[i] = (BYTE)BestColor ((uint32 *)syspal, PalEntries[i].peRed, + GPfxPal.Pal8[i] = (uint8_t)BestColor ((uint32_t *)syspal, PalEntries[i].peRed, PalEntries[i].peGreen, PalEntries[i].peBlue); } } @@ -997,7 +995,7 @@ DDrawFB::LockSurfRes DDrawFB::LockSurf (LPRECT lockrect, LPDIRECTDRAWSURFACE toL LOG1 ("Final result after restoration attempts: %08lx\n", hr); return NoGood; } - Buffer = (BYTE *)desc.lpSurface; + Buffer = (uint8_t *)desc.lpSurface; Pitch = desc.lPitch; BufferingNow = false; return wasLost ? GoodWasLost : Good; @@ -1133,7 +1131,7 @@ void DDrawFB::Update () { if (LockSurf (NULL, NULL) != NoGood) { - BYTE *writept = Buffer + (TrueHeight - Height)/2*Pitch; + uint8_t *writept = Buffer + (TrueHeight - Height)/2*Pitch; LOG3 ("Copy %dx%d (%d)\n", Width, Height, BufferPitch); if (UsePfx) { diff --git a/src/win32/hardware.cpp b/src/win32/hardware.cpp index 787c0a4f38..b083f4fe96 100644 --- a/src/win32/hardware.cpp +++ b/src/win32/hardware.cpp @@ -35,7 +35,6 @@ #define WIN32_LEAN_AND_MEAN #include -#define USE_WINDOWS_DWORD #include "hardware.h" #include "win32iface.h" #include "i_video.h" @@ -47,11 +46,13 @@ #include "doomstat.h" #include "m_argv.h" #include "version.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" EXTERN_CVAR (Bool, ticker) EXTERN_CVAR (Bool, fullscreen) +EXTERN_CVAR (Bool, swtruecolor) EXTERN_CVAR (Float, vid_winscale) +EXTERN_CVAR (Bool, vid_forceddraw) CVAR(Int, win_x, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, win_y, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -68,8 +69,42 @@ FRenderer *gl_CreateInterface(); void I_RestartRenderer(); int currentrenderer = -1; +int currentcanvas = -1; +int currentgpuswitch = -1; bool changerenderer; +// Optimus/Hybrid switcher +CUSTOM_CVAR(Int, vid_gpuswitch, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self != currentgpuswitch) + { + switch (self) + { + case 0: + Printf("Selecting default GPU...\n"); + break; + case 1: + Printf("Selecting high-performance dedicated GPU...\n"); + break; + case 2: + Printf("Selecting power-saving integrated GPU...\n"); + break; + default: + Printf("Unknown option (%d) - falling back to 'default'\n", *vid_gpuswitch); + self = 0; + break; + } + Printf("You must restart " GAMENAME " for this change to take effect.\n"); + } +} + +// Software OpenGL canvas +CUSTOM_CVAR(Bool, vid_used3d, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if ((self ? 1 : 0) != currentcanvas) + Printf("You must restart " GAMENAME " for this change to take effect.\n"); +} + // [ZDoomGL] CUSTOM_CVAR (Int, vid_renderer, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { @@ -118,6 +153,13 @@ void I_InitGraphics () { UCVarValue val; + // todo: implement ATI version of this. this only works for nvidia notebooks, for now. + currentgpuswitch = vid_gpuswitch; + if (currentgpuswitch == 1) + putenv("SHIM_MCCOMPAT=0x800000001"); // discrete + else if (currentgpuswitch == 2) + putenv("SHIM_MCCOMPAT=0x800000000"); // integrated + // If the focus window is destroyed, it doesn't go back to the active window. // (e.g. because the net pane was up, and a button on it had focus) if (GetFocus() == NULL && GetActiveWindow() == Window) @@ -136,9 +178,13 @@ void I_InitGraphics () val.Bool = !!Args->CheckParm ("-devparm"); ticker.SetGenericRepDefault (val, CVAR_Bool); - //currentrenderer = vid_renderer; - if (currentrenderer==1) Video = gl_CreateVideo(); - else Video = new Win32Video (0); + if (currentcanvas == 1) // Software Canvas: 1 = D3D or DirectDraw, 0 = OpenGL + if (currentrenderer == 1) + Video = gl_CreateVideo(); + else + Video = new Win32Video(0); + else + Video = gl_CreateVideo(); if (Video == NULL) I_FatalError ("Failed to initialize display"); @@ -156,6 +202,17 @@ static void I_DeleteRenderer() void I_CreateRenderer() { currentrenderer = vid_renderer; + currentcanvas = vid_used3d; + if (currentrenderer == 1) + Printf("Renderer: OpenGL\n"); + else if (currentcanvas == 0) + Printf("Renderer: Software on OpenGL\n"); + else if (currentcanvas == 1 && vid_forceddraw == false) + Printf("Renderer: Software on Direct3D\n"); + else if (currentcanvas == 1) + Printf("Renderer: Software on DirectDraw\n"); + else + Printf("Renderer: Unknown\n"); if (Renderer == NULL) { if (currentrenderer==1) Renderer = gl_CreateInterface(); @@ -190,7 +247,7 @@ DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old) } break; } - DFrameBuffer *res = Video->CreateFrameBuffer (width, height, fs, old); + DFrameBuffer *res = Video->CreateFrameBuffer (width, height, swtruecolor, fs, old); //* Right now, CreateFrameBuffer cannot return NULL if (res == NULL) @@ -357,6 +414,16 @@ void I_RestoreWindowedPos () extern int NewWidth, NewHeight, NewBits, DisplayBits; +CUSTOM_CVAR(Bool, swtruecolor, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ + // Strictly speaking this doesn't require a mode switch, but it is the easiest + // way to force a CreateFramebuffer call without a lot of refactoring. + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; +} + CUSTOM_CVAR (Bool, fullscreen, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) { NewWidth = screen->GetWidth(); diff --git a/src/win32/hardware.h b/src/win32/hardware.h index 37a9c794c2..d996bf3944 100644 --- a/src/win32/hardware.h +++ b/src/win32/hardware.h @@ -45,7 +45,7 @@ class IVideo virtual EDisplayType GetDisplayType () = 0; virtual void SetWindowedScale (float scale) = 0; - virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old) = 0; + virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old) = 0; virtual void StartModeIterator (int bits, bool fs) = 0; virtual bool NextMode (int *width, int *height, bool *letterbox) = 0; diff --git a/src/win32/i_cd.cpp b/src/win32/i_cd.cpp index 8c328a1499..12a673eb90 100644 --- a/src/win32/i_cd.cpp +++ b/src/win32/i_cd.cpp @@ -39,7 +39,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "doomtype.h" #include "c_cvars.h" #include "c_dispatch.h" diff --git a/src/win32/i_crash.cpp b/src/win32/i_crash.cpp index c7b4376f1e..2e904cc0f5 100644 --- a/src/win32/i_crash.cpp +++ b/src/win32/i_crash.cpp @@ -63,7 +63,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "doomtype.h" #include "resource.h" #include "version.h" @@ -168,7 +167,7 @@ namespace zip struct LocalFileHeader { DWORD Magic; // 0 - BYTE VersionToExtract[2]; // 4 + uint8_t VersionToExtract[2]; // 4 uint16_t Flags; // 6 uint16_t Method; // 8 uint16_t ModTime; // 10 @@ -183,8 +182,8 @@ namespace zip struct CentralDirectoryEntry { DWORD Magic; - BYTE VersionMadeBy[2]; - BYTE VersionToExtract[2]; + uint8_t VersionMadeBy[2]; + uint8_t VersionToExtract[2]; uint16_t Flags; uint16_t Method; uint16_t ModTime; @@ -246,7 +245,7 @@ static HANDLE MakeZip (); static void AddZipFile (HANDLE ziphandle, TarFile *whichfile, short dosdate, short dostime); static HANDLE CreateTempFile (); -static void DumpBytes (HANDLE file, BYTE *address); +static void DumpBytes (HANDLE file, uint8_t *address); static void AddStackInfo (HANDLE file, void *dumpaddress, DWORD code, CONTEXT *ctxt); static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack, DWORD *jump, CONTEXT *ctxt); static void AddToolHelp (HANDLE file); @@ -388,7 +387,7 @@ DWORD *GetTopOfStack (void *top) if (VirtualQuery (top, &memInfo, sizeof(memInfo))) { - return (DWORD *)((BYTE *)memInfo.BaseAddress + memInfo.RegionSize); + return (DWORD *)((uint8_t *)memInfo.BaseAddress + memInfo.RegionSize); } else { @@ -607,7 +606,7 @@ void CreateCrashLog (char *custominfo, DWORD customsize, HWND richlog) if (file != INVALID_HANDLE_VALUE) { - BYTE buffer[512]; + uint8_t buffer[512]; DWORD left; for (;; customsize -= left, custominfo += left) @@ -835,7 +834,7 @@ HANDLE WriteTextReport () #else Writef (file, "\r\nBytes near RIP:"); #endif - DumpBytes (file, (BYTE *)CrashPointers.ExceptionRecord->ExceptionAddress-16); + DumpBytes (file, (uint8_t *)CrashPointers.ExceptionRecord->ExceptionAddress-16); if (ctxt->ContextFlags & CONTEXT_CONTROL) { @@ -946,11 +945,11 @@ static void AddStackInfo (HANDLE file, void *dumpaddress, DWORD code, CONTEXT *c { DWORD *addr = (DWORD *)dumpaddress, *jump; DWORD *topOfStack = GetTopOfStack (dumpaddress); - BYTE peekb; + uint8_t peekb; #ifdef _M_IX86 DWORD peekd; #else - QWORD peekq; + uint64_t peekq; #endif jump = topOfStack; @@ -1002,9 +1001,9 @@ static void AddStackInfo (HANDLE file, void *dumpaddress, DWORD code, CONTEXT *c Writef (file, " "); } #else - if ((QWORD *)topOfStack - (QWORD *)scan < 2) + if ((uint64_t *)topOfStack - (uint64_t *)scan < 2) { - max = (QWORD *)topOfStack - (QWORD *)scan; + max = (uint64_t *)topOfStack - (uint64_t *)scan; } else { @@ -1027,7 +1026,7 @@ static void AddStackInfo (HANDLE file, void *dumpaddress, DWORD code, CONTEXT *c Writef (file, " "); for (i = 0; i < int(max*sizeof(void*)); ++i) { - if (!SafeReadMemory ((BYTE *)scan + i, &peekb, 1)) + if (!SafeReadMemory ((uint8_t *)scan + i, &peekb, 1)) { break; } @@ -1054,7 +1053,7 @@ static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack, DWORD { DWORD *addr = (DWORD *)dumpaddress; - BYTE *pBaseOfImage = (BYTE *)GetModuleHandle (0); + uint8_t *pBaseOfImage = (uint8_t *)GetModuleHandle (0); IMAGE_OPTIONAL_HEADER *pHeader = (IMAGE_OPTIONAL_HEADER *)(pBaseOfImage + ((IMAGE_DOS_HEADER*)pBaseOfImage)->e_lfanew + sizeof(IMAGE_NT_SIGNATURE) + sizeof(IMAGE_FILE_HEADER)); @@ -1082,8 +1081,8 @@ static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack, DWORD }; // Check if address is after a call statement. Print what was called if it is. - const BYTE *bytep = (BYTE *)code; - BYTE peekb; + const uint8_t *bytep = (uint8_t *)code; + uint8_t peekb; #define chkbyte(x,m,v) (SafeReadMemory(x, &peekb, 1) && ((peekb & m) == v)) @@ -1364,11 +1363,11 @@ static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack, DWORD // //========================================================================== -static void DumpBytes (HANDLE file, BYTE *address) +static void DumpBytes (HANDLE file, uint8_t *address) { char line[68*3], *line_p = line; DWORD len; - BYTE peek; + uint8_t peek; for (int i = 0; i < 16*3; ++i) { @@ -2157,7 +2156,7 @@ struct BinStreamInfo static DWORD CALLBACK StreamEditBinary (DWORD_PTR cookie, LPBYTE buffer, LONG cb, LONG *pcb) { BinStreamInfo *info = (BinStreamInfo *)cookie; - BYTE buf16[16]; + uint8_t buf16[16]; DWORD read, i; char *buff_p = (char *)buffer; char *buff_end = (char *)buffer + cb; @@ -2207,7 +2206,7 @@ repeat: buff_p += mysnprintf (buff_p, buff_end - buff_p, "\\cf3 "); for (i = 0; i < read; ++i) { - BYTE code = buf16[i]; + uint8_t code = buf16[i]; if (code < 0x20 || code > 0x7f) code = 0xB7; if (code == '\\' || code == '{' || code == '}') *buff_p++ = '\\'; *buff_p++ = code; @@ -3351,3 +3350,111 @@ void DisplayCrashLog () } CloseTarFiles (); } + +///////////////////////////////////////////////////////////////////////////// + +namespace +{ + bool __declspec(thread) DrawerExceptionSetJumpResult; + CONTEXT __declspec(thread) DrawerExceptionSetJumpContext; + PVOID __declspec(thread) DrawerExceptionHandlerHandle; + char __declspec(thread) *DrawerExceptionReason; + bool __declspec(thread) DrawerExceptionFatal; + + LONG WINAPI DrawerExceptionHandler(_EXCEPTION_POINTERS *exceptionInfo) + { + *exceptionInfo->ContextRecord = DrawerExceptionSetJumpContext; + + DrawerExceptionFatal = false; + switch (exceptionInfo->ExceptionRecord->ExceptionCode) + { + default: DrawerExceptionReason = "Unknown exception code"; break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: DrawerExceptionReason = "Array bounds exceeded"; break; + case EXCEPTION_BREAKPOINT: DrawerExceptionReason = "Breakpoint"; break; + case EXCEPTION_DATATYPE_MISALIGNMENT: DrawerExceptionReason = "Datatype misalignment"; break; + case EXCEPTION_FLT_DENORMAL_OPERAND: DrawerExceptionReason = "Float denormal operand"; break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: DrawerExceptionReason = "Float divide by zero"; break; + case EXCEPTION_FLT_INEXACT_RESULT: DrawerExceptionReason = "Float inexact result"; break; + case EXCEPTION_FLT_INVALID_OPERATION: DrawerExceptionReason = "Float invalid operation"; break; + case EXCEPTION_FLT_OVERFLOW: DrawerExceptionReason = "Float overflow"; break; + case EXCEPTION_FLT_STACK_CHECK: DrawerExceptionReason = "Float stack check"; break; + case EXCEPTION_FLT_UNDERFLOW: DrawerExceptionReason = "Float underflow"; break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: DrawerExceptionReason = "Int divide by zero"; break; + case EXCEPTION_INT_OVERFLOW: DrawerExceptionReason = "Int overflow"; break; + case EXCEPTION_INVALID_DISPOSITION: DrawerExceptionReason = "Invalid disposition"; break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: DrawerExceptionReason = "Noncontinuable exception"; break; + case EXCEPTION_PRIV_INSTRUCTION: DrawerExceptionReason = "Priv instruction"; break; + case EXCEPTION_SINGLE_STEP: DrawerExceptionReason = "Single step"; break; + case EXCEPTION_STACK_OVERFLOW: DrawerExceptionReason = "Stack overflow"; break; + + case EXCEPTION_ILLEGAL_INSTRUCTION: + DrawerExceptionReason = "Illegal instruction"; + DrawerExceptionFatal = true; + break; + + case EXCEPTION_ACCESS_VIOLATION: + if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 0) + { + DrawerExceptionReason = "Read access violation"; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) + { + DrawerExceptionReason = "Write access violation"; + DrawerExceptionFatal = true; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 8) + { + DrawerExceptionReason = "User-mode data execution prevention (DEP) violation"; + DrawerExceptionFatal = true; + } + else + { + DrawerExceptionReason = "Unknown access violation"; + DrawerExceptionFatal = true; + } + break; + + case EXCEPTION_IN_PAGE_ERROR: + if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 0) + { + DrawerExceptionReason = "In page read error"; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) + { + DrawerExceptionReason = "In page write error"; + DrawerExceptionFatal = true; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 8) + { + DrawerExceptionReason = "In page user-mode data execution prevention (DEP) error"; + DrawerExceptionFatal = true; + } + else + { + DrawerExceptionReason = "Unknown in page read error"; + DrawerExceptionFatal = true; + } + break; + } + + return EXCEPTION_CONTINUE_EXECUTION; + } +} + +void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data, const char *reason, bool fatal)) +{ + DrawerExceptionSetJumpResult = false; + RtlCaptureContext(&DrawerExceptionSetJumpContext); + if (DrawerExceptionSetJumpResult) + { + RemoveVectoredExceptionHandler(DrawerExceptionHandlerHandle); + catchBlock(data, DrawerExceptionReason, DrawerExceptionFatal); + } + else + { + DrawerExceptionSetJumpResult = true; + DrawerExceptionHandlerHandle = AddVectoredExceptionHandler(1, DrawerExceptionHandler); + tryBlock(data); + RemoveVectoredExceptionHandler(DrawerExceptionHandlerHandle); + } +} diff --git a/src/win32/i_dijoy.cpp b/src/win32/i_dijoy.cpp index 7fdd907eee..b48efa983f 100644 --- a/src/win32/i_dijoy.cpp +++ b/src/win32/i_dijoy.cpp @@ -11,7 +11,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "i_input.h" #include "i_system.h" #include "d_event.h" @@ -168,7 +167,7 @@ protected: float DeadZone, DefaultDeadZone; float Multiplier, DefaultMultiplier; EJoyAxis GameAxis, DefaultGameAxis; - BYTE ButtonValue; + uint8_t ButtonValue; }; struct ButtonInfo { @@ -176,7 +175,7 @@ protected: GUID Guid; DWORD Type; DWORD Ofs; - BYTE Value; + uint8_t Value; }; LPDIRECTINPUTDEVICE8 Device; @@ -259,7 +258,7 @@ CUSTOM_CVAR(Bool, joy_dinput, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCA // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static const BYTE POVButtons[9] = { 0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09, 0x00 }; +static const uint8_t POVButtons[9] = { 0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09, 0x00 }; //("dc12a687-737f-11cf-884d-00aa004b2e24") static const IID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf, @@ -383,7 +382,7 @@ bool FDInputJoystick::GetDevice() void FDInputJoystick::ProcessInput() { HRESULT hr; - BYTE *state; + uint8_t *state; unsigned i; event_t ev; @@ -402,7 +401,7 @@ void FDInputJoystick::ProcessInput() return; } - state = (BYTE *)alloca(DataFormat.dwDataSize); + state = (uint8_t *)alloca(DataFormat.dwDataSize); hr = Device->GetDeviceState(DataFormat.dwDataSize, state); if (FAILED(hr)) return; @@ -424,7 +423,7 @@ void FDInputJoystick::ProcessInput() AxisInfo *info = &Axes[i]; LONG value = *(LONG *)(state + info->Ofs); double axisval; - BYTE buttonstate = 0; + uint8_t buttonstate = 0; // Scale to [-1.0, 1.0] axisval = (value - info->Min) * 2.0 / (info->Max - info->Min) - 1.0; @@ -450,7 +449,7 @@ void FDInputJoystick::ProcessInput() for (i = 0; i < Buttons.Size(); ++i) { ButtonInfo *info = &Buttons[i]; - BYTE newstate = *(BYTE *)(state + info->Ofs) & 0x80; + uint8_t newstate = *(uint8_t *)(state + info->Ofs) & 0x80; if (newstate != info->Value) { info->Value = newstate; @@ -696,7 +695,7 @@ HRESULT FDInputJoystick::SetDataFormat() objects[numobjs + i].dwOfs = Buttons[i].Ofs = nextofs; objects[numobjs + i].dwType = Buttons[i].Type; objects[numobjs + i].dwFlags = 0; - nextofs += sizeof(BYTE); + nextofs += sizeof(uint8_t); } numobjs += i; diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index 8e1f623067..9b05775feb 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -77,7 +77,6 @@ #endif -#define USE_WINDOWS_DWORD #include "c_dispatch.h" #include "doomtype.h" #include "doomdef.h" @@ -400,7 +399,7 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (!MyGetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) && size != 0) { - BYTE *buffer = (BYTE *)alloca(size); + uint8_t *buffer = (uint8_t *)alloca(size); if (MyGetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)) == size) { int code = GET_RAWINPUT_CODE_WPARAM(wParam); diff --git a/src/win32/i_input.h b/src/win32/i_input.h index f941d9e8e0..500e27bb79 100644 --- a/src/win32/i_input.h +++ b/src/win32/i_input.h @@ -37,13 +37,8 @@ #include "doomtype.h" #include "doomdef.h" -void I_SetMouseCapture(); -void I_ReleaseMouseCapture(); - bool I_InitInput (void *hwnd); void I_ShutdownInput (); -void I_PutInClipboard (const char *str); -FString I_GetFromClipboard (bool windows_has_no_selection_clipboard); void I_GetEvent(); @@ -56,7 +51,7 @@ enum }; -#ifdef USE_WINDOWS_DWORD +#ifdef _WIN32 #include "m_joy.h" // Don't make these definitions available to the main body of the source code. @@ -101,7 +96,7 @@ public: void AllKeysUp(); protected: - BYTE KeyStates[256/8]; + uint8_t KeyStates[256/8]; int CheckKey(int keynum) const { diff --git a/src/win32/i_keyboard.cpp b/src/win32/i_keyboard.cpp index e62942e8d3..9443b0a869 100644 --- a/src/win32/i_keyboard.cpp +++ b/src/win32/i_keyboard.cpp @@ -6,7 +6,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "i_input.h" #include "i_system.h" #include "d_event.h" @@ -70,7 +69,7 @@ extern bool GUICapture; // PRIVATE DATA DEFINITIONS ------------------------------------------------ // Convert DIK_* code to ASCII using Qwerty keymap -static const BYTE Convert[256] = +static const uint8_t Convert[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8, 9, // 0 @@ -133,8 +132,8 @@ FKeyboard::~FKeyboard() bool FKeyboard::CheckAndSetKey(int keynum, INTBOOL down) { - BYTE *statebyte = &KeyStates[keynum >> 3]; - BYTE mask = 1 << (keynum & 7); + uint8_t *statebyte = &KeyStates[keynum >> 3]; + uint8_t mask = 1 << (keynum & 7); if (down) { if (*statebyte & mask) @@ -172,7 +171,7 @@ void FKeyboard::AllKeysUp() { if (KeyStates[i] != 0) { - BYTE states = KeyStates[i]; + uint8_t states = KeyStates[i]; int j = 0; KeyStates[i] = 0; do @@ -469,7 +468,7 @@ bool FRawKeyboard::ProcessRawInput(RAWINPUT *raw, int code) // useful key from the message. if (raw->data.keyboard.VKey >= VK_BROWSER_BACK && raw->data.keyboard.VKey <= VK_LAUNCH_APP2) { - static const BYTE MediaKeys[VK_LAUNCH_APP2 - VK_BROWSER_BACK + 1] = + static const uint8_t MediaKeys[VK_LAUNCH_APP2 - VK_BROWSER_BACK + 1] = { DIK_WEBBACK, DIK_WEBFORWARD, DIK_WEBREFRESH, DIK_WEBSTOP, DIK_WEBSEARCH, DIK_WEBFAVORITES, DIK_WEBHOME, diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index 14c4791c60..99706c9a96 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -61,7 +61,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "doomerrors.h" #include "hardware.h" @@ -109,7 +108,7 @@ LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); void CreateCrashLog (char *custominfo, DWORD customsize, HWND richedit); void DisplayCrashLog (); -extern BYTE *ST_Util_BitsForBitmap (BITMAPINFO *bitmap_info); +extern uint8_t *ST_Util_BitsForBitmap (BITMAPINFO *bitmap_info); void I_FlushBufferedConsoleStuff(); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -1113,10 +1112,10 @@ void DoomSpecificInfo (char *buffer, size_t bufflen) } else { - buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nviewx = %f", ViewPos.X); - buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewy = %f", ViewPos.Y); - buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewz = %f", ViewPos.Z); - buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewangle = %f", ViewAngle); + buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nviewx = %f", r_viewpoint.Pos.X); + buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewy = %f", r_viewpoint.Pos.Y); + buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewz = %f", r_viewpoint.Pos.Z); + buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewangle = %f", r_viewpoint.Angles.Yaw); } } *buffer++ = '\r'; diff --git a/src/win32/i_mouse.cpp b/src/win32/i_mouse.cpp index 0af52cb1ad..ff3b638ebd 100644 --- a/src/win32/i_mouse.cpp +++ b/src/win32/i_mouse.cpp @@ -7,7 +7,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "i_input.h" #include "i_system.h" #include "d_event.h" diff --git a/src/win32/i_rawps2.cpp b/src/win32/i_rawps2.cpp index 29f477a14b..fc43ff6026 100644 --- a/src/win32/i_rawps2.cpp +++ b/src/win32/i_rawps2.cpp @@ -6,7 +6,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "i_input.h" #include "i_system.h" #include "d_event.h" @@ -101,7 +100,7 @@ protected: float DeadZone; float Multiplier; EJoyAxis GameAxis; - BYTE ButtonValue; + uint8_t ButtonValue; }; struct DefaultAxisConfig { @@ -167,22 +166,22 @@ protected: struct PS2Descriptor { const char *AdapterName; - BYTE PacketSize; - SBYTE ControllerNumber; - SBYTE ControllerStatus; - BYTE LeftX; - BYTE LeftY; - BYTE RightX; - BYTE RightY; - SBYTE DPadHat; - BYTE DPadButtonsNibble:1; - SBYTE DPadButtons:7; // up, right, down, left - BYTE ButtonSet1:7; // triangle, circle, cross, square - BYTE ButtonSet1Nibble:1; - BYTE ButtonSet2:7; // L2, R2, L1, R1 - BYTE ButtonSet2Nibble:1; - BYTE ButtonSet3:7; // select, start, lthumb, rthumb - BYTE ButtonSet3Nibble:1; + uint8_t PacketSize; + int8_t ControllerNumber; + int8_t ControllerStatus; + uint8_t LeftX; + uint8_t LeftY; + uint8_t RightX; + uint8_t RightY; + int8_t DPadHat; + uint8_t DPadButtonsNibble:1; + int8_t DPadButtons:7; // up, right, down, left + uint8_t ButtonSet1:7; // triangle, circle, cross, square + uint8_t ButtonSet1Nibble:1; + uint8_t ButtonSet2:7; // L2, R2, L1, R1 + uint8_t ButtonSet2Nibble:1; + uint8_t ButtonSet3:7; // select, start, lthumb, rthumb + uint8_t ButtonSet3Nibble:1; }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -229,7 +228,7 @@ static const int ButtonKeys[16] = KEY_PAD_RTHUMB }; -static const BYTE HatButtons[16] = +static const uint8_t HatButtons[16] = { 1, 1+2, 2, 2+4, 4, 4+8, 8, 8+1, 0, 0, 0, 0, 0, 0, 0, 0 @@ -390,9 +389,9 @@ bool FRawPS2Controller::ProcessInput(RAWHID *raw, int code) // w32api has an incompatible definition of bRawData. // (But the version that comes with MinGW64 is fine.) #if defined(__GNUC__) && !defined(__MINGW64_VERSION_MAJOR) - BYTE *rawdata = &raw->bRawData; + uint8_t *rawdata = &raw->bRawData; #else - BYTE *rawdata = raw->bRawData; + uint8_t *rawdata = raw->bRawData; #endif const PS2Descriptor *desc = &Descriptors[Type]; bool digital; @@ -515,7 +514,7 @@ bool FRawPS2Controller::ProcessInput(RAWHID *raw, int code) void FRawPS2Controller::ProcessThumbstick(int value1, AxisInfo *axis1, int value2, AxisInfo *axis2, int base) { - BYTE buttonstate; + uint8_t buttonstate; double axisval1, axisval2; axisval1 = value1 * (2.0 / 255) - 1.0; diff --git a/src/win32/i_specialpaths.cpp b/src/win32/i_specialpaths.cpp index cddeacde6e..58c87a8ca8 100644 --- a/src/win32/i_specialpaths.cpp +++ b/src/win32/i_specialpaths.cpp @@ -37,7 +37,6 @@ #include #include #include -#define USE_WINDOWS_DWORD #include "cmdlib.h" #include "m_misc.h" diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index f07fb8f1d3..003e956628 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -63,7 +63,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "hardware.h" #include "doomerrors.h" #include @@ -163,7 +162,7 @@ UINT TimerPeriod; UINT TimerEventID; UINT MillisecondsPerTic; HANDLE NewTicArrived; -uint32 LanguageIDs[4]; +uint32_t LanguageIDs[4]; int (*I_GetTime) (bool saveMS); int (*I_WaitForTic) (int); @@ -487,7 +486,7 @@ static void CALLBACK TimerTicked(UINT id, UINT msg, DWORD_PTR user, DWORD_PTR dw // //========================================================================== -double I_GetTimeFrac(uint32 *ms) +double I_GetTimeFrac(uint32_t *ms) { DWORD now = timeGetTime(); if (ms != NULL) @@ -680,11 +679,11 @@ void SetLanguageIDs() } else { - DWORD lang = 0; + uint32_t lang = 0; - ((BYTE *)&lang)[0] = (language)[0]; - ((BYTE *)&lang)[1] = (language)[1]; - ((BYTE *)&lang)[2] = (language)[2]; + ((uint8_t *)&lang)[0] = (language)[0]; + ((uint8_t *)&lang)[1] = (language)[1]; + ((uint8_t *)&lang)[2] = (language)[2]; LanguageIDs[0] = lang; LanguageIDs[1] = lang; LanguageIDs[2] = lang; @@ -910,7 +909,7 @@ void ToEditControl(HWND edit, const char *buf, wchar_t *wbuf, int bpos) }; for (int i = 0; i <= bpos; ++i) { - wchar_t code = (BYTE)buf[i]; + wchar_t code = (uint8_t)buf[i]; if (code >= 0x1D && code <= 0x1F) { // The bar characters, most commonly used to indicate map changes code = 0x2550; // Box Drawings Double Horizontal @@ -985,7 +984,7 @@ static void DoPrintStr(const char *cp, HWND edit, HANDLE StdOut) } else { - const BYTE *color_id = (const BYTE *)cp + 1; + const uint8_t *color_id = (const uint8_t *)cp + 1; EColorRange range = V_ParseFontColor(color_id, CR_UNTRANSLATED, CR_YELLOW); cp = (const char *)color_id; @@ -999,7 +998,7 @@ static void DoPrintStr(const char *cp, HWND edit, HANDLE StdOut) // eight basic colors, and each comes in a dark and a bright // variety. float h, s, v, r, g, b; - WORD attrib = 0; + int attrib = 0; RGBtoHSV(color.r / 255.f, color.g / 255.f, color.b / 255.f, &h, &s, &v); if (s != 0) @@ -1016,7 +1015,7 @@ static void DoPrintStr(const char *cp, HWND edit, HANDLE StdOut) else if (v < 0.90) attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; else attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; } - SetConsoleTextAttribute(StdOut, attrib); + SetConsoleTextAttribute(StdOut, (WORD)attrib); } if (edit != NULL) { @@ -1376,7 +1375,7 @@ static HCURSOR CreateCompatibleCursor(FTexture *cursorpic) Rectangle(xor_mask_dc, 0, 0, 32, 32); FBitmap bmp; - const BYTE *pixels; + const uint8_t *pixels; bmp.Create(picwidth, picheight); cursorpic->CopyTrueColorPixels(&bmp, 0, 0); @@ -1387,7 +1386,7 @@ static HCURSOR CreateCompatibleCursor(FTexture *cursorpic) { for (int x = 0; x < picwidth; ++x) { - const BYTE *bgra = &pixels[x*4 + y*bmp.GetPitch()]; + const uint8_t *bgra = &pixels[x*4 + y*bmp.GetPitch()]; if (bgra[3] != 0) { SetPixelV(and_mask_dc, x, y, RGB(0,0,0)); @@ -1463,7 +1462,7 @@ static HCURSOR CreateAlphaCursor(FTexture *cursorpic) // a negative pitch so that CopyTrueColorPixels will use GDI's orientation. if (scale == 1) { - FBitmap bmp((BYTE *)bits + 31 * 32 * 4, -32 * 4, 32, 32); + FBitmap bmp((uint8_t *)bits + 31 * 32 * 4, -32 * 4, 32, 32); cursorpic->CopyTrueColorPixels(&bmp, 0, 0); } else @@ -1471,7 +1470,7 @@ static HCURSOR CreateAlphaCursor(FTexture *cursorpic) TArray unscaled; unscaled.Resize(32 * 32); for (int i = 0; i < 32 * 32; i++) unscaled[i] = 0; - FBitmap bmp((BYTE *)&unscaled[0] + 31 * 32 * 4, -32 * 4, 32, 32); + FBitmap bmp((uint8_t *)&unscaled[0] + 31 * 32 * 4, -32 * 4, 32, 32); cursorpic->CopyTrueColorPixels(&bmp, 0, 0); uint32_t *scaled = (uint32_t*)bits; for (int y = 0; y < 32 * scale; y++) @@ -1766,7 +1765,7 @@ unsigned int I_MakeRNGSeed() { return (unsigned int)time(NULL); } - if (!CryptGenRandom(prov, sizeof(seed), (BYTE *)&seed)) + if (!CryptGenRandom(prov, sizeof(seed), (uint8_t *)&seed)) { seed = (unsigned int)time(NULL); } @@ -1834,9 +1833,9 @@ int VS14Stat(const char *path, struct _stat64i32 *buffer) buffer->st_uid = 0; buffer->st_gid = 0; buffer->st_size = data.nFileSizeLow; - buffer->st_atime = (*(QWORD*)&data.ftLastAccessTime) / 10000000 - 11644473600LL; - buffer->st_mtime = (*(QWORD*)&data.ftLastWriteTime) / 10000000 - 11644473600LL; - buffer->st_ctime = (*(QWORD*)&data.ftCreationTime) / 10000000 - 11644473600LL; + buffer->st_atime = (*(uint64_t*)&data.ftLastAccessTime) / 10000000 - 11644473600LL; + buffer->st_mtime = (*(uint64_t*)&data.ftLastWriteTime) / 10000000 - 11644473600LL; + buffer->st_ctime = (*(uint64_t*)&data.ftCreationTime) / 10000000 - 11644473600LL; return 0; } #endif diff --git a/src/win32/i_system.h b/src/win32/i_system.h index e7437beb89..5959b61120 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -36,7 +36,7 @@ enum LANGIDX_SysPreferred, LANGIDX_SysDefault }; -extern uint32 LanguageIDs[4]; +extern uint32_t LanguageIDs[4]; extern void SetLanguageIDs (); // [RH] Detects the OS the game is running under. @@ -66,7 +66,7 @@ extern int (*I_WaitForTic) (int); // tic will never arrive (unless it's the current one). extern void (*I_FreezeTime) (bool frozen); -double I_GetTimeFrac (uint32 *ms); +double I_GetTimeFrac (uint32_t *ms); // Return a seed value for the RNG. unsigned int I_MakeRNGSeed(); @@ -175,10 +175,10 @@ FString I_GetLongPathName(FString shortpath); struct findstate_t { - DWORD Attribs; - DWORD Times[3*2]; - DWORD Size[2]; - DWORD Reserved[2]; + uint32_t Attribs; + uint32_t Times[3*2]; + uint32_t Size[2]; + uint32_t Reserved[2]; char Name[MAX_PATH]; char AltName[14]; }; diff --git a/src/win32/i_xinput.cpp b/src/win32/i_xinput.cpp index 7286a9d8f2..6756cd6386 100644 --- a/src/win32/i_xinput.cpp +++ b/src/win32/i_xinput.cpp @@ -9,7 +9,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "i_input.h" #include "i_system.h" #include "d_event.h" @@ -91,7 +90,7 @@ protected: float DeadZone; float Multiplier; EJoyAxis GameAxis; - BYTE ButtonValue; + uint8_t ButtonValue; }; struct DefaultAxisConfig { @@ -287,7 +286,7 @@ void FXInputController::ProcessInput() void FXInputController::ProcessThumbstick(int value1, AxisInfo *axis1, int value2, AxisInfo *axis2, int base) { - BYTE buttonstate; + uint8_t buttonstate; double axisval1, axisval2; axisval1 = (value1 - SHRT_MIN) * 2.0 / 65536 - 1.0; @@ -314,7 +313,7 @@ void FXInputController::ProcessThumbstick(int value1, AxisInfo *axis1, void FXInputController::ProcessTrigger(int value, AxisInfo *axis, int base) { - BYTE buttonstate; + uint8_t buttonstate; double axisval; axisval = Joy_RemoveDeadZone(value / 256.0, axis->DeadZone, &buttonstate); diff --git a/src/win32/optwin32.h b/src/win32/optwin32.h index 0f1458c57e..c6cced98b7 100644 --- a/src/win32/optwin32.h +++ b/src/win32/optwin32.h @@ -6,7 +6,6 @@ #define WIN32_LEAN_AND_MEAN #include #include -#define USE_WINDOWS_DWORD #include "i_module.h" diff --git a/src/win32/st_start.cpp b/src/win32/st_start.cpp index 4c322b90e9..c1556f0023 100644 --- a/src/win32/st_start.cpp +++ b/src/win32/st_start.cpp @@ -41,7 +41,6 @@ #include #include "resource.h" -#define USE_WINDOWS_DWORD #include "st_start.h" #include "resource.h" #include "templates.h" @@ -161,8 +160,8 @@ public: void NetDone(); // Hexen's notch graphics, converted to chunky pixels. - BYTE * NotchBits; - BYTE * NetNotchBits; + uint8_t * NotchBits; + uint8_t * NetNotchBits; }; class FStrifeStartupScreen : public FGraphicalStartupScreen @@ -175,7 +174,7 @@ public: protected: void DrawStuff(int old_laser, int new_laser); - BYTE *StartupPics[4+2+1]; + uint8_t *StartupPics[4+2+1]; }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -187,19 +186,19 @@ int LayoutNetStartPane (HWND pane, int w); bool ST_Util_CreateStartupWindow (); void ST_Util_SizeWindowForBitmap (int scale); BITMAPINFO *ST_Util_CreateBitmap (int width, int height, int color_bits); -BYTE *ST_Util_BitsForBitmap (BITMAPINFO *bitmap_info); +uint8_t *ST_Util_BitsForBitmap (BITMAPINFO *bitmap_info); void ST_Util_FreeBitmap (BITMAPINFO *bitmap_info); void ST_Util_BitmapColorsFromPlaypal (BITMAPINFO *bitmap_info); -void ST_Util_PlanarToChunky4 (BYTE *dest, const BYTE *src, int width, int height); -void ST_Util_DrawBlock (BITMAPINFO *bitmap_info, const BYTE *src, int x, int y, int bytewidth, int height); -void ST_Util_ClearBlock (BITMAPINFO *bitmap_info, BYTE fill, int x, int y, int bytewidth, int height); +void ST_Util_PlanarToChunky4 (uint8_t *dest, const uint8_t *src, int width, int height); +void ST_Util_DrawBlock (BITMAPINFO *bitmap_info, const uint8_t *src, int x, int y, int bytewidth, int height); +void ST_Util_ClearBlock (BITMAPINFO *bitmap_info, uint8_t fill, int x, int y, int bytewidth, int height); void ST_Util_InvalidateRect (HWND hwnd, BITMAPINFO *bitmap_info, int left, int top, int right, int bottom); -BYTE *ST_Util_LoadFont (const char *filename); -void ST_Util_FreeFont (BYTE *font); -BITMAPINFO *ST_Util_AllocTextBitmap (const BYTE *font); -void ST_Util_DrawTextScreen (BITMAPINFO *bitmap_info, const BYTE *text_screen, const BYTE *font); -void ST_Util_UpdateTextBlink (BITMAPINFO *bitmap_info, const BYTE *text_screen, const BYTE *font, bool blink_on); -void ST_Util_DrawChar (BITMAPINFO *screen, const BYTE *font, int x, int y, BYTE charnum, BYTE attrib); +uint8_t *ST_Util_LoadFont (const char *filename); +void ST_Util_FreeFont (uint8_t *font); +BITMAPINFO *ST_Util_AllocTextBitmap (const uint8_t *font); +void ST_Util_DrawTextScreen (BITMAPINFO *bitmap_info, const uint8_t *text_screen, const uint8_t *font); +void ST_Util_UpdateTextBlink (BITMAPINFO *bitmap_info, const uint8_t *text_screen, const uint8_t *font, bool blink_on); +void ST_Util_DrawChar (BITMAPINFO *screen, const uint8_t *font, int x, int y, uint8_t charnum, uint8_t attrib); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -650,16 +649,16 @@ FHexenStartupScreen::FHexenStartupScreen(int max_progress, HRESULT &hr) return; } - NetNotchBits = new BYTE[ST_NETNOTCH_WIDTH / 2 * ST_NETNOTCH_HEIGHT]; + NetNotchBits = new uint8_t[ST_NETNOTCH_WIDTH / 2 * ST_NETNOTCH_HEIGHT]; Wads.ReadLump (netnotch_lump, NetNotchBits); - NotchBits = new BYTE[ST_NOTCH_WIDTH / 2 * ST_NOTCH_HEIGHT]; + NotchBits = new uint8_t[ST_NOTCH_WIDTH / 2 * ST_NOTCH_HEIGHT]; Wads.ReadLump (notch_lump, NotchBits); - BYTE startup_screen[153648]; + uint8_t startup_screen[153648]; union { RGBQUAD color; - DWORD quad; + uint32_t quad; } c; Wads.ReadLump (startup_lump, startup_screen); @@ -805,8 +804,8 @@ FHereticStartupScreen::FHereticStartupScreen(int max_progress, HRESULT &hr) : FGraphicalStartupScreen(max_progress) { int loading_lump = Wads.CheckNumForName ("LOADING"); - BYTE loading_screen[4000]; - BYTE *font; + uint8_t loading_screen[4000]; + uint8_t *font; hr = E_FAIL; if (loading_lump < 0 || Wads.LumpLength (loading_lump) != 4000 || !ST_Util_CreateStartupWindow()) @@ -886,7 +885,7 @@ void FHereticStartupScreen::Progress() void FHereticStartupScreen::LoadingStatus(const char *message, int colors) { - BYTE *font = ST_Util_LoadFont (TEXT_FONT_NAME); + uint8_t *font = ST_Util_LoadFont (TEXT_FONT_NAME); if (font != NULL) { int x; @@ -912,7 +911,7 @@ void FHereticStartupScreen::LoadingStatus(const char *message, int colors) void FHereticStartupScreen::AppendStatusLine(const char *status) { - BYTE *font = ST_Util_LoadFont (TEXT_FONT_NAME); + uint8_t *font = ST_Util_LoadFont (TEXT_FONT_NAME); if (font != NULL) { int x; @@ -979,7 +978,7 @@ FStrifeStartupScreen::FStrifeStartupScreen(int max_progress, HRESULT &hr) if (lumpnum >= 0 && (lumplen = Wads.LumpLength (lumpnum)) == StrifeStartupPicSizes[i]) { FWadLump lumpr = Wads.OpenLumpNum (lumpnum); - StartupPics[i] = new BYTE[lumplen]; + StartupPics[i] = new uint8_t[lumplen]; lumpr.Read (StartupPics[i], lumplen); } } @@ -1096,8 +1095,8 @@ void ST_Endoom() int endoom_lump = Wads.CheckNumForFullName (gameinfo.Endoom, true); - BYTE endoom_screen[4000]; - BYTE *font; + uint8_t endoom_screen[4000]; + uint8_t *font; MSG mess; BOOL bRet; bool blinking = false, blinkstate = false; @@ -1273,10 +1272,10 @@ void ST_Util_SizeWindowForBitmap (int scale) // //========================================================================== -void ST_Util_PlanarToChunky4 (BYTE *dest, const BYTE *src, int width, int height) +void ST_Util_PlanarToChunky4 (uint8_t *dest, const uint8_t *src, int width, int height) { int y, x; - const BYTE *src1, *src2, *src3, *src4; + const uint8_t *src1, *src2, *src3, *src4; size_t plane_size = width / 8 * height; src1 = src; @@ -1315,7 +1314,7 @@ void ST_Util_PlanarToChunky4 (BYTE *dest, const BYTE *src, int width, int height // //========================================================================== -void ST_Util_DrawBlock (BITMAPINFO *bitmap_info, const BYTE *src, int x, int y, int bytewidth, int height) +void ST_Util_DrawBlock (BITMAPINFO *bitmap_info, const uint8_t *src, int x, int y, int bytewidth, int height) { if (src == NULL) { @@ -1324,7 +1323,7 @@ void ST_Util_DrawBlock (BITMAPINFO *bitmap_info, const BYTE *src, int x, int y, int pitchshift = int(bitmap_info->bmiHeader.biBitCount == 4); int destpitch = bitmap_info->bmiHeader.biWidth >> pitchshift; - BYTE *dest = ST_Util_BitsForBitmap(bitmap_info) + (x >> pitchshift) + y * destpitch; + uint8_t *dest = ST_Util_BitsForBitmap(bitmap_info) + (x >> pitchshift) + y * destpitch; ST_Util_InvalidateRect (StartupScreen, bitmap_info, x, y, x + (bytewidth << pitchshift), y + height); @@ -1332,8 +1331,8 @@ void ST_Util_DrawBlock (BITMAPINFO *bitmap_info, const BYTE *src, int x, int y, { // progress notches for (; height > 0; --height) { - ((DWORD *)dest)[0] = ((const DWORD *)src)[0]; - ((DWORD *)dest)[1] = ((const DWORD *)src)[1]; + ((uint32_t *)dest)[0] = ((const uint32_t *)src)[0]; + ((uint32_t *)dest)[1] = ((const uint32_t *)src)[1]; dest += destpitch; src += 8; } @@ -1364,11 +1363,11 @@ void ST_Util_DrawBlock (BITMAPINFO *bitmap_info, const BYTE *src, int x, int y, // //========================================================================== -void ST_Util_ClearBlock (BITMAPINFO *bitmap_info, BYTE fill, int x, int y, int bytewidth, int height) +void ST_Util_ClearBlock (BITMAPINFO *bitmap_info, uint8_t fill, int x, int y, int bytewidth, int height) { int pitchshift = int(bitmap_info->bmiHeader.biBitCount == 4); int destpitch = bitmap_info->bmiHeader.biWidth >> pitchshift; - BYTE *dest = ST_Util_BitsForBitmap(bitmap_info) + (x >> pitchshift) + y * destpitch; + uint8_t *dest = ST_Util_BitsForBitmap(bitmap_info) + (x >> pitchshift) + y * destpitch; ST_Util_InvalidateRect (StartupScreen, bitmap_info, x, y, x + (bytewidth << pitchshift), y + height); @@ -1395,7 +1394,7 @@ void ST_Util_ClearBlock (BITMAPINFO *bitmap_info, BYTE fill, int x, int y, int b BITMAPINFO *ST_Util_CreateBitmap (int width, int height, int color_bits) { - DWORD size_image = (width * height) >> int(color_bits == 4); + uint32_t size_image = (width * height) >> int(color_bits == 4); BITMAPINFO *bitmap_info = (BITMAPINFO *)M_Malloc (sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << color_bits) + size_image); @@ -1424,9 +1423,9 @@ BITMAPINFO *ST_Util_CreateBitmap (int width, int height, int color_bits) // //========================================================================== -BYTE *ST_Util_BitsForBitmap (BITMAPINFO *bitmap_info) +uint8_t *ST_Util_BitsForBitmap (BITMAPINFO *bitmap_info) { - return (BYTE *)bitmap_info + sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << bitmap_info->bmiHeader.biBitCount); + return (uint8_t *)bitmap_info + sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << bitmap_info->bmiHeader.biBitCount); } //========================================================================== @@ -1452,7 +1451,7 @@ void ST_Util_FreeBitmap (BITMAPINFO *bitmap_info) void ST_Util_BitmapColorsFromPlaypal (BITMAPINFO *bitmap_info) { - BYTE playpal[768]; + uint8_t playpal[768]; int i; { @@ -1499,10 +1498,10 @@ void ST_Util_InvalidateRect (HWND hwnd, BITMAPINFO *bitmap_info, int left, int t // //========================================================================== -BYTE *ST_Util_LoadFont (const char *filename) +uint8_t *ST_Util_LoadFont (const char *filename) { int lumpnum, lumplen, height; - BYTE *font; + uint8_t *font; lumpnum = Wads.CheckNumForFullName (filename); if (lumpnum < 0) @@ -1519,13 +1518,13 @@ BYTE *ST_Util_LoadFont (const char *filename) { // let's be reasonable here return NULL; } - font = new BYTE[lumplen + 1]; + font = new uint8_t[lumplen + 1]; font[0] = height; // Store font height in the first byte. Wads.ReadLump (lumpnum, font + 1); return font; } -void ST_Util_FreeFont (BYTE *font) +void ST_Util_FreeFont (uint8_t *font) { delete[] font; } @@ -1539,7 +1538,7 @@ void ST_Util_FreeFont (BYTE *font) // //========================================================================== -BITMAPINFO *ST_Util_AllocTextBitmap (const BYTE *font) +BITMAPINFO *ST_Util_AllocTextBitmap (const uint8_t *font) { BITMAPINFO *bitmap = ST_Util_CreateBitmap (80 * 8, 25 * font[0], 4); memcpy (bitmap->bmiColors, TextModePalette, sizeof(TextModePalette)); @@ -1555,7 +1554,7 @@ BITMAPINFO *ST_Util_AllocTextBitmap (const BYTE *font) // //========================================================================== -void ST_Util_DrawTextScreen (BITMAPINFO *bitmap_info, const BYTE *text_screen, const BYTE *font) +void ST_Util_DrawTextScreen (BITMAPINFO *bitmap_info, const uint8_t *text_screen, const uint8_t *font) { int x, y; @@ -1578,20 +1577,20 @@ void ST_Util_DrawTextScreen (BITMAPINFO *bitmap_info, const BYTE *text_screen, c // //========================================================================== -void ST_Util_DrawChar (BITMAPINFO *screen, const BYTE *font, int x, int y, BYTE charnum, BYTE attrib) +void ST_Util_DrawChar (BITMAPINFO *screen, const uint8_t *font, int x, int y, uint8_t charnum, uint8_t attrib) { - const BYTE bg_left = attrib & 0x70; - const BYTE fg = attrib & 0x0F; - const BYTE fg_left = fg << 4; - const BYTE bg = bg_left >> 4; - const BYTE color_array[4] = { (BYTE)(bg_left | bg), (BYTE)(attrib & 0x7F), (BYTE)(fg_left | bg), (BYTE)(fg_left | fg) }; - const BYTE *src = font + 1 + charnum * font[0]; + const uint8_t bg_left = attrib & 0x70; + const uint8_t fg = attrib & 0x0F; + const uint8_t fg_left = fg << 4; + const uint8_t bg = bg_left >> 4; + const uint8_t color_array[4] = { (uint8_t)(bg_left | bg), (uint8_t)(attrib & 0x7F), (uint8_t)(fg_left | bg), (uint8_t)(fg_left | fg) }; + const uint8_t *src = font + 1 + charnum * font[0]; int pitch = screen->bmiHeader.biWidth >> 1; - BYTE *dest = ST_Util_BitsForBitmap(screen) + x*4 + y * font[0] * pitch; + uint8_t *dest = ST_Util_BitsForBitmap(screen) + x*4 + y * font[0] * pitch; for (y = font[0]; y > 0; --y) { - BYTE srcbyte = *src++; + uint8_t srcbyte = *src++; // Pixels 0 and 1 dest[0] = color_array[(srcbyte >> 6) & 3]; @@ -1614,7 +1613,7 @@ void ST_Util_DrawChar (BITMAPINFO *screen, const BYTE *font, int x, int y, BYTE // //========================================================================== -void ST_Util_UpdateTextBlink (BITMAPINFO *bitmap_info, const BYTE *text_screen, const BYTE *font, bool on) +void ST_Util_UpdateTextBlink (BITMAPINFO *bitmap_info, const uint8_t *text_screen, const uint8_t *font, bool on) { int x, y; diff --git a/src/win32/win32gliface.cpp b/src/win32/win32gliface.cpp index 48de9e8e09..7d3ff00be6 100644 --- a/src/win32/win32gliface.cpp +++ b/src/win32/win32gliface.cpp @@ -5,7 +5,6 @@ #include #include "wglext.h" -#define USE_WINDOWS_DWORD #include "win32iface.h" #include "win32gliface.h" //#include "gl/gl_intern.h" @@ -25,6 +24,7 @@ #include "gl/renderer/gl_renderer.h" #include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_swframebuffer.h" extern HWND Window; extern BOOL AppActive; @@ -71,7 +71,7 @@ public: void StartModeIterator(int bits, bool fs); bool NextMode(int *width, int *height, bool *letterbox); bool GoFullscreen(bool yes); - DFrameBuffer *CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer *old); + DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old); virtual bool SetResolution(int width, int height, int bits); void DumpAdapters(); bool InitHardware(HWND Window, int multisample); @@ -422,7 +422,8 @@ bool Win32GLVideo::GoFullscreen(bool yes) // //========================================================================== -DFrameBuffer *Win32GLVideo::CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer *old) + +DFrameBuffer *Win32GLVideo::CreateFrameBuffer(int width, int height, bool bgra, bool fs, DFrameBuffer *old) { Win32GLFrameBuffer *fb; @@ -458,14 +459,18 @@ DFrameBuffer *Win32GLVideo::CreateFrameBuffer(int width, int height, bool fs, DF fb->m_Height == m_DisplayHeight && fb->m_Bits == m_DisplayBits && fb->m_RefreshHz == m_DisplayHz && - fb->m_Fullscreen == fs) + fb->m_Fullscreen == fs && + fb->m_Bgra == bgra) { return old; } //old->GetFlash(flashColor, flashAmount); delete old; } - fb = new OpenGLFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs); + if (vid_renderer == 1) + fb = new OpenGLFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs); + else + fb = new OpenGLSWFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs, bgra); return fb; } @@ -941,13 +946,14 @@ IMPLEMENT_CLASS(Win32GLFrameBuffer, true, false) // //========================================================================== -Win32GLFrameBuffer::Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) : BaseWinFB(width, height) +Win32GLFrameBuffer::Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen, bool bgra) : BaseWinFB(width, height, bgra) { m_Width = width; m_Height = height; m_Bits = bits; m_RefreshHz = refreshHz; m_Fullscreen = fullscreen; + m_Bgra = bgra; m_Lock=0; RECT r; diff --git a/src/win32/win32gliface.h b/src/win32/win32gliface.h index cd7e508d35..ca949c864f 100644 --- a/src/win32/win32gliface.h +++ b/src/win32/win32gliface.h @@ -33,7 +33,7 @@ public: Win32GLFrameBuffer() {} // Actually, hMonitor is a HMONITOR, but it's passed as a void * as there // look to be some cross-platform bits in the way. - Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen); + Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen, bool bgra); virtual ~Win32GLFrameBuffer(); @@ -75,7 +75,7 @@ protected: float m_Gamma, m_Brightness, m_Contrast; uint16_t m_origGamma[768]; bool m_supportsGamma; - bool m_Fullscreen; + bool m_Fullscreen, m_Bgra; int m_Width, m_Height, m_Bits, m_RefreshHz; int m_Lock; char m_displayDeviceNameBuffer[32/*CCHDEVICENAME*/]; // do not use windows.h constants here! diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index 03ae6ffde4..8e7768b64f 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -55,7 +55,7 @@ class Win32Video : public IVideo EDisplayType GetDisplayType () { return DISPLAY_Both; } void SetWindowedScale (float scale); - DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old); void StartModeIterator (int bits, bool fs); bool NextMode (int *width, int *height, bool *letterbox); @@ -105,7 +105,7 @@ class BaseWinFB : public DFrameBuffer { DECLARE_ABSTRACT_CLASS(BaseWinFB, DFrameBuffer) public: - BaseWinFB (int width, int height) : DFrameBuffer (width, height), Windowed (true) {} + BaseWinFB(int width, int height, bool bgra) : DFrameBuffer(width, height, bgra), Windowed(true) {} bool IsFullscreen () { return !Windowed; } virtual void Blank () = 0; diff --git a/src/win32/win32swiface.h b/src/win32/win32swiface.h index f8e332c40b..65cb4583d3 100644 --- a/src/win32/win32swiface.h +++ b/src/win32/win32swiface.h @@ -61,7 +61,7 @@ private: HRESULT AttemptRestore (); HRESULT LastHR; - BYTE GammaTable[3][256]; + uint8_t GammaTable[3][256]; PalEntry SourcePalette[256]; PALETTEENTRY PalEntries[256]; DWORD FlipFlags; @@ -102,7 +102,7 @@ class D3DFB : public BaseWinFB { DECLARE_CLASS(D3DFB, BaseWinFB) public: - D3DFB (UINT adapter, int width, int height, bool fullscreen); + D3DFB (UINT adapter, int width, int height, bool bgra, bool fullscreen); ~D3DFB (); bool IsValid (); @@ -124,7 +124,7 @@ public: bool PaintToWindow (); void SetVSync (bool vsync); void NewRefreshRate(); - void GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type); + void GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type); void ReleaseScreenshotBuffer(); void SetBlendingRect (int x1, int y1, int x2, int y2); bool Begin2D (bool copy3d); @@ -132,11 +132,11 @@ public: FNativeTexture *CreateTexture (FTexture *gametex, bool wrapping); FNativePalette *CreatePalette (FRemapTable *remap); void DrawTextureParms (FTexture *img, DrawParms &parms); - void Clear (int left, int top, int right, int bottom, int palcolor, uint32 color); + void Clear (int left, int top, int right, int bottom, int palcolor, uint32_t color); void Dim (PalEntry color, float amount, int x1, int y1, int w, int h); void FlatFill (int left, int top, int right, int bottom, FTexture *src, bool local_origin); - void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 realcolor); - void DrawPixel(int x, int y, int palcolor, uint32 rgbcolor); + void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32_t realcolor); + void DrawPixel(int x, int y, int palcolor, uint32_t rgbcolor); void FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) override; @@ -169,14 +169,14 @@ private: { struct { - BYTE Flags; - BYTE ShaderNum:4; - BYTE BlendOp:4; - BYTE SrcBlend, DestBlend; + uint8_t Flags; + uint8_t ShaderNum:4; + uint8_t BlendOp:4; + uint8_t SrcBlend, DestBlend; }; DWORD Group1; }; - BYTE Desat; + uint8_t Desat; D3DPal *Palette; IDirect3DTexture9 *Texture; int NumVerts; // Number of _unique_ vertices used by this set. @@ -296,6 +296,7 @@ private: bool NeedPalUpdate; bool NeedGammaUpdate; int FBWidth, FBHeight; + D3DFORMAT FBFormat; bool VSync; RECT BlendingRect; int In2D; @@ -303,7 +304,7 @@ private: bool SM14; bool GatheringWipeScreen; bool AALines; - BYTE BlockNum; + uint8_t BlockNum; D3DPal *Palettes; D3DTex *Textures; Atlas *Atlases; diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index ea6e237d68..f734ac7891 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -55,7 +55,6 @@ #include #include -#define USE_WINDOWS_DWORD #include "doomtype.h" #include "c_dispatch.h" @@ -69,7 +68,7 @@ #include "m_argv.h" #include "r_defs.h" #include "v_text.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" #include "version.h" #include "win32iface.h" @@ -636,7 +635,7 @@ bool Win32Video::NextMode (int *width, int *height, bool *letterbox) return false; } -DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) +DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool bgra, bool fullscreen, DFrameBuffer *old) { static int retry = 0; static int owidth, oheight; @@ -657,7 +656,8 @@ DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscr BaseWinFB *fb = static_cast (old); if (fb->Width == width && fb->Height == height && - fb->Windowed == !fullscreen) + fb->Windowed == !fullscreen && + fb->Bgra == bgra) { return old; } @@ -674,12 +674,13 @@ DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscr if (D3D != NULL) { - fb = new D3DFB (m_Adapter, width, height, fullscreen); + fb = new D3DFB (m_Adapter, width, height, bgra, fullscreen); } else { fb = new DDrawFB (width, height, fullscreen); } + LOG1 ("New fb created @ %p\n", fb); // If we could not create the framebuffer, try again with slightly @@ -738,7 +739,7 @@ DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscr } ++retry; - fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); + fb = static_cast(CreateFrameBuffer (width, height, bgra, fullscreen, NULL)); } retry = 0; diff --git a/src/x86.cpp b/src/x86.cpp index 17c946ac0f..dff76c9576 100644 --- a/src/x86.cpp +++ b/src/x86.cpp @@ -2,10 +2,7 @@ #include "doomdef.h" #include "x86.h" -extern "C" -{ - CPUInfo CPU; -} +CPUInfo CPU; #if !defined(__amd64__) && !defined(__i386__) && !defined(_M_IX86) && !defined(_M_X64) void CheckCPUID(CPUInfo *cpu) @@ -285,14 +282,14 @@ void DoBlending_SSE2(const PalEntry *from, PalEntry *to, int count, int r, int g unaligned = ((size_t)from | (size_t)to) & 0xF; #if defined(__amd64__) || defined(_M_X64) - long long color; + int64_t color; blending256 = _mm_set_epi64x(0x10001000100ll, 0x10001000100ll); - color = ((long long)r << 32) | (g << 16) | b; + color = ((int64_t)r << 32) | (g << 16) | b; blendcolor = _mm_set_epi64x(color, color); - color = ((long long)a << 32) | (a << 16) | a; + color = ((int64_t)a << 32) | (a << 16) | a; blendalpha = _mm_set_epi64x(color, color); #else int color; diff --git a/src/x86.h b/src/x86.h index cf9ae0e01d..1839e2c352 100644 --- a/src/x86.h +++ b/src/x86.h @@ -8,99 +8,99 @@ struct CPUInfo // 92 bytes union { char VendorID[16]; - uint32 dwVendorID[4]; + uint32_t dwVendorID[4]; }; union { char CPUString[48]; - uint32 dwCPUString[12]; + uint32_t dwCPUString[12]; }; - BYTE Stepping; - BYTE Model; - BYTE Family; - BYTE Type; + uint8_t Stepping; + uint8_t Model; + uint8_t Family; + uint8_t Type; union { struct { - BYTE BrandIndex; - BYTE CLFlush; - BYTE CPUCount; - BYTE APICID; + uint8_t BrandIndex; + uint8_t CLFlush; + uint8_t CPUCount; + uint8_t APICID; - uint32 bSSE3:1; - uint32 DontCare1:8; - uint32 bSSSE3:1; - uint32 DontCare1a:9; - uint32 bSSE41:1; - uint32 bSSE42:1; - uint32 DontCare2a:11; + uint32_t bSSE3:1; + uint32_t DontCare1:8; + uint32_t bSSSE3:1; + uint32_t DontCare1a:9; + uint32_t bSSE41:1; + uint32_t bSSE42:1; + uint32_t DontCare2a:11; - uint32 bFPU:1; - uint32 bVME:1; - uint32 bDE:1; - uint32 bPSE:1; - uint32 bRDTSC:1; - uint32 bMSR:1; - uint32 bPAE:1; - uint32 bMCE:1; - uint32 bCX8:1; - uint32 bAPIC:1; - uint32 bReserved1:1; - uint32 bSEP:1; - uint32 bMTRR:1; - uint32 bPGE:1; - uint32 bMCA:1; - uint32 bCMOV:1; - uint32 bPAT:1; - uint32 bPSE36:1; - uint32 bPSN:1; - uint32 bCFLUSH:1; - uint32 bReserved2:1; - uint32 bDS:1; - uint32 bACPI:1; - uint32 bMMX:1; - uint32 bFXSR:1; - uint32 bSSE:1; - uint32 bSSE2:1; - uint32 bSS:1; - uint32 bHTT:1; - uint32 bTM:1; - uint32 bReserved3:1; - uint32 bPBE:1; + uint32_t bFPU:1; + uint32_t bVME:1; + uint32_t bDE:1; + uint32_t bPSE:1; + uint32_t bRDTSC:1; + uint32_t bMSR:1; + uint32_t bPAE:1; + uint32_t bMCE:1; + uint32_t bCX8:1; + uint32_t bAPIC:1; + uint32_t bReserved1:1; + uint32_t bSEP:1; + uint32_t bMTRR:1; + uint32_t bPGE:1; + uint32_t bMCA:1; + uint32_t bCMOV:1; + uint32_t bPAT:1; + uint32_t bPSE36:1; + uint32_t bPSN:1; + uint32_t bCFLUSH:1; + uint32_t bReserved2:1; + uint32_t bDS:1; + uint32_t bACPI:1; + uint32_t bMMX:1; + uint32_t bFXSR:1; + uint32_t bSSE:1; + uint32_t bSSE2:1; + uint32_t bSS:1; + uint32_t bHTT:1; + uint32_t bTM:1; + uint32_t bReserved3:1; + uint32_t bPBE:1; - uint32 DontCare2:22; - uint32 bMMXPlus:1; // AMD's MMX extensions - uint32 bMMXAgain:1; // Just a copy of bMMX above - uint32 DontCare3:6; - uint32 b3DNowPlus:1; - uint32 b3DNow:1; + uint32_t DontCare2:22; + uint32_t bMMXPlus:1; // AMD's MMX extensions + uint32_t bMMXAgain:1; // Just a copy of bMMX above + uint32_t DontCare3:6; + uint32_t b3DNowPlus:1; + uint32_t b3DNow:1; }; - uint32 FeatureFlags[4]; + uint32_t FeatureFlags[4]; }; - BYTE AMDStepping; - BYTE AMDModel; - BYTE AMDFamily; - BYTE bIsAMD; + uint8_t AMDStepping; + uint8_t AMDModel; + uint8_t AMDFamily; + uint8_t bIsAMD; union { struct { - BYTE DataL1LineSize; - BYTE DataL1LinesPerTag; - BYTE DataL1Associativity; - BYTE DataL1SizeKB; + uint8_t DataL1LineSize; + uint8_t DataL1LinesPerTag; + uint8_t DataL1Associativity; + uint8_t DataL1SizeKB; }; - uint32 AMD_DataL1Info; + uint32_t AMD_DataL1Info; }; }; -extern "C" CPUInfo CPU; +extern CPUInfo CPU; struct PalEntry; void CheckCPUID (CPUInfo *cpu); diff --git a/src/xlat/xlat.h b/src/xlat/xlat.h index 770f69bcae..cf5fc70895 100644 --- a/src/xlat/xlat.h +++ b/src/xlat/xlat.h @@ -66,19 +66,19 @@ struct FBoomArg { bool bOrExisting; bool bUseConstant; - BYTE ListSize; - BYTE ArgNum; - BYTE ConstantValue; + uint8_t ListSize; + uint8_t ArgNum; + uint8_t ConstantValue; uint16_t AndValue; uint16_t ResultFilter[15]; - BYTE ResultValue[15]; + uint8_t ResultValue[15]; }; struct FBoomTranslator { uint16_t FirstLinetype; uint16_t LastLinetype; - BYTE NewSpecial; + uint8_t NewSpecial; TArray Args; } ; diff --git a/src/zstring.h b/src/zstring.h index e486a0e8b1..4de4a2ba07 100644 --- a/src/zstring.h +++ b/src/zstring.h @@ -1,3 +1,4 @@ +#pragma once /* ** zstring.h ** @@ -31,8 +32,6 @@ ** */ -#ifndef ZSTRING_H -#define ZSTRING_H #include #include @@ -450,4 +449,3 @@ template<> struct THashTraits int Compare(const FString &left, const FString &right) { return left.Compare(right); } }; -#endif diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index 9f7564b95f..93807661eb 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -516,3 +516,53 @@ B68EB7CFB4CC481796E2919B9C16DFBD // Moc11.wad e1m6 setlinespecial 2410 Sector_Set3DFloor 32002 4 1 0 0 } +1ED329858AB154C55878DA1C11A4F100 // unloved.pk3:unlovedmaps.wad map01 +{ + clipmidtex +} + +FA23E72FA955E66EC68609F72C0BA71E // unloved.pk3:unlovedmaps.wad map02 +{ + clipmidtex +} + +41BEC1F643CFEEC997AF98276A05EC88 // unloved.pk3:unlovedmaps.wad map03 +{ + clipmidtex +} + +AF9A6370BE562584BC11165ECF364713 // unloved.pk3:unlovedmaps.wad map04 +{ + clipmidtex +} + +DC96228097DD004C40CCB1DB14A91EAA // unloved.pk3:unlovedmaps.wad map05 +{ + clipmidtex +} + +261E64897A572C8DB8DC041E64BE27AD // unloved2beta1.pk3:u2_new2maps2.wad map06 +{ + clipmidtex +} + +04800B1F35E8C036EBABC8C616402927 // unloved2beta1.pk3:u2_new2maps2.wad map07 +{ + clipmidtex +} + +9E54F70648A77BBD090FF78A3AA05367 // unloved2beta1.pk3:u2_new2maps2.wad map08 +{ + clipmidtex +} + +72E9E0F41F691B7F956E62F35B4A617F // unloved2beta1.pk3:u2_new2maps2.wad map09 +{ + clipmidtex +} + +3D3FE412E87AD8B2316DAEC9E25F2E5D // unloved2beta1.pk3:u2_new2maps2.wad map10 +{ + clipmidtex +} + diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 55477ee2e2..0e0e9831d0 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1676,6 +1676,7 @@ OPTMNU_NETWORK = "Network Options"; OPTMNU_SOUND = "Sound Options"; OPTMNU_DISPLAY = "Display Options"; OPTMNU_VIDEO = "Set video mode"; +OPTMNU_CHANGERENDER = "Change Rendering Output"; OPTMNU_DEFAULTS = "Reset to defaults"; OPTMNU_RESETTOSAVED = "Reset to last saved"; OPTMNU_CONSOLE = "Go to console"; @@ -1800,6 +1801,9 @@ DSPLYMNU_SCREENSIZE = "Screen size"; DSPLYMNU_BRIGHTNESS = "Brightness"; DSPLYMNU_VSYNC = "Vertical Sync"; DSPLYMNU_CAPFPS = "Rendering Interpolation"; +DSPLYMNU_COLUMNMETHOD = "Column render mode"; +DSPLYMNU_BLENDMETHOD = "Transparency render mode"; + DSPLYMNU_WIPETYPE = "Screen wipe style"; DSPLYMNU_SHOWENDOOM = "Show ENDOOM screen"; DSPLYMNU_BLOODFADE = "Blood Flash Intensity"; @@ -1808,6 +1812,7 @@ DSPLYMNU_WATERFADE = "Underwater Blend Intensity"; DSPLYMNU_PALLETEHACK = "DirectDraw palette hack"; // Not used DSPLYMNU_ATTACHEDSURFACES = "Use attached surfaces"; // Not used DSPLYMNU_SKYMODE = "Sky render mode"; +DSPLYMNU_LINEARSKY = "Linear skies"; DSPLYMNU_GZDFULLBRIGHT = "Fullbright overrides sector color"; DSPLYMNU_DRAWFUZZ = "Use fuzz effect"; DSPLYMNU_TRANSSOUL = "Lost Soul translucency"; @@ -1827,6 +1832,7 @@ DSPLYMNU_DIMCOLOR = "Dim color"; DSPLYMNU_MOVEBOB = "View bob amount while moving"; DSPLYMNU_STILLBOB = "View bob amount while not moving"; DSPLYMNU_BOBSPEED = "Weapon bob speed"; +DSPLYMNU_GPUSWITCH = "Notebook Switchable GPU"; // HUD Options HUDMNU_TITLE = "HUD Options"; @@ -2166,6 +2172,13 @@ MODMNU_QUALITY = "Quality"; MODMNU_VOLUMERAMPING = "Volume ramping"; MODMNU_CHIPOMATIC = "Chip-o-matic"; +// Renderer Options +RNDMNU_TITLE = "CHANGE RENDERER"; +RNDMNU_RENDERER = "Hardware Acceleration"; +RNDMNU_TRUECOLOR = "Software Truecolor Mode"; +RNDMNU_POLY = "Poly Renderer (experimental)"; +RNDMNU_CANVAS = "Software Canvas"; + // Video Options VIDMNU_TITLE = "VIDEO MODE"; VIDMNU_FULLSCREEN = "Fullscreen"; @@ -2208,9 +2221,6 @@ JOYMNU_NOAXES = "No configurable axes"; // Option Values OPTVAL_OFF = "Off"; OPTVAL_ON = "On"; -OPTVAL_LOW = "Low"; -OPTVAL_MEDIUM = "Medium"; -OPTVAL_HIGH = "High"; OPTVAL_MALE = "Male"; OPTVAL_FEMALE = "Female"; OPTVAL_OTHER = "Other"; @@ -2229,6 +2239,8 @@ OPTVAL_INVERTED = "Inverted"; OPTVAL_NOTINVERTED = "Not Inverted"; OPTVAL_ORIGINAL = "Original"; OPTVAL_OPTIMIZED = "Optimized"; +OPTVAL_CLASSIC = "Classic (Faster)"; +OPTVAL_PRECISE = "Precise"; OPTVAL_NORMAL = "Normal"; OPTVAL_STRETCH = "Stretch"; OPTVAL_CAPPED = "Capped"; @@ -2345,6 +2357,13 @@ OPTVAL_WARNINGS = "Warnings"; OPTVAL_NOTIFICATIONS = "Notifications"; OPTVAL_EVERYTHING = "Everything"; OPTVAL_FULLSCREENONLY = "Fullscreen only"; +OPTVAL_GL = "OpenGL"; +OPTVAL_D3D = "Direct3D"; +OPTVAL_HWPOLY = "OpenGL-Accelerated"; +OPTVAL_SWDOOM = "Doom Software Renderer"; +OPTVAL_DEDICATED = "High-Performance"; +OPTVAL_INTEGRATED = "Power-Saving"; + // Colors C_BRICK = "\cabrick"; C_TAN = "\cbtan"; @@ -2656,6 +2675,7 @@ GLLIGHTMNU_LIGHTDEFS = "Enable light definitions"; GLLIGHTMNU_CLIPLIGHTS = "Clip lights"; GLLIGHTMNU_LIGHTSPRITES = "Lights affect sprites"; GLLIGHTMNU_LIGHTPARTICLES = "Lights affect particles"; +GLLIGHTMNU_LIGHTSHADOWMAP = "Light shadowmaps"; // OpenGL Preferences GLPREFMNU_TITLE = "OPENGL PREFERENCES"; @@ -2772,3 +2792,17 @@ OPTVAL_HIGH = "High"; OPTVAL_EXTREME = "Extreme"; OPTVAL_OBVERSEFIRST = "Obverse"; OPTVAL_REVERSEFIRST = "Reverse"; + +// QZDoom exclusive: + +DSPLYMNU_TCOPT = "TrueColor Options"; + +TCMNU_TITLE = "TRUECOLOR OPTIONS"; + + +TCMNU_TRUECOLOR = "True color output"; +TCMNU_MINFILTER = "Linear filter when downscaling"; +TCMNU_MAGFILTER = "Linear filter when upscaling"; +TCMNU_MIPMAP = "Use mipmapped textures"; +TCMNU_DYNLIGHTS = "Dynamic lights"; + diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 2dd3e5e559..ad96a21559 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -345,6 +345,7 @@ OptionMenu "OptionsMenu" Submenu "$OPTMNU_SOUND", "SoundOptions" Submenu "$OPTMNU_DISPLAY", "VideoOptions" Submenu "$OPTMNU_VIDEO", "VideoModeMenu" + Submenu "$OPTMNU_CHANGERENDER", "RendererMenu" StaticText " " SafeCommand "$OPTMNU_DEFAULTS", "reset2defaults" SafeCommand "$OPTMNU_RESETTOSAVED", "reset2saved" @@ -612,6 +613,18 @@ OptionMenu "JoystickConfigMenu" // //------------------------------------------------------------------------------------------- +OptionValue ColumnMethods +{ + 0.0, "$OPTVAL_ORIGINAL" + 1.0, "$OPTVAL_OPTIMIZED" +} + +OptionValue BlendMethods +{ + 0.0, "$OPTVAL_CLASSIC" + 1.0, "$OPTVAL_PRECISE" +} + OptionValue SkyModes { 0.0, "$OPTVAL_NORMAL" @@ -670,6 +683,13 @@ OptionValue Fuzziness 2.0, "$OPTVAL_SHADOW" } +OptionValue GPUSwitch +{ + 0.0, "$OPTVAL_DEFAULT" + 1.0, "$OPTVAL_DEDICATED" + 2.0, "$OPTVAL_INTEGRATED" +} + OptionMenu "OpenGLOptions" { Title "$GLMNU_TITLE" @@ -678,11 +698,23 @@ OptionMenu "OpenGLOptions" Submenu "$GLMNU_PREFS", "GLPrefOptions" } +OptionMenu "TrueColorOptions" +{ + Title "$TCMNU_TITLE" + StaticText " " + //Option "$TCMNU_TRUECOLOR", "swtruecolor", "OnOff" + Option "$TCMNU_MINFILTER", "r_minfilter", "OnOff" + Option "$TCMNU_MAGFILTER", "r_magfilter", "OnOff" + Option "$TCMNU_MIPMAP", "r_mipmap", "OnOff" + Option "$TCMNU_DYNLIGHTS", "r_dynlights", "OnOff" +} + OptionMenu "VideoOptions" { Title "$DSPLYMNU_TITLE" Submenu "$DSPLYMNU_GLOPT", "OpenGLOptions" + Submenu "$DSPLYMNU_TCOPT", "TrueColorOptions" Submenu "$DSPLYMNU_SCOREBOARD", "ScoreboardOptions" StaticText " " Slider "$DSPLYMNU_SCREENSIZE", "screenblocks", 3.0, 12.0, 1.0, 0 @@ -697,6 +729,7 @@ OptionMenu "VideoOptions" Slider "$DSPLYMNU_BLOODFADE", "blood_fade_scalar", 0.0, 1.0, 0.05, 2 Slider "$DSPLYMNU_PICKUPFADE", "pickup_fade_scalar", 0.0, 1.0, 0.05, 2 Slider "$DSPLYMNU_WATERFADE", "underwater_fade_scalar", 0.0, 1.0, 0.05, 2 + Option "$DSPLYMNU_BLENDMETHOD", "r_blendmethod", "BlendMethods" StaticText " " Option "$DSPLYMNU_WIPETYPE", "wipetype", "Wipes" @@ -709,6 +742,7 @@ OptionMenu "VideoOptions" } Option "$DSPLYMNU_SKYMODE", "r_skymode", "SkyModes" + Option "$DSPLYMNU_LINEARSKY", "r_linearsky", "OnOff" Option "$DSPLYMNU_GZDFULLBRIGHT", "r_fullbrightignoresectorcolor", "OnOff" Option "$DSPLYMNU_DRAWFUZZ", "r_drawfuzz", "Fuzziness" Slider "$DSPLYMNU_TRANSSOUL", "transsouls", 0.25, 1.0, 0.05, 2 @@ -728,7 +762,11 @@ OptionMenu "VideoOptions" Slider "$DSPLYMNU_MOVEBOB", "movebob", 0, 1.0, 0.05, 2 Slider "$DSPLYMNU_STILLBOB", "stillbob", 0, 1.0, 0.05, 2 Slider "$DSPLYMNU_BOBSPEED", "wbobspeed", 0, 2.0, 0.1 - + IfOption(Windows) + { + StaticText " " + Option "$DSPLYMNU_GPUSWITCH", vid_gpuswitch, "GPUSwitch" + } } //------------------------------------------------------------------------------------------- @@ -1750,6 +1788,42 @@ OptionMenu ModReplayerOptions // the foo_dumb preferences in foobar2000. } +/*======================================= + * + * Change Renderer Menu + * + *=======================================*/ + +OptionValue "PolyDoom" +{ + 0, "$OPTVAL_SWDOOM" + 1, "$OPTVAL_HWPOLY" +} + +OptionValue "D3DGL" +{ + 0, "$OPTVAL_GL" + 1, "$OPTVAL_D3D" +} + +OptionValue "GLD3D" +{ + 0, "$OPTVAL_D3D" + 1, "$OPTVAL_GL" +} + +OptionMenu RendererMenu +{ + Title "$RNDMNU_TITLE" + Option "$RNDMNU_RENDERER", "vid_renderer", "PolyDoom" + Option "$RNDMNU_TRUECOLOR", "swtruecolor", "OnOff" + Option "$RNDMNU_POLY", "r_polyrenderer", "OnOff" + IfOption(Windows) + { + Option "$RNDMNU_CANVAS", "vid_used3d", "D3DGL" + } +} + /*======================================= * * Video mode menu diff --git a/wadsrc/static/menudef.zz b/wadsrc/static/menudef.zz index 84ddb94485..054c5ac67a 100644 --- a/wadsrc/static/menudef.zz +++ b/wadsrc/static/menudef.zz @@ -226,6 +226,7 @@ OptionMenu "GLLightOptions" Option "$GLLIGHTMNU_CLIPLIGHTS", gl_lights_checkside, "YesNo" Option "$GLLIGHTMNU_LIGHTSPRITES", gl_light_sprites, "YesNo" Option "$GLLIGHTMNU_LIGHTPARTICLES", gl_light_particles, "YesNo" + Option "$GLLIGHTMNU_LIGHTSHADOWMAP", gl_light_shadowmap, "YesNo" } OptionMenu "GLPrefOptions" diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index a6bc6bbd7f..9a682f3868 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -26,6 +26,7 @@ out vec4 FragNormal; uniform sampler2D tex; +uniform sampler2D ShadowMap; vec4 Process(vec4 color); vec4 ProcessTexel(); @@ -132,6 +133,74 @@ float R_DoomLightingEquation(float light) return lightscale; } +//=========================================================================== +// +// Check if light is in shadow according to its 1D shadow map +// +//=========================================================================== + +#ifdef SUPPORTS_SHADOWMAPS + +float sampleShadowmap(vec2 dir, float v) +{ + float u; + if (abs(dir.x) > abs(dir.y)) + { + if (dir.x >= 0.0) + u = dir.y / dir.x * 0.125 + (0.25 + 0.125); + else + u = dir.y / dir.x * 0.125 + (0.75 + 0.125); + } + else + { + if (dir.y >= 0.0) + u = dir.x / dir.y * 0.125 + 0.125; + else + u = dir.x / dir.y * 0.125 + (0.50 + 0.125); + } + float dist2 = dot(dir, dir); + return texture(ShadowMap, vec2(u, v)).x > dist2 ? 1.0 : 0.0; +} + +//=========================================================================== +// +// Check if light is in shadow using Percentage Closer Filtering (PCF) +// +//=========================================================================== + +#define PCF_FILTER_STEP_COUNT 3 +#define PCF_COUNT (PCF_FILTER_STEP_COUNT * 2 + 1) + +float shadowmapAttenuation(vec4 lightpos, float shadowIndex) +{ + if (shadowIndex >= 1024.0) + return 1.0; // No shadowmap available for this light + + float v = (shadowIndex + 0.5) / 1024.0; + + vec2 ray = pixelpos.xz - lightpos.xz; + float length = length(ray); + if (length < 3.0) + return 1.0; + + vec2 dir = ray / length; + + ray -= dir * 2.0; // margin + dir = dir * min(length / 50.0, 1.0); // avoid sampling behind light + + vec2 normal = vec2(-dir.y, dir.x); + vec2 bias = dir * 10.0; + + float sum = 0.0; + for (float x = -PCF_FILTER_STEP_COUNT; x <= PCF_FILTER_STEP_COUNT; x++) + { + sum += sampleShadowmap(ray + normal * x - bias * abs(x), v); + } + return sum / PCF_COUNT; +} + +#endif + //=========================================================================== // // Standard lambertian diffuse light calculation @@ -151,10 +220,14 @@ float diffuseContribution(vec3 lightDirection, vec3 normal) // //=========================================================================== -float pointLightAttenuation(vec4 lightpos, float attenuate) +float pointLightAttenuation(vec4 lightpos, float lightcolorA) { float attenuation = max(lightpos.w - distance(pixelpos.xyz, lightpos.xyz),0.0) / lightpos.w; - if (attenuate == 0.0) +#ifdef SUPPORTS_SHADOWMAPS + float shadowIndex = abs(lightcolorA) - 1.0; + attenuation *= shadowmapAttenuation(lightpos, shadowIndex); +#endif + if (lightcolorA >= 0.0) // Sign bit is the attenuated light flag { return attenuation; } diff --git a/wadsrc/static/shaders/glsl/shadowmap.fp b/wadsrc/static/shaders/glsl/shadowmap.fp new file mode 100644 index 0000000000..194da954ef --- /dev/null +++ b/wadsrc/static/shaders/glsl/shadowmap.fp @@ -0,0 +1,162 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +struct GPUNode +{ + vec2 aabb_min; + vec2 aabb_max; + int left; + int right; + int line_index; + int padding; +}; + +struct GPULine +{ + vec2 pos; + vec2 delta; +}; + +layout(std430, binding = 2) buffer LightNodes +{ + GPUNode nodes[]; +}; + +layout(std430, binding = 3) buffer LightLines +{ + GPULine lines[]; +}; + +layout(std430, binding = 4) buffer LightList +{ + vec4 lights[]; +}; + +bool overlapRayAABB(vec2 ray_start2d, vec2 ray_end2d, vec2 aabb_min2d, vec2 aabb_max2d) +{ + // To do: simplify test to use a 2D test + vec3 ray_start = vec3(ray_start2d, 0.0); + vec3 ray_end = vec3(ray_end2d, 0.0); + vec3 aabb_min = vec3(aabb_min2d, -1.0); + vec3 aabb_max = vec3(aabb_max2d, 1.0); + + vec3 c = (ray_start + ray_end) * 0.5f; + vec3 w = ray_end - c; + vec3 h = (aabb_max - aabb_min) * 0.5f; // aabb.extents(); + + c -= (aabb_max + aabb_min) * 0.5f; // aabb.center(); + + vec3 v = abs(w); + + if (abs(c.x) > v.x + h.x || abs(c.y) > v.y + h.y || abs(c.z) > v.z + h.z) + return false; // disjoint; + + if (abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y || + abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x || + abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x) + return false; // disjoint; + + return true; // overlap; +} + +float intersectRayLine(vec2 ray_start, vec2 ray_end, int line_index, vec2 raydelta, float rayd, float raydist2) +{ + const float epsilon = 0.0000001; + GPULine line = lines[line_index]; + + vec2 raynormal = vec2(raydelta.y, -raydelta.x); + + float den = dot(raynormal, line.delta); + if (abs(den) > epsilon) + { + float t_line = (rayd - dot(raynormal, line.pos)) / den; + if (t_line >= 0.0 && t_line <= 1.0) + { + vec2 linehitdelta = line.pos + line.delta * t_line - ray_start; + float t = dot(raydelta, linehitdelta) / raydist2; + return t > 0.0 ? t : 1.0; + } + } + + return 1.0; +} + +bool isLeaf(int node_index) +{ + return nodes[node_index].line_index != -1; +} + +float rayTest(vec2 ray_start, vec2 ray_end) +{ + vec2 raydelta = ray_end - ray_start; + float raydist2 = dot(raydelta, raydelta); + vec2 raynormal = vec2(raydelta.y, -raydelta.x); + float rayd = dot(raynormal, ray_start); + if (raydist2 < 1.0) + return 1.0; + + float t = 1.0; + + int stack[16]; + int stack_pos = 1; + stack[0] = nodes.length() - 1; + while (stack_pos > 0) + { + int node_index = stack[stack_pos - 1]; + + if (!overlapRayAABB(ray_start, ray_end, nodes[node_index].aabb_min, nodes[node_index].aabb_max)) + { + stack_pos--; + } + else if (isLeaf(node_index)) + { + t = min(intersectRayLine(ray_start, ray_end, nodes[node_index].line_index, raydelta, rayd, raydist2), t); + stack_pos--; + } + else if (stack_pos == 16) + { + stack_pos--; // stack overflow + } + else + { + stack[stack_pos - 1] = nodes[node_index].left; + stack[stack_pos] = nodes[node_index].right; + stack_pos++; + } + } + + return t; +} + +void main() +{ + int lightIndex = int(gl_FragCoord.y); + + vec4 light = lights[lightIndex]; + float radius = light.w; + vec2 lightpos = light.xy; + + if (radius > 0.0) + { + vec2 pixelpos; + switch (int(gl_FragCoord.x) / 256) + { + case 0: pixelpos = vec2((gl_FragCoord.x - 128.0) / 128.0, 1.0); break; + case 1: pixelpos = vec2(1.0, (gl_FragCoord.x - 384.0) / 128.0); break; + case 2: pixelpos = vec2(-(gl_FragCoord.x - 640.0) / 128.0, -1.0); break; + case 3: pixelpos = vec2(-1.0, -(gl_FragCoord.x - 896.0) / 128.0); break; + } + pixelpos = lightpos + pixelpos * radius; + + float t = rayTest(lightpos, pixelpos); + vec2 delta = (pixelpos - lightpos) * t; + float dist2 = dot(delta, delta); + + FragColor = vec4(dist2, 0.0, 0.0, 1.0); + } + else + { + FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } +} diff --git a/wadsrc/static/shaders/glsl/swshader.fp b/wadsrc/static/shaders/glsl/swshader.fp new file mode 100644 index 0000000000..e33389f29b --- /dev/null +++ b/wadsrc/static/shaders/glsl/swshader.fp @@ -0,0 +1,148 @@ + +precision mediump float; + +in vec4 PixelColor0; +in vec4 PixelColor1; +in vec4 PixelTexCoord0; + +out vec4 FragColor; + +uniform sampler2D Image; +uniform sampler2D Palette; +uniform sampler2D NewScreen; +uniform sampler2D Burn; + +uniform vec4 Desaturation; // { Desat, 1 - Desat } +uniform vec4 PaletteMod; +uniform vec4 Weights; // RGB->Gray weighting { 77/256.0, 143/256.0, 37/256.0, 1 } +uniform vec4 Gamma; + +vec4 TextureLookup(vec2 tex_coord) +{ +#if defined(PALTEX) + float index = texture(Image, tex_coord).x; + index = index * PaletteMod.x + PaletteMod.y; + return texture(Palette, vec2(index, 0.5)); +#else + return texture(Image, tex_coord); +#endif +} + +vec4 Invert(vec4 rgb) +{ +#if defined(INVERT) + rgb.rgb = Weights.www - rgb.xyz; +#endif + return rgb; +} + +float Grayscale(vec4 rgb) +{ + return dot(rgb.rgb, Weights.rgb); +} + +vec4 SampleTexture(vec2 tex_coord) +{ + return Invert(TextureLookup(tex_coord)); +} + +// Normal color calculation for most drawing modes. + +vec4 NormalColor(vec2 tex_coord, vec4 Flash, vec4 InvFlash) +{ + return Flash + SampleTexture(tex_coord) * InvFlash; +} + +// Copy the red channel to the alpha channel. Pays no attention to palettes. + +vec4 RedToAlpha(vec2 tex_coord, vec4 Flash, vec4 InvFlash) +{ + vec4 color = Invert(texture(Image, tex_coord)); + color.a = color.r; + return Flash + color * InvFlash; +} + +// Just return the value of c0. + +vec4 VertexColor(vec4 color) +{ + return color; +} + +// Emulate one of the special colormaps. (Invulnerability, gold, etc.) + +vec4 SpecialColormap(vec2 tex_coord, vec4 start, vec4 end) +{ + vec4 color = SampleTexture(tex_coord); + vec4 range = end - start; + // We can't store values greater than 1.0 in a color register, so we multiply + // the final result by 2 and expect the caller to divide the start and end by 2. + color.rgb = 2.0 * (start + Grayscale(color) * range).rgb; + // Duplicate alpha semantics of NormalColor. + color.a = start.a + color.a * end.a; + return color; +} + +// In-game colormap effect: fade to a particular color and multiply by another, with +// optional desaturation of the original color. Desaturation is stored in c1. +// Fade level is packed int fade.a. Fade.rgb has been premultiplied by alpha. +// Overall alpha is in color.a. +vec4 InGameColormap(vec2 tex_coord, vec4 color, vec4 fade) +{ + vec4 rgb = SampleTexture(tex_coord); + + // Desaturate +#if defined(DESAT) + vec3 intensity; + intensity.rgb = vec3(Grayscale(rgb) * Desaturation.x); + rgb.rgb = intensity.rgb + rgb.rgb * Desaturation.y; +#endif + + // Fade + rgb.rgb = rgb.rgb * fade.aaa + fade.rgb; + + // Shade and Alpha + rgb = rgb * color; + + return rgb; +} + +// Windowed gamma correction. + +vec4 GammaCorrection(vec2 tex_coord) +{ + vec4 color = texture(Image, tex_coord); + color.rgb = pow(color.rgb, Gamma.rgb); + return color; +} + +// The burn wipe effect. + +vec4 BurnWipe(vec4 coord) +{ + vec4 color = texture(NewScreen, coord.xy); + vec4 alpha = texture(Burn, coord.zw); + color.a = alpha.r * 2.0; + return color; +} + +void main() +{ +#if defined(ENORMALCOLOR) + FragColor = NormalColor(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EREDTOALPHA) + FragColor = RedToAlpha(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EVERTEXCOLOR) + FragColor = VertexColor(PixelColor0); +#elif defined(ESPECIALCOLORMAP) + FragColor = SpecialColormap(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EINGAMECOLORMAP) + FragColor = InGameColormap(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EBURNWIPE) + FragColor = BurnWipe(PixelTexCoord0); +#elif defined(EGAMMACORRECTION) + FragColor = GammaCorrection(PixelTexCoord0.xy); +#else + #error Entry point define is missing +#endif +} diff --git a/wadsrc/static/shaders/glsl/swshader.vp b/wadsrc/static/shaders/glsl/swshader.vp new file mode 100644 index 0000000000..a317025aaf --- /dev/null +++ b/wadsrc/static/shaders/glsl/swshader.vp @@ -0,0 +1,22 @@ + +in vec4 AttrPosition; +in vec4 AttrColor0; +in vec4 AttrColor1; +in vec4 AttrTexCoord0; + +out vec4 PixelColor0; +out vec4 PixelColor1; +out vec4 PixelTexCoord0; + +uniform vec4 ScreenSize; + +void main() +{ + gl_Position = vec4(AttrPosition.xy / ScreenSize.xy * 2.0 - 1.0, 1.0, 1.0); +#if defined(EGAMMACORRECTION) + gl_Position.y = -gl_Position.y; +#endif + PixelColor0 = AttrColor0.bgra; + PixelColor1 = AttrColor1.bgra; + PixelTexCoord0 = AttrTexCoord0; +} diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 31d91c6682..029a38be48 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -624,6 +624,7 @@ class Actor : Thinker native action native int OverlayID(); action native double OverlayX(int layer = 0); action native double OverlayY(int layer = 0); + action native double OverlayAlpha(int layer = 0); // DECORATE setters - it probably makes more sense to set these values directly now... void A_SetMass(int newmass) { mass = newmass; } @@ -1041,6 +1042,8 @@ class Actor : Thinker native native void A_WeaponOffset(double wx = 0, double wy = 32, int flags = 0); action native void A_OverlayOffset(int layer = PSP_WEAPON, double wx = 0, double wy = 32, int flags = 0); action native void A_OverlayFlags(int layer, int flags, bool set); + action native void A_OverlayAlpha(int layer, double alph); + action native void A_OverlayRenderStyle(int layer, int style); int ACS_NamedExecute(name script, int mapnum=0, int arg1=0, int arg2=0, int arg3=0) { diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index a3ca6fdb91..19667cf8ae 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -316,6 +316,7 @@ class Thinker : Object native play STAT_LIGHTTRANSFER, // A sector light transfer. These must be ticked after the light effects. STAT_EARTHQUAKE, // Earthquake actors STAT_MAPMARKER, // Map marker actors + STAT_DLIGHT, // Dynamic lights STAT_DEFAULT = 100, // Thinkers go here unless specified otherwise. STAT_SECTOREFFECT, // All sector effects that cause floor and ceiling movement diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 1982c44727..95863452c8 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -721,7 +721,11 @@ enum EPSpriteFlags PSPF_ADDBOB = 1 << 1, PSPF_POWDOUBLE = 1 << 2, PSPF_CVARFAST = 1 << 3, + PSPF_ALPHA = 1 << 4, + PSPF_RENDERSTYLE= 1 << 5, PSPF_FLIP = 1 << 6, + PSPF_FORCEALPHA = 1 << 7, + PSPF_FORCESTYLE = 1 << 8, }; // Default psprite layers diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 0329bd3415..d18b0bde44 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -210,12 +210,14 @@ class PSprite : Object native play native readonly PlayerInfo Owner; native SpriteID Sprite; native int Frame; + native readonly int RenderStyle; native readonly int ID; native Bool processPending; native double x; native double y; native double oldx; native double oldy; + native double alpha; native Bool firstTic; native int Tics; native bool bAddWeapon;