gzdoom/src/swrenderer/r_swrenderer.cpp

392 lines
11 KiB
C++
Raw Normal View History

2016-03-01 15:47:10 +00:00
/*
** 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"
2016-12-31 11:45:07 +00:00
#include "swrenderer/things/r_playersprite.h"
#include "swrenderer/scene/r_scene.h"
#include "swrenderer/scene/r_light.h"
2016-03-01 15:47:10 +00:00
#include "v_palette.h"
#include "v_video.h"
#include "m_png.h"
#include "r_swrenderer.h"
#include "scene/r_opaque_pass.h"
2016-12-27 05:31:55 +00:00
#include "scene/r_3dfloors.h"
2017-01-05 03:55:26 +00:00
#include "scene/r_portal.h"
2016-03-01 15:47:10 +00:00
#include "textures/textures.h"
#include "r_data/voxels.h"
2016-12-27 05:31:55 +00:00
#include "drawers/r_draw_rgba.h"
#include "polyrenderer/poly_renderer.h"
2016-11-30 23:42:14 +00:00
#include "p_setup.h"
#include "g_levellocals.h"
2016-03-01 15:47:10 +00:00
// [BB] Use ZDoom's freelook limit for the sotfware renderer.
// Note: ZDoom's limit is chosen such that the sky is rendered properly.
CUSTOM_CVAR (Bool, cl_oldfreelooklimit, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
if (usergame) // [SP] Update pitch limits to the netgame/gamesim.
players[consoleplayer].SendPitchLimits();
}
EXTERN_CVAR(Bool, r_shadercolormaps)
EXTERN_CVAR(Float, maxviewpitch) // [SP] CVAR from GZDoom
2016-03-01 15:47:10 +00:00
2016-11-30 23:42:14 +00:00
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();
}
2016-11-30 23:42:14 +00:00
}
using namespace swrenderer;
FSoftwareRenderer::FSoftwareRenderer()
{
}
FSoftwareRenderer::~FSoftwareRenderer()
{
}
2016-03-01 15:47:10 +00:00
void FSoftwareRenderer::Init()
{
mScene.Init();
InitSWColorMaps();
2016-03-01 15:47:10 +00:00
}
void FSoftwareRenderer::PrecacheTexture(FTexture *tex, int cache)
{
2017-01-15 00:36:57 +00:00
bool isbgra = screen->IsBgra();
2016-03-01 15:47:10 +00:00
if (tex != NULL)
{
if (cache & FTextureManager::HIT_Columnmode)
{
const FTexture::Span *spanp;
2017-01-15 00:36:57 +00:00
if (isbgra)
tex->GetColumnBgra(0, &spanp);
else
tex->GetColumn(0, &spanp);
2016-03-01 15:47:10 +00:00
}
else if (cache != 0)
{
2017-01-15 00:36:57 +00:00
if (isbgra)
2016-06-14 21:05:20 +00:00
tex->GetPixelsBgra();
else
tex->GetPixels ();
2016-03-01 15:47:10 +00:00
}
else
{
tex->Unload ();
}
}
}
void FSoftwareRenderer::Precache(uint8_t *texhitlist, TMap<PClassActor*, bool> &actorhitlist)
{
uint8_t *spritelist = new uint8_t[sprites.Size()];
TMap<PClassActor*, bool>::Iterator it(actorhitlist);
TMap<PClassActor*, bool>::Pair *pair;
memset(spritelist, 0, sprites.Size());
while (it.NextPair(pair))
{
PClassActor *cls = pair->Key;
for (unsigned i = 0; i < cls->GetStateCount(); i++)
{
spritelist[cls->GetStates()[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]);
}
}
2016-03-01 15:47:10 +00:00
void FSoftwareRenderer::RenderView(player_t *player)
{
2016-11-30 23:42:14 +00:00
if (r_polyrenderer)
{
PolyRenderer::Instance()->Viewpoint = r_viewpoint;
PolyRenderer::Instance()->Viewwindow = r_viewwindow;
PolyRenderer::Instance()->RenderView(player);
r_viewpoint = PolyRenderer::Instance()->Viewpoint;
r_viewwindow = PolyRenderer::Instance()->Viewwindow;
}
2017-01-15 00:36:57 +00:00
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();
2017-01-15 00:36:57 +00:00
}
2016-03-01 15:47:10 +00:00
void FSoftwareRenderer::RemapVoxels()
{
for (unsigned i=0; i<Voxels.Size(); i++)
{
Voxels[i]->CreateBgraSlabData();
2016-03-01 15:47:10 +00:00
Voxels[i]->Remap();
}
}
void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height)
2016-03-01 15:47:10 +00:00
{
DCanvas *pic = new DSimpleCanvas (width, height, false);
2016-03-01 15:47:10 +00:00
PalEntry palette[256];
// Take a snapshot of the player's view
pic->Lock ();
2016-11-30 23:42:14 +00:00
if (r_polyrenderer)
{
PolyRenderer::Instance()->Viewpoint = r_viewpoint;
PolyRenderer::Instance()->Viewwindow = r_viewwindow;
PolyRenderer::Instance()->RenderViewToCanvas(player->mo, pic, 0, 0, width, height, true);
r_viewpoint = PolyRenderer::Instance()->Viewpoint;
r_viewwindow = PolyRenderer::Instance()->Viewwindow;
}
2016-11-30 23:42:14 +00:00
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;
}
2016-03-01 15:47:10 +00:00
screen->GetFlashedPalette (palette);
M_CreatePNG (file, pic->GetBuffer(), palette, SS_PAL, width, height, pic->GetPitch());
pic->Unlock ();
delete pic;
}
void FSoftwareRenderer::DrawRemainingPlayerSprites()
{
2016-11-21 14:19:24 +00:00
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()->Viewpoint = r_viewpoint;
PolyRenderer::Instance()->Viewwindow = r_viewwindow;
PolyRenderer::Instance()->RenderRemainingPlayerSprites();
r_viewpoint = PolyRenderer::Instance()->Viewpoint;
r_viewwindow = PolyRenderer::Instance()->Viewwindow;
}
2016-03-01 15:47:10 +00:00
}
int FSoftwareRenderer::GetMaxViewPitch(bool down)
{
2017-01-15 00:36:57 +00:00
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 : ((cl_oldfreelooklimit) ? MAX_UP_ANGLE : MAX_DN_ANGLE));
2016-03-01 15:47:10 +00:00
}
bool FSoftwareRenderer::RequireGLNodes()
{
return true;
}
2016-03-01 15:47:10 +00:00
void FSoftwareRenderer::OnModeSet ()
{
mScene.ScreenResized();
2016-03-01 15:47:10 +00:00
}
void FSoftwareRenderer::SetClearColor(int color)
2016-03-01 15:47:10 +00:00
{
mScene.SetClearColor(color);
2016-03-01 15:47:10 +00:00
}
void FSoftwareRenderer::RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov)
{
auto renderTarget = r_polyrenderer ? PolyRenderer::Instance()->RenderTarget : mScene.MainThread()->Viewport->RenderTarget;
auto &cameraViewpoint = r_polyrenderer ? PolyRenderer::Instance()->Viewpoint : mScene.MainThread()->Viewport->viewpoint;
auto &cameraViewwindow = r_polyrenderer ? PolyRenderer::Instance()->Viewwindow : mScene.MainThread()->Viewport->viewwindow;
// Grab global state shared with rest of zdoom
cameraViewpoint = r_viewpoint;
cameraViewwindow = r_viewwindow;
2017-02-01 15:02:21 +00:00
uint8_t *Pixels = renderTarget->IsBgra() ? (uint8_t*)tex->GetPixelsBgra() : (uint8_t*)tex->GetPixels();
DSimpleCanvas *Canvas = renderTarget->IsBgra() ? tex->GetCanvasBgra() : tex->GetCanvas();
2016-03-01 15:47:10 +00:00
// 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.
2017-01-26 09:22:54 +00:00
CameraLight savedCameraLight = *CameraLight::Instance();
2016-03-01 15:47:10 +00:00
DAngle savedfov = cameraViewpoint.FieldOfView;
R_SetFOV (cameraViewpoint, (double)fov);
2017-01-15 00:36:57 +00:00
2016-11-30 23:42:14 +00:00
if (r_polyrenderer)
PolyRenderer::Instance()->RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), tex->bFirstUpdate);
2016-11-30 23:42:14 +00:00
else
mScene.RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), tex->bFirstUpdate);
2017-01-15 00:36:57 +00:00
R_SetFOV (cameraViewpoint, savedfov);
if (Canvas->IsBgra())
2016-03-01 15:47:10 +00:00
{
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());
}
2016-03-01 15:47:10 +00:00
}
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);
}
2016-03-01 15:47:10 +00:00
}
if (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;
}
}
2016-06-17 08:16:34 +00:00
tex->SetUpdated();
2017-01-26 09:22:54 +00:00
*CameraLight::Instance() = savedCameraLight;
// Sync state back to zdoom
r_viewpoint = cameraViewpoint;
r_viewwindow = cameraViewwindow;
2016-03-01 15:47:10 +00:00
}
2017-01-15 00:36:57 +00:00
void FSoftwareRenderer::PreprocessLevel()
{
// This just sets the default colormap for the spftware renderer.
NormalLight.Maps = realcolormaps.Maps;
NormalLight.ChangeColor(PalEntry(255, 255, 255), 0);
NormalLight.ChangeFade(level.fadeto);
if (level.fadeto == 0)
{
SetDefaultColormap(level.info->FadeTable);
if (level.flags & LEVEL_HASFADETABLE)
{
2017-03-15 22:38:12 +00:00
// This should really be done differently.
level.fadeto = 0xff939393; //[SP] Hexen True-color compatibility, just use gray.
2017-03-15 22:38:12 +00:00
for (auto &s : level.sectors)
{
s.Colormap.FadeColor = level.fadeto;
}
}
}
2017-01-15 00:36:57 +00:00
}
void FSoftwareRenderer::CleanLevelData()
{
}
double FSoftwareRenderer::GetVisibility()
{
2017-03-12 19:40:00 +00:00
return mScene.MainThread()->Light->GetVisibility();
}
void FSoftwareRenderer::SetVisibility(double vis)
{
2017-03-12 19:40:00 +00:00
mScene.MainThread()->Light->SetVisibility(mScene.MainThread()->Viewport.get(), vis);
}