mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-12-16 15:21:02 +00:00
489 lines
13 KiB
C++
489 lines
13 KiB
C++
//
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright(C) 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/
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
/*
|
|
** gl_2ddrawer.h
|
|
** Container class for drawing 2d graphics with a vertex buffer
|
|
**
|
|
**/
|
|
|
|
#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
|
|
dg.mScissor[0] = GLRenderer->ScreenToWindowX(parms.lclip);
|
|
dg.mScissor[1] = GLRenderer->ScreenToWindowY(parms.dclip);
|
|
dg.mScissor[2] = GLRenderer->ScreenToWindowX(parms.rclip) - dg.mScissor[0];
|
|
dg.mScissor[3] = GLRenderer->ScreenToWindowY(parms.uclip) - dg.mScissor[1];
|
|
|
|
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++;
|
|
dg.mVertCount = 8;
|
|
}
|
|
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::Draw()
|
|
{
|
|
F2DDrawer::EDrawType lasttype = DrawTypeTexture;
|
|
|
|
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];
|
|
// DrawTypePoly may not use the color part of the vertex buffer because it needs to use gl_SetColor to produce proper output.
|
|
if (lasttype == DrawTypePoly && dg->mType != DrawTypePoly)
|
|
{
|
|
gl_RenderState.ResetColor(); // this is needed to reset the desaturation.
|
|
EnableColorArray(true);
|
|
}
|
|
else if (lasttype != DrawTypePoly && dg->mType == DrawTypePoly)
|
|
{
|
|
EnableColorArray(false);
|
|
}
|
|
lasttype = dg->mType;
|
|
|
|
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);
|
|
}
|
|
|
|
const auto &viewport = GLRenderer->mScreenViewport;
|
|
glScissor(viewport.left, viewport.top, viewport.width, viewport.height);
|
|
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;
|
|
}
|
|
gl_RenderState.SetVertexBuffer(GLRenderer->mVBO);
|
|
glset.lightmode = savedlightmode;
|
|
}
|
|
|
|
void F2DDrawer::Clear()
|
|
{
|
|
mVertices.Clear();
|
|
mData.Clear();
|
|
}
|