mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-12-16 15:41:39 +00:00
489 lines
14 KiB
C++
489 lines
14 KiB
C++
|
/*
|
||
|
** gl_2ddrawer.h
|
||
|
** Container class for drawing 2d graphics with a vertex buffer
|
||
|
**
|
||
|
**---------------------------------------------------------------------------
|
||
|
** Copyright 2016 Christoph Oelckers
|
||
|
** All rights reserved.
|
||
|
**
|
||
|
** Redistribution and use in source and binary forms, with or without
|
||
|
** modification, are permitted provided that the following conditions
|
||
|
** are met:
|
||
|
**
|
||
|
** 1. Redistributions of source code must retain the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer.
|
||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer in the
|
||
|
** documentation and/or other materials provided with the distribution.
|
||
|
** 3. The name of the author may not be used to endorse or promote products
|
||
|
** derived from this software without specific prior written permission.
|
||
|
** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be
|
||
|
** covered by the terms of the GNU Lesser General Public License as published
|
||
|
** by the Free Software Foundation; either version 2.1 of the License, or (at
|
||
|
** your option) any later version.
|
||
|
** 5. Full disclosure of the entire project's source code, except for third
|
||
|
** party libraries is mandatory. (NOTE: This clause is non-negotiable!)
|
||
|
**
|
||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
**---------------------------------------------------------------------------
|
||
|
**
|
||
|
*/
|
||
|
|
||
|
#include "gl/system/gl_system.h"
|
||
|
#include "gl/system/gl_framebuffer.h"
|
||
|
#include "gl/renderer/gl_renderer.h"
|
||
|
#include "gl/renderer/gl_2ddrawer.h"
|
||
|
#include "gl/textures/gl_material.h"
|
||
|
#include "gl/renderer/gl_renderstate.h"
|
||
|
#include "gl/renderer/gl_lightdata.h"
|
||
|
#include "gl/scene/gl_drawinfo.h"
|
||
|
#include "gl/textures/gl_translate.h"
|
||
|
#include "vectors.h"
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
int F2DDrawer::AddData(const F2DDrawer::DataGeneric *data)
|
||
|
{
|
||
|
int addr = mData.Reserve(data->mLen);
|
||
|
memcpy(&mData[addr], data, data->mLen);
|
||
|
mLastLineCmd = -1;
|
||
|
return addr;
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// Draws a texture
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void F2DDrawer::AddTexture(FTexture *img, DrawParms &parms)
|
||
|
{
|
||
|
double xscale = parms.destwidth / parms.texwidth;
|
||
|
double yscale = parms.destheight / parms.texheight;
|
||
|
double x = parms.x - parms.left * xscale;
|
||
|
double y = parms.y - parms.top * yscale;
|
||
|
double w = parms.destwidth;
|
||
|
double h = parms.destheight;
|
||
|
float u1, v1, u2, v2;
|
||
|
int light = 255;
|
||
|
|
||
|
FMaterial * gltex = FMaterial::ValidateTexture(img, false);
|
||
|
if (gltex == nullptr) return;
|
||
|
|
||
|
DataTexture dg;
|
||
|
|
||
|
dg.mType = DrawTypeTexture;
|
||
|
dg.mLen = (sizeof(dg) + 7) & ~7;
|
||
|
dg.mVertCount = 4;
|
||
|
dg.mRenderStyle = parms.style;
|
||
|
dg.mMasked = !!parms.masked;
|
||
|
dg.mTexture = gltex;
|
||
|
|
||
|
if (parms.colorOverlay && (parms.colorOverlay & 0xffffff) == 0)
|
||
|
{
|
||
|
// handle black overlays as reduced light.
|
||
|
light = 255 - APART(parms.colorOverlay);
|
||
|
parms.colorOverlay = 0;
|
||
|
}
|
||
|
dg.mVertIndex = (int)mVertices.Reserve(parms.colorOverlay == 0? 4 : 8);
|
||
|
dg.mColorOverlay = parms.colorOverlay;
|
||
|
dg.mTranslation = 0;
|
||
|
|
||
|
if (!img->bHasCanvas)
|
||
|
{
|
||
|
if (!parms.alphaChannel)
|
||
|
{
|
||
|
if (parms.remap != NULL && !parms.remap->Inactive)
|
||
|
{
|
||
|
GLTranslationPalette * pal = static_cast<GLTranslationPalette*>(parms.remap->GetNative());
|
||
|
if (pal) dg.mTranslation = -pal->GetIndex();
|
||
|
}
|
||
|
}
|
||
|
dg.mAlphaTexture = !!(parms.style.Flags & STYLEF_RedIsAlpha);
|
||
|
u1 = gltex->GetUL();
|
||
|
v1 = gltex->GetVT();
|
||
|
u2 = gltex->GetUR();
|
||
|
v2 = gltex->GetVB();
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dg.mAlphaTexture = false;
|
||
|
u1 = 0.f;
|
||
|
v1 = 1.f;
|
||
|
u2 = 1.f;
|
||
|
v2 = 0.f;
|
||
|
}
|
||
|
|
||
|
if (parms.flipX)
|
||
|
std::swap(u1, u2);
|
||
|
|
||
|
|
||
|
if (parms.windowleft > 0 || parms.windowright < parms.texwidth)
|
||
|
{
|
||
|
double wi = MIN(parms.windowright, parms.texwidth);
|
||
|
x += parms.windowleft * xscale;
|
||
|
w -= (parms.texwidth - wi + parms.windowleft) * xscale;
|
||
|
|
||
|
u1 = float(u1 + parms.windowleft / parms.texwidth);
|
||
|
u2 = float(u2 - (parms.texwidth - wi) / parms.texwidth);
|
||
|
}
|
||
|
|
||
|
PalEntry color;
|
||
|
if (parms.style.Flags & STYLEF_ColorIsFixed)
|
||
|
{
|
||
|
color = parms.fillcolor;
|
||
|
std::swap(color.r, color.b);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
color = PalEntry(light, light, light);
|
||
|
}
|
||
|
color.a = (BYTE)(parms.Alpha * 255);
|
||
|
|
||
|
// scissor test doesn't use the current viewport for the coordinates, so use real screen coordinates
|
||
|
int btm = (SCREENHEIGHT - screen->GetHeight()) / 2;
|
||
|
btm = SCREENHEIGHT - btm;
|
||
|
|
||
|
int space = (static_cast<OpenGLFrameBuffer*>(screen)->GetTrueHeight() - screen->GetHeight()) / 2;
|
||
|
dg.mScissor[0] = parms.lclip;
|
||
|
dg.mScissor[1] = btm - parms.dclip + space;
|
||
|
dg.mScissor[2] = parms.rclip - parms.lclip;
|
||
|
dg.mScissor[3] = parms.dclip - parms.uclip;
|
||
|
|
||
|
FSimpleVertex *ptr = &mVertices[dg.mVertIndex];
|
||
|
ptr->Set(x, y, 0, u1, v1, color); ptr++;
|
||
|
ptr->Set(x, y + h, 0, u1, v2, color); ptr++;
|
||
|
ptr->Set(x + w, y, 0, u2, v1, color); ptr++;
|
||
|
ptr->Set(x + w, y + h, 0, u2, v2, color); ptr++;
|
||
|
if (parms.colorOverlay != 0)
|
||
|
{
|
||
|
color = parms.colorOverlay;
|
||
|
std::swap(color.r, color.b);
|
||
|
ptr->Set(x, y, 0, u1, v1, color); ptr++;
|
||
|
ptr->Set(x, y + h, 0, u1, v2, color); ptr++;
|
||
|
ptr->Set(x + w, y, 0, u2, v1, color); ptr++;
|
||
|
ptr->Set(x + w, y + h, 0, u2, v2, color); ptr++;
|
||
|
}
|
||
|
AddData(&dg);
|
||
|
}
|
||
|
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void F2DDrawer::AddPoly(FTexture *texture, FVector2 *points, int npoints,
|
||
|
double originx, double originy, double scalex, double scaley,
|
||
|
DAngle rotation, FDynamicColormap *colormap, int lightlevel)
|
||
|
{
|
||
|
FMaterial *gltexture = FMaterial::ValidateTexture(texture, false);
|
||
|
|
||
|
if (gltexture == nullptr)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
DataSimplePoly poly;
|
||
|
|
||
|
poly.mType = DrawTypePoly;
|
||
|
poly.mLen = (sizeof(poly) + 7) & ~7;
|
||
|
poly.mTexture = gltexture;
|
||
|
poly.mColormap = colormap;
|
||
|
poly.mLightLevel = lightlevel;
|
||
|
poly.mVertCount = npoints;
|
||
|
poly.mVertIndex = (int)mVertices.Reserve(npoints);
|
||
|
|
||
|
bool dorotate = rotation != 0;
|
||
|
|
||
|
float cosrot = cos(rotation.Radians());
|
||
|
float sinrot = sin(rotation.Radians());
|
||
|
|
||
|
float uscale = float(1.f / (texture->GetScaledWidth() * scalex));
|
||
|
float vscale = float(1.f / (texture->GetScaledHeight() * scaley));
|
||
|
if (texture->bHasCanvas)
|
||
|
{
|
||
|
vscale = 0 - vscale;
|
||
|
}
|
||
|
float ox = float(originx);
|
||
|
float oy = float(originy);
|
||
|
|
||
|
for (int i = 0; i < npoints; ++i)
|
||
|
{
|
||
|
float u = points[i].X - 0.5f - ox;
|
||
|
float v = points[i].Y - 0.5f - oy;
|
||
|
if (dorotate)
|
||
|
{
|
||
|
float t = u;
|
||
|
u = t * cosrot - v * sinrot;
|
||
|
v = v * cosrot + t * sinrot;
|
||
|
}
|
||
|
mVertices[poly.mVertIndex+i].Set(points[i].X, points[i].Y, 0, u*uscale, v*vscale);
|
||
|
}
|
||
|
AddData(&poly);
|
||
|
}
|
||
|
|
||
|
//===========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//===========================================================================
|
||
|
|
||
|
void F2DDrawer::AddDim(PalEntry color, float damount, int x1, int y1, int w, int h)
|
||
|
{
|
||
|
color.a = uint8_t(damount * 255);
|
||
|
std::swap(color.r, color.b);
|
||
|
|
||
|
DataGeneric dg;
|
||
|
|
||
|
dg.mType = DrawTypeDim;
|
||
|
dg.mLen = (sizeof(dg) + 7) & ~7;
|
||
|
dg.mVertCount = 4;
|
||
|
dg.mVertIndex = (int)mVertices.Reserve(4);
|
||
|
FSimpleVertex *ptr = &mVertices[dg.mVertIndex];
|
||
|
ptr->Set(x1, y1, 0, 0, 0, color); ptr++;
|
||
|
ptr->Set(x1, y1 + h, 0, 0, 0, color); ptr++;
|
||
|
ptr->Set(x1 + w, y1 + h, 0, 0, 0, color); ptr++;
|
||
|
ptr->Set(x1 + w, y1, 0, 0, 0, color); ptr++;
|
||
|
AddData(&dg);
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void F2DDrawer::AddClear(int left, int top, int right, int bottom, int palcolor, uint32 color)
|
||
|
{
|
||
|
PalEntry p = palcolor == -1 || color != 0 ? (PalEntry)color : GPalette.BaseColors[palcolor];
|
||
|
AddDim(p, 1.f, left, top, right - left, bottom - top);
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void F2DDrawer::AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin)
|
||
|
{
|
||
|
float fU1, fU2, fV1, fV2;
|
||
|
|
||
|
FMaterial *gltexture = FMaterial::ValidateTexture(src, false);
|
||
|
|
||
|
if (!gltexture) return;
|
||
|
|
||
|
DataFlatFill dg;
|
||
|
|
||
|
dg.mType = DrawTypeFlatFill;
|
||
|
dg.mLen = (sizeof(dg) + 7) & ~7;
|
||
|
dg.mVertCount = 4;
|
||
|
dg.mVertIndex = (int)mVertices.Reserve(4);
|
||
|
dg.mTexture = gltexture;
|
||
|
|
||
|
// scaling is not used here.
|
||
|
if (!local_origin)
|
||
|
{
|
||
|
fU1 = float(left) / src->GetWidth();
|
||
|
fV1 = float(top) / src->GetHeight();
|
||
|
fU2 = float(right) / src->GetWidth();
|
||
|
fV2 = float(bottom) / src->GetHeight();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fU1 = 0;
|
||
|
fV1 = 0;
|
||
|
fU2 = float(right - left) / src->GetWidth();
|
||
|
fV2 = float(bottom - top) / src->GetHeight();
|
||
|
}
|
||
|
FSimpleVertex *ptr = &mVertices[dg.mVertIndex];
|
||
|
ptr->Set(left, top, 0, fU1, fV1); ptr++;
|
||
|
ptr->Set(left, bottom, 0, fU1, fV2); ptr++;
|
||
|
ptr->Set(right, top, 0, fU2, fV1); ptr++;
|
||
|
ptr->Set(right, bottom, 0, fU2, fV2); ptr++;
|
||
|
AddData(&dg);
|
||
|
}
|
||
|
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color)
|
||
|
{
|
||
|
PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor];
|
||
|
p.a = 255;
|
||
|
std::swap(p.r, p.b);
|
||
|
|
||
|
DataGeneric dg;
|
||
|
|
||
|
dg.mType = DrawTypeLine;
|
||
|
dg.mLen = (sizeof(dg) + 7) & ~7;
|
||
|
dg.mVertCount = 2;
|
||
|
dg.mVertIndex = (int)mVertices.Reserve(2);
|
||
|
mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p);
|
||
|
mVertices[dg.mVertIndex+1].Set(x2, y2, 0, 0, 0, p);
|
||
|
|
||
|
// Test if we can batch multiple line commands
|
||
|
if (mLastLineCmd == -1)
|
||
|
{
|
||
|
mLastLineCmd = AddData(&dg);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DataGeneric *dg = (DataGeneric *)&mData[mLastLineCmd];
|
||
|
dg->mVertCount += 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32 color)
|
||
|
{
|
||
|
PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor];
|
||
|
p.a = 255;
|
||
|
std::swap(p.r, p.b);
|
||
|
|
||
|
DataGeneric dg;
|
||
|
|
||
|
dg.mType = DrawTypePixel;
|
||
|
dg.mLen = (sizeof(dg) + 7) & ~7;
|
||
|
dg.mVertCount = 2;
|
||
|
dg.mVertIndex = (int)mVertices.Reserve(1);
|
||
|
mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p);
|
||
|
AddData(&dg);
|
||
|
}
|
||
|
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
void F2DDrawer::Flush()
|
||
|
{
|
||
|
if (mData.Size() == 0) return;
|
||
|
SBYTE 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;
|
||
|
|
||
|
set(&mVertices[0], mVertices.Size());
|
||
|
for (unsigned i = 0; i < mData.Size();)
|
||
|
{
|
||
|
DataGeneric *dg = (DataGeneric *)&mData[i];
|
||
|
switch (dg->mType)
|
||
|
{
|
||
|
default:
|
||
|
break;
|
||
|
|
||
|
case DrawTypeTexture:
|
||
|
{
|
||
|
DataTexture *dt = static_cast<DataTexture*>(dg);
|
||
|
|
||
|
gl_SetRenderStyle(dt->mRenderStyle, !dt->mMasked, false);
|
||
|
gl_RenderState.SetMaterial(dt->mTexture, CLAMP_XY_NOMIP, dt->mTranslation, -1, dt->mAlphaTexture);
|
||
|
if (dt->mTexture->tex->bHasCanvas) gl_RenderState.SetTextureMode(TM_OPAQUE);
|
||
|
|
||
|
glEnable(GL_SCISSOR_TEST);
|
||
|
glScissor(dt->mScissor[0], dt->mScissor[1], dt->mScissor[2], dt->mScissor[3]);
|
||
|
|
||
|
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
|
||
|
gl_RenderState.Apply();
|
||
|
|
||
|
glDrawArrays(GL_TRIANGLE_STRIP, dt->mVertIndex, 4);
|
||
|
|
||
|
gl_RenderState.BlendEquation(GL_FUNC_ADD);
|
||
|
if (dt->mVertCount > 4)
|
||
|
{
|
||
|
gl_RenderState.SetTextureMode(TM_MASK);
|
||
|
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||
|
gl_RenderState.Apply();
|
||
|
glDrawArrays(GL_TRIANGLE_STRIP, dt->mVertIndex + 4, 4);
|
||
|
}
|
||
|
|
||
|
glScissor(0, 0, screen->GetWidth(), screen->GetHeight());
|
||
|
glDisable(GL_SCISSOR_TEST);
|
||
|
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||
|
gl_RenderState.SetTextureMode(TM_MODULATE);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case DrawTypePoly:
|
||
|
{
|
||
|
DataSimplePoly *dsp = static_cast<DataSimplePoly*>(dg);
|
||
|
|
||
|
FColormap cm;
|
||
|
cm = dsp->mColormap;
|
||
|
gl_SetColor(dsp->mLightLevel, 0, cm, 1.f);
|
||
|
gl_RenderState.SetMaterial(dsp->mTexture, CLAMP_NONE, 0, -1, false);
|
||
|
gl_RenderState.Apply();
|
||
|
glDrawArrays(GL_TRIANGLE_FAN, dsp->mVertIndex, dsp->mVertCount);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case DrawTypeFlatFill:
|
||
|
{
|
||
|
DataFlatFill *dff = static_cast<DataFlatFill*>(dg);
|
||
|
gl_RenderState.SetMaterial(dff->mTexture, CLAMP_NONE, 0, -1, false);
|
||
|
gl_RenderState.Apply();
|
||
|
glDrawArrays(GL_TRIANGLE_STRIP, dg->mVertIndex, dg->mVertCount);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case DrawTypeDim:
|
||
|
gl_RenderState.EnableTexture(false);
|
||
|
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||
|
gl_RenderState.AlphaFunc(GL_GREATER, 0);
|
||
|
gl_RenderState.Apply();
|
||
|
glDrawArrays(GL_TRIANGLE_FAN, dg->mVertIndex, dg->mVertCount);
|
||
|
gl_RenderState.EnableTexture(true);
|
||
|
break;
|
||
|
|
||
|
case DrawTypeLine:
|
||
|
gl_RenderState.EnableTexture(false);
|
||
|
gl_RenderState.Apply();
|
||
|
glDrawArrays(GL_LINES, dg->mVertIndex, dg->mVertCount);
|
||
|
gl_RenderState.EnableTexture(true);
|
||
|
break;
|
||
|
|
||
|
case DrawTypePixel:
|
||
|
gl_RenderState.EnableTexture(false);
|
||
|
gl_RenderState.Apply();
|
||
|
glDrawArrays(GL_POINTS, dg->mVertIndex, dg->mVertCount);
|
||
|
gl_RenderState.EnableTexture(true);
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
i += dg->mLen;
|
||
|
}
|
||
|
mVertices.Clear();
|
||
|
mData.Clear();
|
||
|
gl_RenderState.SetVertexBuffer(GLRenderer->mVBO);
|
||
|
glset.lightmode = savedlightmode;
|
||
|
}
|
||
|
|
||
|
|