2019-10-17 07:42:11 +00:00
|
|
|
/*
|
|
|
|
** glbackend.cpp
|
|
|
|
**
|
|
|
|
** OpenGL API abstraction
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 2019 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.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
2019-10-04 21:29:00 +00:00
|
|
|
#include <memory>
|
2020-05-30 22:01:00 +00:00
|
|
|
#include <assert.h>
|
2019-10-06 17:32:35 +00:00
|
|
|
#include "glbackend.h"
|
2019-10-17 18:29:58 +00:00
|
|
|
#include "textures.h"
|
|
|
|
#include "palette.h"
|
2019-11-09 13:05:52 +00:00
|
|
|
#include "gamecontrol.h"
|
2019-12-28 21:36:47 +00:00
|
|
|
#include "v_2ddrawer.h"
|
|
|
|
#include "v_video.h"
|
2020-01-18 12:23:01 +00:00
|
|
|
#include "flatvertices.h"
|
2020-04-26 21:17:54 +00:00
|
|
|
#include "build.h"
|
2020-05-30 22:01:00 +00:00
|
|
|
#include "v_draw.h"
|
|
|
|
#include "v_font.h"
|
2020-06-04 16:46:44 +00:00
|
|
|
#include "hw_viewpointuniforms.h"
|
2020-06-04 18:14:48 +00:00
|
|
|
#include "hw_viewpointbuffer.h"
|
2020-06-11 22:25:52 +00:00
|
|
|
#include "hw_renderstate.h"
|
2020-06-11 16:40:53 +00:00
|
|
|
#include "hw_cvars.h"
|
2020-08-28 07:06:49 +00:00
|
|
|
#include "gamestruct.h"
|
2021-03-01 19:48:03 +00:00
|
|
|
#include "gl_models.h"
|
2021-03-25 08:13:16 +00:00
|
|
|
#include "gamefuncs.h"
|
2019-10-04 21:29:00 +00:00
|
|
|
|
2021-03-15 18:39:58 +00:00
|
|
|
CVARD(Bool, hw_hightile, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable hightile texture rendering")
|
|
|
|
bool hw_int_useindexedcolortextures;
|
|
|
|
CUSTOM_CVARD(Bool, hw_useindexedcolortextures, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable indexed color texture rendering")
|
|
|
|
{
|
|
|
|
if (screen) screen->SetTextureFilterMode();
|
|
|
|
}
|
|
|
|
|
2021-03-18 11:32:31 +00:00
|
|
|
EXTERN_CVAR(Bool, gl_texture)
|
2020-07-05 01:31:48 +00:00
|
|
|
|
2020-04-28 20:55:37 +00:00
|
|
|
F2DDrawer twodpsp;
|
2020-06-04 16:46:44 +00:00
|
|
|
static int BufferLock = 0;
|
2020-04-28 20:55:37 +00:00
|
|
|
|
2020-01-19 22:27:59 +00:00
|
|
|
TArray<VSMatrix> matrixArray;
|
2020-09-11 23:11:32 +00:00
|
|
|
void Draw2D(F2DDrawer* drawer, FRenderState& state);
|
2020-01-03 09:09:38 +00:00
|
|
|
|
2019-09-16 17:35:04 +00:00
|
|
|
GLInstance GLInterface;
|
|
|
|
|
2019-10-06 19:15:53 +00:00
|
|
|
GLInstance::GLInstance()
|
|
|
|
:palmanager(this)
|
|
|
|
{
|
2020-01-19 13:08:48 +00:00
|
|
|
VSMatrix mat(0);
|
|
|
|
matrixArray.Push(mat);
|
2019-10-06 19:15:53 +00:00
|
|
|
}
|
|
|
|
|
2020-06-15 18:09:49 +00:00
|
|
|
IHardwareTexture *setpalettelayer(int layer, int translation)
|
|
|
|
{
|
|
|
|
if (layer == 1)
|
|
|
|
return GLInterface.palmanager.GetPalette(GetTranslationType(translation) - Translation_Remap);
|
|
|
|
else if (layer == 2)
|
|
|
|
return GLInterface.palmanager.GetLookup(GetTranslationIndex(translation));
|
|
|
|
else return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-11-07 19:31:16 +00:00
|
|
|
void GLInstance::Init(int ydim)
|
2019-09-16 20:56:48 +00:00
|
|
|
{
|
2020-06-15 18:09:49 +00:00
|
|
|
FMaterial::SetLayerCallback(setpalettelayer);
|
2019-10-05 10:28:08 +00:00
|
|
|
new(&renderState) PolymostRenderState; // reset to defaults.
|
|
|
|
}
|
|
|
|
|
2019-09-16 20:56:48 +00:00
|
|
|
void GLInstance::Deinit()
|
|
|
|
{
|
2020-05-24 22:31:05 +00:00
|
|
|
palmanager.DeleteAll();
|
2019-10-06 22:07:45 +00:00
|
|
|
lastPalswapIndex = -1;
|
2019-09-16 20:56:48 +00:00
|
|
|
}
|
2020-01-02 22:15:16 +00:00
|
|
|
|
2020-01-18 21:41:08 +00:00
|
|
|
void GLInstance::Draw(EDrawType type, size_t start, size_t count)
|
|
|
|
{
|
2020-06-04 16:46:44 +00:00
|
|
|
assert (BufferLock > 0);
|
2020-04-11 22:21:35 +00:00
|
|
|
applyMapFog();
|
2020-01-18 21:41:08 +00:00
|
|
|
renderState.vindex = start;
|
|
|
|
renderState.vcount = count;
|
|
|
|
renderState.primtype = type;
|
|
|
|
rendercommands.Push(renderState);
|
2020-04-11 22:21:35 +00:00
|
|
|
clearMapFog();
|
2020-01-18 21:41:08 +00:00
|
|
|
renderState.StateFlags &= ~(STF_CLEARCOLOR | STF_CLEARDEPTH | STF_VIEWPORTSET | STF_SCISSORSET);
|
2020-01-18 12:23:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-18 21:41:08 +00:00
|
|
|
void GLInstance::DoDraw()
|
|
|
|
{
|
2020-06-12 20:32:49 +00:00
|
|
|
GLState lastState;
|
|
|
|
|
|
|
|
if (rendercommands.Size() > 0)
|
2020-06-11 16:40:53 +00:00
|
|
|
{
|
2020-11-10 15:22:02 +00:00
|
|
|
if (!useMapFog) hw_int_useindexedcolortextures = hw_useindexedcolortextures;
|
|
|
|
|
2020-06-12 20:32:49 +00:00
|
|
|
lastState.Flags = ~rendercommands[0].StateFlags; // Force ALL flags to be considered 'changed'.
|
|
|
|
lastState.DepthFunc = INT_MIN; // Something totally invalid.
|
2020-06-14 18:59:26 +00:00
|
|
|
screen->RenderState()->EnableMultisampling(true);
|
2021-03-01 19:48:03 +00:00
|
|
|
auto& state = *screen->RenderState();
|
2020-06-12 20:32:49 +00:00
|
|
|
|
|
|
|
for (auto& rs : rendercommands)
|
|
|
|
{
|
2021-03-01 19:48:03 +00:00
|
|
|
if (rs.Apply(state, lastState))
|
|
|
|
{
|
|
|
|
if (!rs.model)
|
|
|
|
{
|
|
|
|
state.Draw(rs.primtype, rs.vindex, rs.vcount);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FHWModelRenderer mr(*screen->RenderState(), 0);
|
|
|
|
state.SetDepthFunc(DF_LEqual);
|
|
|
|
state.EnableTexture(true);
|
|
|
|
rs.model->BuildVertexBuffer(&mr);
|
|
|
|
mr.SetupFrame(rs.model, rs.mframes[0], rs.mframes[1], rs.mfactor);
|
|
|
|
rs.model->RenderFrame(&mr, rs.mMaterial.mTexture, rs.mframes[0], rs.mframes[1], 0.f, rs.mMaterial.mTranslation);
|
|
|
|
state.SetDepthFunc(DF_Less);
|
|
|
|
state.SetVertexBuffer(screen->mVertexData);
|
|
|
|
}
|
|
|
|
}
|
2020-06-12 20:32:49 +00:00
|
|
|
}
|
|
|
|
renderState.Apply(*screen->RenderState(), lastState); // apply any pending change before returning.
|
|
|
|
rendercommands.Clear();
|
2020-11-10 15:22:02 +00:00
|
|
|
hw_int_useindexedcolortextures = false;
|
2020-01-03 22:38:50 +00:00
|
|
|
}
|
2020-01-19 13:08:48 +00:00
|
|
|
matrixArray.Resize(1);
|
2019-09-16 17:35:04 +00:00
|
|
|
}
|
2019-09-16 20:56:48 +00:00
|
|
|
|
2019-10-04 16:12:03 +00:00
|
|
|
|
2020-01-19 13:08:48 +00:00
|
|
|
int GLInstance::SetMatrix(int num, const VSMatrix *mat)
|
2019-10-04 16:12:03 +00:00
|
|
|
{
|
2020-01-19 13:08:48 +00:00
|
|
|
int r = renderState.matrixIndex[num];
|
2020-01-03 22:38:50 +00:00
|
|
|
renderState.matrixIndex[num] = matrixArray.Size();
|
|
|
|
matrixArray.Push(*mat);
|
2020-01-19 13:08:48 +00:00
|
|
|
return r;
|
2019-10-04 16:12:03 +00:00
|
|
|
}
|
|
|
|
|
2020-06-04 16:46:44 +00:00
|
|
|
void GLInstance::SetIdentityMatrix(int num)
|
|
|
|
{
|
2020-06-12 20:32:49 +00:00
|
|
|
renderState.matrixIndex[num] = -1;
|
2020-06-04 16:46:44 +00:00
|
|
|
}
|
|
|
|
|
2019-10-07 20:11:09 +00:00
|
|
|
void GLInstance::SetPalswap(int index)
|
|
|
|
{
|
2020-06-05 21:18:21 +00:00
|
|
|
renderState.ShadeDiv = lookups.tables[index].ShadeFactor;
|
2020-09-20 18:39:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetFade(int index)
|
|
|
|
{
|
2021-02-27 12:30:52 +00:00
|
|
|
renderState.FogColor = lookups.getFade(index);
|
2019-10-07 20:11:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-27 22:52:40 +00:00
|
|
|
bool PolymostRenderState::Apply(FRenderState& state, GLState& oldState)
|
2020-06-11 16:40:53 +00:00
|
|
|
{
|
2021-02-27 12:30:52 +00:00
|
|
|
// Fog must be done before the texture so that the texture selector can override it.
|
|
|
|
bool foggy = (GLInterface.useMapFog || (FogColor & 0xffffff));
|
|
|
|
// Disable brightmaps if non-black fog is used.
|
|
|
|
if (!(Flags & RF_FogDisabled) && ShadeDiv >= 1 / 1000.f && foggy)
|
|
|
|
{
|
|
|
|
state.EnableFog(1);
|
|
|
|
float density = GLInterface.useMapFog ? 350.f : 350.f - Scale(numshades - Shade, 150, numshades);
|
|
|
|
state.SetFog((GLInterface.useMapFog) ? PalEntry(0x999999) : FogColor, density);
|
|
|
|
state.SetSoftLightLevel(255);
|
|
|
|
state.SetLightParms(128.f, 1 / 1000.f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
state.EnableFog(0);
|
2021-03-17 23:28:38 +00:00
|
|
|
state.SetFog(0, 0);
|
2021-02-27 12:30:52 +00:00
|
|
|
state.SetSoftLightLevel(ShadeDiv >= 1 / 1000.f ? 255 - Scale(Shade, 255, numshades) : 255);
|
|
|
|
state.SetLightParms(VisFactor, ShadeDiv / (numshades - 2));
|
|
|
|
}
|
|
|
|
|
2020-06-11 16:40:53 +00:00
|
|
|
if (Flags & RF_ColorOnly)
|
|
|
|
{
|
|
|
|
state.EnableTexture(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-01-27 22:52:40 +00:00
|
|
|
if (!mMaterial.mTexture) return false; // Oh no! Something passed an invalid tile!
|
2020-07-05 01:31:48 +00:00
|
|
|
state.EnableTexture(gl_texture);
|
2020-11-10 08:08:48 +00:00
|
|
|
state.SetMaterial(mMaterial.mTexture, mMaterial.uFlags, mMaterial.mScaleFlags, mMaterial.mClampMode, mMaterial.mTranslation, mMaterial.mOverrideShader);
|
2020-06-11 16:40:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
state.SetColor(Color[0], Color[1], Color[2], Color[3]);
|
|
|
|
if (StateFlags != oldState.Flags)
|
|
|
|
{
|
|
|
|
state.EnableDepthTest(StateFlags & STF_DEPTHTEST);
|
|
|
|
|
|
|
|
if ((StateFlags ^ oldState.Flags) & (STF_STENCILTEST | STF_STENCILWRITE))
|
|
|
|
{
|
|
|
|
if (StateFlags & STF_STENCILWRITE)
|
|
|
|
{
|
|
|
|
state.EnableStencil(true);
|
|
|
|
state.SetEffect(EFF_STENCIL);
|
|
|
|
state.SetStencil(0, SOP_Increment, SF_ColorMaskOff);
|
|
|
|
}
|
|
|
|
else if (StateFlags & STF_STENCILTEST)
|
|
|
|
{
|
|
|
|
state.EnableStencil(true);
|
|
|
|
state.SetEffect(EFF_NONE);
|
|
|
|
state.SetStencil(1, SOP_Keep, SF_DepthMaskOff);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
state.EnableStencil(false);
|
|
|
|
state.SetEffect(EFF_NONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((StateFlags ^ oldState.Flags) & (STF_CULLCW | STF_CULLCCW))
|
|
|
|
{
|
|
|
|
int cull = Cull_None;
|
|
|
|
if (StateFlags & STF_CULLCCW) cull = Cull_CCW;
|
|
|
|
else if (StateFlags & STF_CULLCW) cull = Cull_CW;
|
|
|
|
state.SetCulling(cull);
|
|
|
|
}
|
|
|
|
state.SetColorMask(StateFlags & STF_COLORMASK);
|
|
|
|
state.SetDepthMask(StateFlags & STF_DEPTHMASK);
|
|
|
|
if (StateFlags & (STF_CLEARCOLOR | STF_CLEARDEPTH))
|
|
|
|
{
|
|
|
|
int clear = 0;
|
2020-06-12 18:31:23 +00:00
|
|
|
if (StateFlags & STF_CLEARCOLOR) clear |= CT_Color;
|
2020-06-11 16:40:53 +00:00
|
|
|
if (StateFlags & STF_CLEARDEPTH) clear |= CT_Depth;
|
|
|
|
state.Clear(clear);
|
|
|
|
}
|
|
|
|
if (StateFlags & STF_VIEWPORTSET)
|
|
|
|
{
|
|
|
|
state.SetViewport(vp_x, vp_y, vp_w, vp_h);
|
|
|
|
}
|
|
|
|
if (StateFlags & STF_SCISSORSET)
|
|
|
|
{
|
|
|
|
state.SetScissor(sc_x, sc_y, sc_w, sc_h);
|
|
|
|
}
|
|
|
|
state.SetDepthBias(mBias.mFactor, mBias.mUnits);
|
|
|
|
|
|
|
|
StateFlags &= ~(STF_CLEARCOLOR | STF_CLEARDEPTH | STF_VIEWPORTSET | STF_SCISSORSET);
|
|
|
|
oldState.Flags = StateFlags;
|
|
|
|
}
|
|
|
|
state.SetRenderStyle(Style);
|
|
|
|
if (DepthFunc != oldState.DepthFunc)
|
|
|
|
{
|
|
|
|
state.SetDepthFunc(DepthFunc);
|
|
|
|
oldState.DepthFunc = DepthFunc;
|
|
|
|
}
|
2020-09-05 21:20:48 +00:00
|
|
|
|
2020-06-11 21:55:23 +00:00
|
|
|
state.SetTextureMode(TextureMode);
|
2020-06-11 16:40:53 +00:00
|
|
|
|
|
|
|
state.SetNpotEmulation(NPOTEmulation.Y, NPOTEmulation.X);
|
|
|
|
state.AlphaFunc(Alpha_Greater, AlphaTest ? AlphaThreshold : -1.f);
|
|
|
|
|
|
|
|
if (matrixIndex[Matrix_Model] != -1)
|
|
|
|
{
|
|
|
|
state.EnableModelMatrix(true);
|
|
|
|
state.mModelMatrix = matrixArray[matrixIndex[Matrix_Model]];
|
|
|
|
}
|
|
|
|
else state.EnableModelMatrix(false);
|
|
|
|
|
|
|
|
memset(matrixIndex, -1, sizeof(matrixIndex));
|
2021-01-27 22:52:40 +00:00
|
|
|
return true;
|
2020-06-11 16:40:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DoWriteSavePic(FileWriter* file, ESSType ssformat, uint8_t* scr, int width, int height, bool upsidedown)
|
|
|
|
{
|
|
|
|
int pixelsize = 3;
|
|
|
|
int pitch = width * pixelsize;
|
|
|
|
if (upsidedown)
|
|
|
|
{
|
|
|
|
scr += ((height - 1) * width * pixelsize);
|
|
|
|
pitch *= -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
M_CreatePNG(file, scr, nullptr, ssformat, width, height, pitch, vid_gamma);
|
|
|
|
}
|
|
|
|
|
2020-04-26 21:17:54 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Render the view to a savegame picture
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void WriteSavePic(FileWriter* file, int width, int height)
|
|
|
|
{
|
|
|
|
IntRect bounds;
|
|
|
|
bounds.left = 0;
|
|
|
|
bounds.top = 0;
|
|
|
|
bounds.width = width;
|
|
|
|
bounds.height = height;
|
2020-06-11 16:40:53 +00:00
|
|
|
auto& RenderState = *screen->RenderState();
|
2020-04-26 21:17:54 +00:00
|
|
|
|
|
|
|
// we must be sure the GPU finished reading from the buffer before we fill it with new data.
|
2020-06-11 22:25:52 +00:00
|
|
|
screen->WaitForCommands(false);
|
2020-05-31 08:53:11 +00:00
|
|
|
screen->mVertexData->Reset();
|
2020-04-26 21:17:54 +00:00
|
|
|
|
|
|
|
// Switch to render buffers dimensioned for the savepic
|
2020-06-11 16:40:53 +00:00
|
|
|
screen->SetSaveBuffers(true);
|
2020-08-31 20:49:50 +00:00
|
|
|
|
2020-06-11 16:40:53 +00:00
|
|
|
screen->ImageTransitionScene(true);
|
|
|
|
|
|
|
|
RenderState.SetVertexBuffer(screen->mVertexData);
|
|
|
|
screen->mVertexData->Reset();
|
|
|
|
//screen->mLights->Clear();
|
|
|
|
screen->mViewpoints->Clear();
|
2020-04-26 21:17:54 +00:00
|
|
|
|
|
|
|
int oldx = xdim;
|
|
|
|
int oldy = ydim;
|
|
|
|
auto oldwindowxy1 = windowxy1;
|
|
|
|
auto oldwindowxy2 = windowxy2;
|
|
|
|
|
|
|
|
xdim = width;
|
|
|
|
ydim = height;
|
|
|
|
videoSetViewableArea(0, 0, width - 1, height - 1);
|
|
|
|
renderSetAspect(65536, 65536);
|
2020-08-31 20:49:50 +00:00
|
|
|
screen->SetSceneRenderTarget(false);
|
|
|
|
RenderState.SetPassType(NORMAL_PASS);
|
|
|
|
RenderState.EnableDrawBuffers(1, true);
|
|
|
|
|
2020-09-11 23:11:32 +00:00
|
|
|
screen->SetViewportRects(&bounds);
|
2020-09-19 19:31:35 +00:00
|
|
|
twodpsp.Clear();
|
2020-04-26 21:17:54 +00:00
|
|
|
bool didit = gi->GenerateSavePic();
|
|
|
|
|
2020-09-11 23:11:32 +00:00
|
|
|
float Brightness = 8.f / (r_scenebrightness + 8.f);
|
|
|
|
screen->PostProcessScene(false, 0, Brightness, []() {
|
|
|
|
Draw2D(&twodpsp, *screen->RenderState()); // draws the weapon sprites
|
|
|
|
});
|
|
|
|
|
2020-04-26 21:17:54 +00:00
|
|
|
xdim = oldx;
|
|
|
|
ydim = oldy;
|
|
|
|
videoSetViewableArea(oldwindowxy1.x, oldwindowxy1.y, oldwindowxy2.x, oldwindowxy2.y);
|
|
|
|
|
|
|
|
// The 2D drawers can contain some garbage from the dirty render setup. Get rid of that first.
|
2020-05-30 22:01:00 +00:00
|
|
|
twod->Clear();
|
2020-04-26 21:17:54 +00:00
|
|
|
twodpsp.Clear();
|
|
|
|
|
2020-06-11 16:40:53 +00:00
|
|
|
int numpixels = width * height;
|
|
|
|
uint8_t* scr = (uint8_t*)M_Malloc(numpixels * 3);
|
|
|
|
screen->CopyScreenToBuffer(width, height, scr);
|
2020-04-26 21:17:54 +00:00
|
|
|
|
2020-06-11 16:40:53 +00:00
|
|
|
DoWriteSavePic(file, SS_RGB, scr, width, height, screen->FlipSavePic());
|
|
|
|
M_Free(scr);
|
2020-04-26 21:17:54 +00:00
|
|
|
|
|
|
|
// Switch back the screen render buffers
|
|
|
|
screen->SetViewportRects(nullptr);
|
2020-06-11 16:40:53 +00:00
|
|
|
screen->SetSaveBuffers(false);
|
2020-04-26 21:17:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-06-04 16:46:44 +00:00
|
|
|
static HWViewpointUniforms vp;
|
|
|
|
|
|
|
|
void renderSetProjectionMatrix(const float* p)
|
|
|
|
{
|
|
|
|
if (p)
|
|
|
|
{
|
|
|
|
vp.mProjectionMatrix.loadMatrix(p);
|
|
|
|
GLInterface.mProjectionM5 = p[5];
|
|
|
|
}
|
|
|
|
else vp.mProjectionMatrix.loadIdentity();
|
|
|
|
}
|
|
|
|
|
|
|
|
void renderSetViewMatrix(const float* p)
|
|
|
|
{
|
|
|
|
if (p) vp.mViewMatrix.loadMatrix(p);
|
|
|
|
else vp.mViewMatrix.loadIdentity();
|
|
|
|
}
|
2020-05-30 22:01:00 +00:00
|
|
|
|
2020-06-05 17:06:31 +00:00
|
|
|
void renderSetVisibility(float vis)
|
|
|
|
{
|
|
|
|
vp.mGlobVis = vis;
|
|
|
|
}
|
|
|
|
|
2021-03-15 22:46:03 +00:00
|
|
|
void renderSetViewpoint(float x, float y, float z)
|
|
|
|
{
|
2021-03-17 23:28:38 +00:00
|
|
|
vp.mCameraPos = {x, z, y, 0};
|
2021-03-15 22:46:03 +00:00
|
|
|
}
|
|
|
|
|
2020-05-30 22:01:00 +00:00
|
|
|
void renderBeginScene()
|
|
|
|
{
|
2020-06-04 16:46:44 +00:00
|
|
|
assert(BufferLock == 0);
|
|
|
|
|
2020-06-11 16:40:53 +00:00
|
|
|
vp.mPalLightLevels = numshades | (static_cast<int>(gl_fogmode) << 8) | ((int)5 << 16);
|
2020-06-11 22:25:52 +00:00
|
|
|
screen->mViewpoints->SetViewpoint(*screen->RenderState(), &vp);
|
2020-06-04 16:46:44 +00:00
|
|
|
|
2020-05-30 22:01:00 +00:00
|
|
|
if (BufferLock++ == 0)
|
|
|
|
{
|
|
|
|
screen->mVertexData->Map();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void renderFinishScene()
|
|
|
|
{
|
2020-06-04 16:46:44 +00:00
|
|
|
assert(BufferLock == 1);
|
2020-05-30 22:01:00 +00:00
|
|
|
if (--BufferLock == 0)
|
|
|
|
{
|
|
|
|
screen->mVertexData->Unmap();
|
|
|
|
GLInterface.DoDraw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// DFrameBuffer :: DrawRateStuff
|
|
|
|
//
|
|
|
|
// Draws the fps counter, dot ticker, and palette debug.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
CVAR(Bool, vid_fps, false, 0)
|
|
|
|
|
2020-07-03 19:44:57 +00:00
|
|
|
|
|
|
|
static FString statFPS()
|
|
|
|
{
|
|
|
|
static int32_t frameCount;
|
|
|
|
static double lastFrameTime;
|
|
|
|
static double cumulativeFrameDelay;
|
|
|
|
static double lastFPS;
|
|
|
|
|
|
|
|
FString output;
|
|
|
|
|
|
|
|
double frameTime = I_msTimeF();
|
|
|
|
double frameDelay = frameTime - lastFrameTime;
|
|
|
|
cumulativeFrameDelay += frameDelay;
|
|
|
|
|
2020-07-26 15:04:02 +00:00
|
|
|
frameCount++;
|
2020-07-03 19:44:57 +00:00
|
|
|
if (frameDelay >= 0)
|
|
|
|
{
|
|
|
|
output.AppendFormat("%5.1f fps (%.1f ms)\n", lastFPS, frameDelay);
|
|
|
|
|
|
|
|
if (cumulativeFrameDelay >= 1000.0)
|
|
|
|
{
|
2020-07-26 15:04:02 +00:00
|
|
|
lastFPS = 1000. * frameCount / cumulativeFrameDelay;
|
2020-07-03 19:44:57 +00:00
|
|
|
frameCount = 0;
|
|
|
|
cumulativeFrameDelay = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lastFrameTime = frameTime;
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2020-05-30 22:01:00 +00:00
|
|
|
void DrawRateStuff()
|
|
|
|
{
|
|
|
|
// Draws frame time and cumulative fps
|
|
|
|
if (vid_fps)
|
|
|
|
{
|
2020-07-03 19:44:57 +00:00
|
|
|
FString fpsbuff = statFPS();
|
2020-05-30 22:01:00 +00:00
|
|
|
|
|
|
|
int textScale = active_con_scale(twod);
|
|
|
|
int rate_x = screen->GetWidth() / textScale - NewConsoleFont->StringWidth(&fpsbuff[0]);
|
|
|
|
twod->AddColorOnlyQuad(rate_x * textScale, 0, screen->GetWidth(), NewConsoleFont->GetHeight() * textScale, MAKEARGB(255, 0, 0, 0));
|
|
|
|
DrawText(twod, NewConsoleFont, CR_WHITE, rate_x, 0, (char*)&fpsbuff[0],
|
|
|
|
DTA_VirtualWidth, screen->GetWidth() / textScale,
|
|
|
|
DTA_VirtualHeight, screen->GetHeight() / textScale,
|
|
|
|
DTA_KeepRatio, true, TAG_DONE);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 20:06:47 +00:00
|
|
|
int32_t r_scenebrightness = 0;
|
|
|
|
|
2020-06-11 16:40:53 +00:00
|
|
|
|
|
|
|
|
2020-04-28 20:55:37 +00:00
|
|
|
void videoShowFrame(int32_t w)
|
|
|
|
{
|
|
|
|
if (gl_ssao)
|
|
|
|
{
|
2020-06-11 16:40:53 +00:00
|
|
|
screen->AmbientOccludeScene(GLInterface.GetProjectionM5());
|
|
|
|
// To do: the translucent part of the scene should be drawn here, but the render setup in the games is really too broken to do SSAO.
|
2020-04-28 20:55:37 +00:00
|
|
|
|
2020-06-11 16:40:53 +00:00
|
|
|
//glDrawBuffers(1, buffers);
|
2020-04-28 20:55:37 +00:00
|
|
|
}
|
|
|
|
|
2020-06-07 20:06:47 +00:00
|
|
|
float Brightness = 8.f / (r_scenebrightness + 8.f);
|
|
|
|
|
|
|
|
screen->PostProcessScene(false, 0, Brightness, []() {
|
2020-06-11 16:40:53 +00:00
|
|
|
Draw2D(&twodpsp, *screen->RenderState()); // draws the weapon sprites
|
2020-04-28 20:55:37 +00:00
|
|
|
});
|
|
|
|
screen->Update();
|
2020-06-14 19:13:22 +00:00
|
|
|
screen->mVertexData->Reset();
|
2020-08-30 07:32:34 +00:00
|
|
|
videoSetBrightness(0); // immediately reset this after rendering so that the value doesn't stick around in the backend.
|
2020-08-30 17:59:46 +00:00
|
|
|
|
|
|
|
// After finishing the frame, reset everything for the next frame. This needs to be done better.
|
|
|
|
if (!w)
|
|
|
|
{
|
|
|
|
screen->BeginFrame();
|
|
|
|
bool useSSAO = (gl_ssao != 0);
|
|
|
|
screen->SetSceneRenderTarget(useSSAO);
|
|
|
|
twodpsp.Clear();
|
|
|
|
twod->Clear();
|
|
|
|
}
|
2020-04-28 20:55:37 +00:00
|
|
|
}
|
2020-06-11 16:40:53 +00:00
|
|
|
|
2020-05-20 20:48:54 +00:00
|
|
|
TMap<int64_t, bool> cachemap;
|
|
|
|
|
2020-05-20 20:18:03 +00:00
|
|
|
void markTileForPrecache(int tilenum, int palnum)
|
|
|
|
{
|
2020-05-20 20:48:54 +00:00
|
|
|
int i, j;
|
|
|
|
if ((picanm[tilenum].sf & PICANM_ANIMTYPE_MASK) == PICANM_ANIMTYPE_BACK)
|
|
|
|
{
|
|
|
|
i = tilenum - picanm[tilenum].num;
|
|
|
|
j = tilenum;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i = tilenum;
|
|
|
|
j = tilenum + picanm[tilenum].num;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; i <= j; i++)
|
|
|
|
{
|
2020-06-21 23:07:19 +00:00
|
|
|
int64_t val = i + (int64_t(palnum) << 32);
|
2020-05-20 20:48:54 +00:00
|
|
|
cachemap.Insert(val, true);
|
|
|
|
}
|
2020-05-20 20:18:03 +00:00
|
|
|
}
|
2020-06-11 16:40:53 +00:00
|
|
|
|
2020-05-20 20:18:03 +00:00
|
|
|
void precacheMarkedTiles()
|
|
|
|
{
|
2020-05-20 20:48:54 +00:00
|
|
|
decltype(cachemap)::Iterator it(cachemap);
|
|
|
|
decltype(cachemap)::Pair* pair;
|
|
|
|
while (it.NextPair(pair))
|
|
|
|
{
|
2020-06-21 23:07:19 +00:00
|
|
|
int dapicnum = pair->Key & 0x7fffffff;
|
2020-05-20 20:48:54 +00:00
|
|
|
int dapalnum = pair->Key >> 32;
|
2021-03-15 18:39:58 +00:00
|
|
|
Polymost::polymost_precache(dapicnum, dapalnum, 0);
|
2020-05-20 20:48:54 +00:00
|
|
|
}
|
2020-05-20 20:18:03 +00:00
|
|
|
}
|
2020-06-26 20:49:56 +00:00
|
|
|
|
2020-08-14 19:01:27 +00:00
|
|
|
void hud_drawsprite(double sx, double sy, int z, double a, int picnum, int dashade, int dapalnum, int dastat, double alpha)
|
2020-07-16 11:23:26 +00:00
|
|
|
{
|
|
|
|
double dz = z / 65536.;
|
2020-08-14 19:01:27 +00:00
|
|
|
alpha *= (dastat & RS_TRANS1)? glblend[0].def[!!(dastat & RS_TRANS2)].alpha : 1.;
|
2020-09-13 21:14:44 +00:00
|
|
|
int palid = TRANSLATION(Translation_Remap + curbasepal, dapalnum);
|
2020-09-14 20:16:33 +00:00
|
|
|
|
|
|
|
if (picanm[picnum].sf & PICANM_ANIMTYPE_MASK)
|
|
|
|
picnum += animateoffs(picnum, 0);
|
|
|
|
|
2020-11-10 09:08:17 +00:00
|
|
|
auto tex = tileGetTexture(picnum);
|
2020-08-14 19:01:27 +00:00
|
|
|
|
2020-11-10 09:08:17 +00:00
|
|
|
DrawTexture(&twodpsp, tex, sx, sy,
|
|
|
|
DTA_ScaleX, dz, DTA_ScaleY, dz,
|
2020-08-14 19:01:27 +00:00
|
|
|
DTA_Color, shadeToLight(dashade),
|
2020-11-10 09:08:17 +00:00
|
|
|
DTA_TranslationIndex, palid,
|
2020-07-16 11:23:26 +00:00
|
|
|
DTA_ViewportX, windowxy1.x, DTA_ViewportY, windowxy1.y,
|
|
|
|
DTA_ViewportWidth, windowxy2.x - windowxy1.x + 1, DTA_ViewportHeight, windowxy2.y - windowxy1.y + 1,
|
2020-08-14 19:01:27 +00:00
|
|
|
DTA_FullscreenScale, (dastat & RS_STRETCH)? FSMode_ScaleToScreen: FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
|
2020-07-16 11:23:26 +00:00
|
|
|
DTA_CenterOffsetRel, !(dastat & (RS_TOPLEFT | RS_CENTER)),
|
|
|
|
DTA_TopLeft, !!(dastat & RS_TOPLEFT),
|
|
|
|
DTA_CenterOffset, !!(dastat & RS_CENTER),
|
2020-08-14 19:01:27 +00:00
|
|
|
DTA_FlipX, !!(dastat & RS_XFLIPHUD),
|
|
|
|
DTA_FlipY, !!(dastat & RS_YFLIPHUD),
|
2020-07-16 11:23:26 +00:00
|
|
|
DTA_Pin, (dastat & RS_ALIGN_R) ? 1 : (dastat & RS_ALIGN_L) ? -1 : 0,
|
2020-07-24 21:23:19 +00:00
|
|
|
DTA_Rotate, a * (-360./2048),
|
2020-07-26 16:35:47 +00:00
|
|
|
DTA_FlipOffsets, !(dastat & (/*RS_TOPLEFT |*/ RS_CENTER)),
|
2020-08-14 19:01:27 +00:00
|
|
|
DTA_Alpha, alpha,
|
2020-07-16 11:23:26 +00:00
|
|
|
TAG_DONE);
|
|
|
|
}
|
|
|
|
|