This one was particularly nasty because Windows also defines a DWORD, but in Windows it is an unsigned long, not an unsigned int so changing types caused type conflicts and not all could be removed. Those referring to the Windows type have to be kept, fortunately they are mostly in the Win32 directory, with a handful of exceptions elsewhere.
585 lines
16 KiB
C++
585 lines
16 KiB
C++
/*
|
|
** 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);
|
|
}
|