mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-05-31 09:11:43 +00:00
- moved the 2D drawing code to its own directory under 'rendering'.
This commit is contained in:
parent
ef3e5ef01e
commit
c3890342e6
8 changed files with 8 additions and 5 deletions
394
src/rendering/2D/f_wipe.cpp
Normal file
394
src/rendering/2D/f_wipe.cpp
Normal file
|
@ -0,0 +1,394 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright 1993-1996 id Software
|
||||
// Copyright 1999-2016 Randy Heit
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// Mission begin melt/wipe screen special effect.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "v_video.h"
|
||||
#include "m_random.h"
|
||||
#include "f_wipe.h"
|
||||
#include "templates.h"
|
||||
#include "textures/bitmap.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
|
||||
class FBurnTexture : public FTexture
|
||||
{
|
||||
TArray<uint32_t> WorkBuffer;
|
||||
public:
|
||||
FBurnTexture(int w, int h)
|
||||
: WorkBuffer(w*h, true)
|
||||
{
|
||||
Width = w;
|
||||
Height = h;
|
||||
}
|
||||
|
||||
FBitmap GetBgraBitmap(PalEntry*, int *trans) override
|
||||
{
|
||||
FBitmap bmp;
|
||||
bmp.Create(Width, Height);
|
||||
bmp.CopyPixelDataRGB(0, 0, (uint8_t*)WorkBuffer.Data(), Width, Height, 4, Width*4, 0, CF_RGBA, nullptr);
|
||||
if (trans) *trans = 0;
|
||||
return bmp;
|
||||
}
|
||||
|
||||
uint32_t *GetBuffer()
|
||||
{
|
||||
return WorkBuffer.Data();
|
||||
}
|
||||
};
|
||||
|
||||
int wipe_CalcBurn (uint8_t *burnarray, int width, int height, int density)
|
||||
{
|
||||
// This is a modified version of the fire that was once used
|
||||
// on the player setup menu.
|
||||
static int voop;
|
||||
|
||||
int a, b;
|
||||
uint8_t *from;
|
||||
|
||||
// generator
|
||||
from = &burnarray[width * height];
|
||||
b = voop;
|
||||
voop += density / 3;
|
||||
for (a = 0; a < density/8; a++)
|
||||
{
|
||||
unsigned int offs = (a+b) & (width - 1);
|
||||
unsigned int v = M_Random();
|
||||
v = MIN(from[offs] + 4 + (v & 15) + (v >> 3) + (M_Random() & 31), 255u);
|
||||
from[offs] = from[width*2 + ((offs + width*3/2) & (width - 1))] = v;
|
||||
}
|
||||
|
||||
density = MIN(density + 10, width * 7);
|
||||
|
||||
from = burnarray;
|
||||
for (b = 0; b <= height; b += 2)
|
||||
{
|
||||
uint8_t *pixel = from;
|
||||
|
||||
// special case: first pixel on line
|
||||
uint8_t *p = pixel + (width << 1);
|
||||
unsigned int top = *p + *(p + width - 1) + *(p + 1);
|
||||
unsigned int bottom = *(pixel + (width << 2));
|
||||
unsigned int c1 = (top + bottom) >> 2;
|
||||
if (c1 > 1) c1--;
|
||||
*pixel = c1;
|
||||
*(pixel + width) = (c1 + bottom) >> 1;
|
||||
pixel++;
|
||||
|
||||
// main line loop
|
||||
for (a = 1; a < width-1; a++)
|
||||
{
|
||||
// sum top pixels
|
||||
p = pixel + (width << 1);
|
||||
top = *p + *(p - 1) + *(p + 1);
|
||||
|
||||
// bottom pixel
|
||||
bottom = *(pixel + (width << 2));
|
||||
|
||||
// combine pixels
|
||||
c1 = (top + bottom) >> 2;
|
||||
if (c1 > 1) c1--;
|
||||
|
||||
// store pixels
|
||||
*pixel = c1;
|
||||
*(pixel + width) = (c1 + bottom) >> 1; // interpolate
|
||||
|
||||
// next pixel
|
||||
pixel++;
|
||||
}
|
||||
|
||||
// special case: last pixel on line
|
||||
p = pixel + (width << 1);
|
||||
top = *p + *(p - 1) + *(p - width + 1);
|
||||
bottom = *(pixel + (width << 2));
|
||||
c1 = (top + bottom) >> 2;
|
||||
if (c1 > 1) c1--;
|
||||
*pixel = c1;
|
||||
*(pixel + width) = (c1 + bottom) >> 1;
|
||||
|
||||
// next line
|
||||
from += width << 1;
|
||||
}
|
||||
|
||||
// Check for done-ness. (Every pixel with level 126 or higher counts as done.)
|
||||
for (a = width * height, from = burnarray; a != 0; --a, ++from)
|
||||
{
|
||||
if (*from < 126)
|
||||
{
|
||||
return density;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
||||
class Wiper_Crossfade : public Wiper
|
||||
{
|
||||
public:
|
||||
bool Run(int ticks) override;
|
||||
|
||||
private:
|
||||
int Clock = 0;
|
||||
};
|
||||
|
||||
class Wiper_Melt : public Wiper
|
||||
{
|
||||
public:
|
||||
Wiper_Melt();
|
||||
bool Run(int ticks) override;
|
||||
|
||||
private:
|
||||
static const int WIDTH = 320, HEIGHT = 200;
|
||||
int y[WIDTH];
|
||||
};
|
||||
|
||||
class Wiper_Burn : public Wiper
|
||||
{
|
||||
public:
|
||||
~Wiper_Burn();
|
||||
bool Run(int ticks) override;
|
||||
void SetTextures(FTexture *startscreen, FTexture *endscreen) override;
|
||||
|
||||
private:
|
||||
static const int WIDTH = 64, HEIGHT = 64;
|
||||
uint8_t BurnArray[WIDTH * (HEIGHT + 5)] = {0};
|
||||
FBurnTexture *BurnTexture = nullptr;
|
||||
int Density = 4;
|
||||
int BurnTime = 8;
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Screen wipes
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
Wiper *Wiper::Create(int type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case wipe_Burn:
|
||||
return new Wiper_Burn;
|
||||
|
||||
case wipe_Fade:
|
||||
return new Wiper_Crossfade;
|
||||
|
||||
case wipe_Melt:
|
||||
return new Wiper_Melt;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenGLFrameBuffer :: WipeCleanup
|
||||
//
|
||||
// Release any resources that were specifically created for the wipe.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
Wiper::~Wiper()
|
||||
{
|
||||
if (startScreen != nullptr) delete startScreen;
|
||||
if (endScreen != nullptr) delete endScreen;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// WIPE: CROSSFADE ---------------------------------------------------------
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenGLFrameBuffer :: Wiper_Crossfade :: Run
|
||||
//
|
||||
// Fades the old screen into the new one over 32 ticks.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool Wiper_Crossfade::Run(int ticks)
|
||||
{
|
||||
Clock += ticks;
|
||||
screen->DrawTexture(startScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
|
||||
screen->DrawTexture(endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, DTA_Alpha, clamp(Clock / 32.f, 0.f, 1.f), TAG_DONE);
|
||||
return Clock >= 32;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenGLFrameBuffer :: Wiper_Melt Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenGLFrameBuffer :: Wiper_Melt :: Run
|
||||
//
|
||||
// Melts the old screen into the new one over 32 ticks.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool Wiper_Melt::Run(int ticks)
|
||||
{
|
||||
bool done;
|
||||
screen->DrawTexture(endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
|
||||
|
||||
// Copy the old screen in vertical strips on top of the new one.
|
||||
while (ticks--)
|
||||
{
|
||||
done = true;
|
||||
for (int i = 0; i < WIDTH; i++)
|
||||
{
|
||||
if (y[i] < 0)
|
||||
{
|
||||
y[i]++;
|
||||
done = false;
|
||||
}
|
||||
else if (y[i] < HEIGHT)
|
||||
{
|
||||
int dy = (y[i] < 16) ? y[i] + 1 : 8;
|
||||
y[i] = MIN(y[i] + dy, HEIGHT);
|
||||
done = false;
|
||||
}
|
||||
if (ticks == 0)
|
||||
{
|
||||
struct {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} dpt;
|
||||
struct {
|
||||
int32_t left;
|
||||
int32_t top;
|
||||
int32_t right;
|
||||
int32_t bottom;
|
||||
} rect;
|
||||
|
||||
// Only draw for the final tick.
|
||||
// No need for optimization. Wipes won't ever be drawn with anything else.
|
||||
|
||||
int w = startScreen->GetDisplayWidth();
|
||||
int h = startScreen->GetDisplayHeight();
|
||||
dpt.x = i * w / WIDTH;
|
||||
dpt.y = MAX(0, y[i] * h / HEIGHT);
|
||||
rect.left = dpt.x;
|
||||
rect.top = 0;
|
||||
rect.right = (i + 1) * w / WIDTH;
|
||||
rect.bottom = h - dpt.y;
|
||||
if (rect.bottom > rect.top)
|
||||
{
|
||||
screen->DrawTexture(startScreen, 0, dpt.y, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_ClipLeft, rect.left, DTA_ClipRight, rect.right, DTA_Masked, false, TAG_DONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenGLFrameBuffer :: Wiper_Burn Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void Wiper_Burn::SetTextures(FTexture *startscreen, FTexture *endscreen)
|
||||
{
|
||||
startScreen = startscreen;
|
||||
endScreen = endscreen;
|
||||
BurnTexture = new FBurnTexture(WIDTH, HEIGHT);
|
||||
auto mat = FMaterial::ValidateTexture(startscreen, false);
|
||||
mat->AddTextureLayer(BurnTexture);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenGLFrameBuffer :: Wiper_Burn Destructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
Wiper_Burn::~Wiper_Burn()
|
||||
{
|
||||
if (BurnTexture != nullptr) delete BurnTexture;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OpenGLFrameBuffer :: Wiper_Burn :: Run
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool Wiper_Burn::Run(int ticks)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
BurnTexture->SystemTextures.Clean(true, true);
|
||||
const uint8_t *src = BurnArray;
|
||||
uint32_t *dest = (uint32_t *)BurnTexture->GetBuffer();
|
||||
for (int y = HEIGHT; y != 0; --y)
|
||||
{
|
||||
for (int x = WIDTH; x != 0; --x)
|
||||
{
|
||||
uint8_t s = clamp<int>((*src++)*2, 0, 255);
|
||||
*dest++ = MAKEARGB(s,255,255,255);
|
||||
}
|
||||
}
|
||||
|
||||
screen->DrawTexture(startScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
|
||||
screen->DrawTexture(endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Burn, true, DTA_Masked, false, TAG_DONE);
|
||||
|
||||
// The fire may not always stabilize, so the wipe is forced to end
|
||||
// after an arbitrary maximum time.
|
||||
return done || (BurnTime > 40);
|
||||
}
|
60
src/rendering/2D/f_wipe.h
Normal file
60
src/rendering/2D/f_wipe.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright 1993-1996 id Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// Mission start screen wipe/melt, special effects.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef __F_WIPE_H__
|
||||
#define __F_WIPE_H__
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
class FTexture;
|
||||
int wipe_CalcBurn(uint8_t *buffer, int width, int height, int density);
|
||||
|
||||
enum
|
||||
{
|
||||
wipe_None, // don't bother
|
||||
wipe_Melt, // weird screen melt
|
||||
wipe_Burn, // fade in shape of fire
|
||||
wipe_Fade, // crossfade from old to new
|
||||
wipe_NUMWIPES
|
||||
};
|
||||
|
||||
class Wiper
|
||||
{
|
||||
protected:
|
||||
FTexture *startScreen = nullptr, *endScreen = nullptr;
|
||||
public:
|
||||
virtual ~Wiper();
|
||||
virtual bool Run(int ticks) = 0;
|
||||
virtual void SetTextures(FTexture *startscreen, FTexture *endscreen)
|
||||
{
|
||||
startScreen = startscreen;
|
||||
endScreen = endscreen;
|
||||
}
|
||||
|
||||
static Wiper *Create(int type);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
677
src/rendering/2D/v_2ddrawer.cpp
Normal file
677
src/rendering/2D/v_2ddrawer.cpp
Normal file
|
@ -0,0 +1,677 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016-2018 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** v_2ddrawer.h
|
||||
** Device independent 2D draw list
|
||||
**
|
||||
**/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "doomtype.h"
|
||||
#include "templates.h"
|
||||
#include "r_utility.h"
|
||||
#include "v_video.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "vm.h"
|
||||
|
||||
EXTERN_CVAR(Float, transsouls)
|
||||
|
||||
IMPLEMENT_CLASS(DShape2D, false, false)
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DShape2D, Clear)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(DShape2D);
|
||||
PARAM_INT(which);
|
||||
if ( which&C_Verts ) self->mVertices.Clear();
|
||||
if ( which&C_Coords ) self->mCoords.Clear();
|
||||
if ( which&C_Indices ) self->mIndices.Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DShape2D, PushVertex)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(DShape2D);
|
||||
PARAM_FLOAT(x);
|
||||
PARAM_FLOAT(y);
|
||||
self->mVertices.Push(DVector2(x,y));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DShape2D, PushCoord)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(DShape2D);
|
||||
PARAM_FLOAT(u);
|
||||
PARAM_FLOAT(v);
|
||||
self->mCoords.Push(DVector2(u,v));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DShape2D, PushTriangle)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(DShape2D);
|
||||
PARAM_INT(a);
|
||||
PARAM_INT(b);
|
||||
PARAM_INT(c);
|
||||
self->mIndices.Push(a);
|
||||
self->mIndices.Push(b);
|
||||
self->mIndices.Push(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int F2DDrawer::AddCommand(const RenderCommand *data)
|
||||
{
|
||||
if (mData.Size() > 0 && data->isCompatible(mData.Last()))
|
||||
{
|
||||
// Merge with the last command.
|
||||
mData.Last().mIndexCount += data->mIndexCount;
|
||||
mData.Last().mVertCount += data->mVertCount;
|
||||
return mData.Size();
|
||||
}
|
||||
else
|
||||
{
|
||||
return mData.Push(*data);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::AddIndices(int firstvert, int count, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, count);
|
||||
int addr = mIndices.Reserve(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
mIndices[addr + i] = firstvert + va_arg(ap, int);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// SetStyle
|
||||
//
|
||||
// Patterned after R_SetPatchStyle.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool F2DDrawer::SetStyle(FTexture *tex, DrawParms &parms, PalEntry &vertexcolor, RenderCommand &quad)
|
||||
{
|
||||
FRenderStyle style = parms.style;
|
||||
float alpha;
|
||||
bool stencilling;
|
||||
|
||||
if (style.Flags & STYLEF_TransSoulsAlpha)
|
||||
{
|
||||
alpha = transsouls;
|
||||
}
|
||||
else if (style.Flags & STYLEF_Alpha1)
|
||||
{
|
||||
alpha = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
alpha = clamp(parms.Alpha, 0.f, 1.f);
|
||||
}
|
||||
|
||||
style.CheckFuzz();
|
||||
if (style.BlendOp == STYLEOP_Shadow || style.BlendOp == STYLEOP_Fuzz)
|
||||
{
|
||||
style = LegacyRenderStyles[STYLE_TranslucentStencil];
|
||||
alpha = 0.3f;
|
||||
parms.fillcolor = 0;
|
||||
}
|
||||
else if (style.BlendOp == STYLEOP_FuzzOrAdd)
|
||||
{
|
||||
style.BlendOp = STYLEOP_Add;
|
||||
}
|
||||
else if (style.BlendOp == STYLEOP_FuzzOrSub)
|
||||
{
|
||||
style.BlendOp = STYLEOP_Sub;
|
||||
}
|
||||
else if (style.BlendOp == STYLEOP_FuzzOrRevSub)
|
||||
{
|
||||
style.BlendOp = STYLEOP_RevSub;
|
||||
}
|
||||
|
||||
stencilling = false;
|
||||
|
||||
if (style.Flags & STYLEF_InvertOverlay)
|
||||
{
|
||||
// Only the overlay color is inverted, not the overlay alpha.
|
||||
parms.colorOverlay.r = 255 - parms.colorOverlay.r;
|
||||
parms.colorOverlay.g = 255 - parms.colorOverlay.g;
|
||||
parms.colorOverlay.b = 255 - parms.colorOverlay.b;
|
||||
}
|
||||
|
||||
SetColorOverlay(parms.colorOverlay, alpha, vertexcolor, quad.mColor1);
|
||||
|
||||
if (style.Flags & STYLEF_ColorIsFixed)
|
||||
{
|
||||
if (style.Flags & STYLEF_InvertSource)
|
||||
{ // Since the source color is a constant, we can invert it now
|
||||
// without spending time doing it in the shader.
|
||||
parms.fillcolor.r = 255 - parms.fillcolor.r;
|
||||
parms.fillcolor.g = 255 - parms.fillcolor.g;
|
||||
parms.fillcolor.b = 255 - parms.fillcolor.b;
|
||||
style.Flags &= ~STYLEF_InvertSource;
|
||||
}
|
||||
if (parms.desaturate > 0)
|
||||
{
|
||||
// Desaturation can also be computed here without having to do it in the shader.
|
||||
auto gray = parms.fillcolor.Luminance();
|
||||
auto notgray = 255 - gray;
|
||||
parms.fillcolor.r = uint8_t((parms.fillcolor.r * notgray + gray * 255) / 255);
|
||||
parms.fillcolor.g = uint8_t((parms.fillcolor.g * notgray + gray * 255) / 255);
|
||||
parms.fillcolor.b = uint8_t((parms.fillcolor.b * notgray + gray * 255) / 255);
|
||||
parms.desaturate = 0;
|
||||
}
|
||||
|
||||
// Set up the color mod to replace the color from the image data.
|
||||
vertexcolor.r = parms.fillcolor.r;
|
||||
vertexcolor.g = parms.fillcolor.g;
|
||||
vertexcolor.b = parms.fillcolor.b;
|
||||
|
||||
if (style.Flags & STYLEF_RedIsAlpha)
|
||||
{
|
||||
quad.mDrawMode = TM_ALPHATEXTURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
quad.mDrawMode = TM_STENCIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (style.Flags & STYLEF_RedIsAlpha)
|
||||
{
|
||||
quad.mDrawMode = TM_ALPHATEXTURE;
|
||||
}
|
||||
else if (style.Flags & STYLEF_InvertSource)
|
||||
{
|
||||
quad.mDrawMode = TM_INVERSE;
|
||||
}
|
||||
|
||||
if (parms.specialcolormap != nullptr)
|
||||
{ // draw with an invulnerability or similar colormap.
|
||||
|
||||
auto scm = parms.specialcolormap;
|
||||
|
||||
quad.mSpecialColormap[0] = PalEntry(255, int(scm->ColorizeStart[0] * 127.5f), int(scm->ColorizeStart[1] * 127.5f), int(scm->ColorizeStart[2] * 127.5f));
|
||||
quad.mSpecialColormap[1] = PalEntry(255, int(scm->ColorizeEnd[0] * 127.5f), int(scm->ColorizeEnd[1] * 127.5f), int(scm->ColorizeEnd[2] * 127.5f));
|
||||
quad.mColor1 = 0; // this disables the color overlay.
|
||||
}
|
||||
quad.mDesaturate = parms.desaturate;
|
||||
}
|
||||
// apply the element's own color. This is being blended with anything that came before.
|
||||
vertexcolor = PalEntry((vertexcolor.a * parms.color.a) / 255, (vertexcolor.r * parms.color.r) / 255, (vertexcolor.g * parms.color.g) / 255, (vertexcolor.b * parms.color.b) / 255);
|
||||
|
||||
if (!parms.masked)
|
||||
{
|
||||
// For TM_ALPHATEXTURE and TM_STENCIL the mask cannot be turned off because it would not yield a usable result.
|
||||
if (quad.mDrawMode == TM_NORMAL) quad.mDrawMode = TM_OPAQUE;
|
||||
else if (quad.mDrawMode == TM_INVERSE) quad.mDrawMode = TM_INVERTOPAQUE;
|
||||
}
|
||||
quad.mRenderStyle = parms.style; // this contains the blend mode and blend equation settings.
|
||||
if (parms.burn) quad.mFlags |= DTF_Burn;
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Draws a texture
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::SetColorOverlay(PalEntry color, float alpha, PalEntry &vertexcolor, PalEntry &overlaycolor)
|
||||
{
|
||||
if (color.a != 0 && (color & 0xffffff) != 0)
|
||||
{
|
||||
// overlay color uses premultiplied alpha.
|
||||
int a = color.a * 256 / 255;
|
||||
overlaycolor.r = (color.r * a) >> 8;
|
||||
overlaycolor.g = (color.g * a) >> 8;
|
||||
overlaycolor.b = (color.b * a) >> 8;
|
||||
overlaycolor.a = 0; // The overlay gets added on top of the texture data so to preserve the pixel's alpha this must be 0.
|
||||
}
|
||||
else
|
||||
{
|
||||
overlaycolor = 0;
|
||||
}
|
||||
// Vertex intensity is the inverse of the overlay so that the shader can do a simple addition to combine them.
|
||||
uint8_t light = 255 - color.a;
|
||||
vertexcolor = PalEntry(int(alpha * 255), light, light, light);
|
||||
|
||||
// The real color gets multiplied into vertexcolor later.
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Draws a texture
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::AddTexture(FTexture *img, DrawParms &parms)
|
||||
{
|
||||
if (parms.style.BlendOp == STYLEOP_None) return; // not supposed to be drawn.
|
||||
|
||||
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;
|
||||
double u1, v1, u2, v2;
|
||||
PalEntry vertexcolor;
|
||||
|
||||
RenderCommand dg;
|
||||
|
||||
dg.mType = DrawTypeTriangles;
|
||||
dg.mVertCount = 4;
|
||||
dg.mTexture = img;
|
||||
if (img->isWarped()) dg.mFlags |= DTF_Wrap;
|
||||
|
||||
dg.mTranslation = 0;
|
||||
SetStyle(img, parms, vertexcolor, dg);
|
||||
|
||||
if (!img->isHardwareCanvas() && parms.remap != nullptr && !parms.remap->Inactive)
|
||||
{
|
||||
dg.mTranslation = parms.remap;
|
||||
}
|
||||
u1 = parms.srcx;
|
||||
v1 = parms.srcy;
|
||||
u2 = parms.srcx + parms.srcwidth;
|
||||
v2 = parms.srcy + parms.srcheight;
|
||||
|
||||
if (parms.flipX)
|
||||
std::swap(u1, u2);
|
||||
|
||||
if (parms.flipY)
|
||||
std::swap(v1, v2);
|
||||
|
||||
// This is crap. Only kept for backwards compatibility with scripts that may have used it.
|
||||
// Note that this only works for unflipped full textures.
|
||||
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);
|
||||
}
|
||||
|
||||
if (x < (double)parms.lclip || y < (double)parms.uclip || x + w >(double)parms.rclip || y + h >(double)parms.dclip)
|
||||
{
|
||||
dg.mScissor[0] = parms.lclip;
|
||||
dg.mScissor[1] = parms.uclip;
|
||||
dg.mScissor[2] = parms.rclip;
|
||||
dg.mScissor[3] = parms.dclip;
|
||||
dg.mFlags |= DTF_Scissor;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(dg.mScissor, 0, sizeof(dg.mScissor));
|
||||
}
|
||||
|
||||
dg.mVertCount = 4;
|
||||
dg.mVertIndex = (int)mVertices.Reserve(4);
|
||||
TwoDVertex *ptr = &mVertices[dg.mVertIndex];
|
||||
ptr->Set(x, y, 0, u1, v1, vertexcolor); ptr++;
|
||||
ptr->Set(x, y + h, 0, u1, v2, vertexcolor); ptr++;
|
||||
ptr->Set(x + w, y, 0, u2, v1, vertexcolor); ptr++;
|
||||
ptr->Set(x + w, y + h, 0, u2, v2, vertexcolor); ptr++;
|
||||
dg.mIndexIndex = mIndices.Size();
|
||||
dg.mIndexCount += 6;
|
||||
AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2);
|
||||
AddCommand(&dg);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::AddShape( FTexture *img, DShape2D *shape, DrawParms &parms )
|
||||
{
|
||||
if (parms.style.BlendOp == STYLEOP_None) return; // not supposed to be drawn.
|
||||
|
||||
PalEntry vertexcolor;
|
||||
|
||||
RenderCommand dg;
|
||||
|
||||
dg.mType = DrawTypeTriangles;
|
||||
dg.mVertCount = shape->mVertices.Size();
|
||||
dg.mFlags |= DTF_Wrap;
|
||||
dg.mTexture = img;
|
||||
|
||||
dg.mTranslation = 0;
|
||||
SetStyle(img, parms, vertexcolor, dg);
|
||||
|
||||
if (!img->isHardwareCanvas() && parms.remap != nullptr && !parms.remap->Inactive)
|
||||
dg.mTranslation = parms.remap;
|
||||
|
||||
double minx = 16383, miny = 16383, maxx = -16384, maxy = -16384;
|
||||
for ( int i=0; i<dg.mVertCount; i++ )
|
||||
{
|
||||
if ( shape->mVertices[i].X < minx ) minx = shape->mVertices[i].X;
|
||||
if ( shape->mVertices[i].Y < miny ) miny = shape->mVertices[i].Y;
|
||||
if ( shape->mVertices[i].X > maxx ) maxx = shape->mVertices[i].X;
|
||||
if ( shape->mVertices[i].Y > maxy ) maxy = shape->mVertices[i].Y;
|
||||
}
|
||||
if (minx < (double)parms.lclip || miny < (double)parms.uclip || maxx >(double)parms.rclip || maxy >(double)parms.dclip)
|
||||
{
|
||||
dg.mScissor[0] = parms.lclip;
|
||||
dg.mScissor[1] = parms.uclip;
|
||||
dg.mScissor[2] = parms.rclip;
|
||||
dg.mScissor[3] = parms.dclip;
|
||||
dg.mFlags |= DTF_Scissor;
|
||||
}
|
||||
else
|
||||
memset(dg.mScissor, 0, sizeof(dg.mScissor));
|
||||
|
||||
dg.mVertIndex = (int)mVertices.Reserve(dg.mVertCount);
|
||||
TwoDVertex *ptr = &mVertices[dg.mVertIndex];
|
||||
for ( int i=0; i<dg.mVertCount; i++ )
|
||||
ptr[i].Set(shape->mVertices[i].X, shape->mVertices[i].Y, 0, shape->mCoords[i].X, shape->mCoords[i].Y, vertexcolor);
|
||||
dg.mIndexIndex = mIndices.Size();
|
||||
dg.mIndexCount += shape->mIndices.Size();
|
||||
for ( int i=0; i<int(shape->mIndices.Size()); i+=3 )
|
||||
AddIndices(dg.mVertIndex, 3, shape->mIndices[i], shape->mIndices[i+1], shape->mIndices[i+2]);
|
||||
AddCommand(&dg);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::AddPoly(FTexture *texture, FVector2 *points, int npoints,
|
||||
double originx, double originy, double scalex, double scaley,
|
||||
DAngle rotation, const FColormap &colormap, PalEntry flatcolor, int lightlevel,
|
||||
uint32_t *indices, size_t indexcount)
|
||||
{
|
||||
// Use an equation similar to player sprites to determine shade
|
||||
|
||||
// Convert a light level into an unbounded colormap index (shade).
|
||||
// Why the +12? I wish I knew, but experimentation indicates it
|
||||
// is necessary in order to best reproduce Doom's original lighting.
|
||||
double fadelevel;
|
||||
|
||||
if (vid_rendermode != 4 || primaryLevel->lightMode == ELightMode::Doom || primaryLevel->lightMode == ELightMode::ZDoomSoftware || primaryLevel->lightMode == ELightMode::DoomSoftware)
|
||||
{
|
||||
double map = (NUMCOLORMAPS * 2.) - ((lightlevel + 12) * (NUMCOLORMAPS / 128.));
|
||||
fadelevel = clamp((map - 12) / NUMCOLORMAPS, 0.0, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The hardware renderer's light modes 0, 1 and 4 use a linear light scale which must be used here as well. Otherwise the automap gets too dark.
|
||||
fadelevel = 1. - clamp(lightlevel, 0, 255) / 255.f;
|
||||
}
|
||||
|
||||
RenderCommand poly;
|
||||
|
||||
poly.mType = DrawTypeTriangles;
|
||||
poly.mTexture = texture;
|
||||
poly.mRenderStyle = DefaultRenderStyle();
|
||||
poly.mFlags |= DTF_Wrap;
|
||||
poly.mDesaturate = colormap.Desaturation;
|
||||
|
||||
PalEntry color0;
|
||||
double invfade = 1. - fadelevel;
|
||||
|
||||
color0.r = uint8_t(colormap.LightColor.r * invfade);
|
||||
color0.g = uint8_t(colormap.LightColor.g * invfade);
|
||||
color0.b = uint8_t(colormap.LightColor.b * invfade);
|
||||
color0.a = 255;
|
||||
|
||||
poly.mColor1.a = 0;
|
||||
poly.mColor1.r = uint8_t(colormap.FadeColor.r * fadelevel);
|
||||
poly.mColor1.g = uint8_t(colormap.FadeColor.g * fadelevel);
|
||||
poly.mColor1.b = uint8_t(colormap.FadeColor.b * fadelevel);
|
||||
|
||||
bool dorotate = rotation != 0;
|
||||
|
||||
float cosrot = (float)cos(rotation.Radians());
|
||||
float sinrot = (float)sin(rotation.Radians());
|
||||
|
||||
float uscale = float(1.f / (texture->GetDisplayWidth() * scalex));
|
||||
float vscale = float(1.f / (texture->GetDisplayHeight() * scaley));
|
||||
float ox = float(originx);
|
||||
float oy = float(originy);
|
||||
|
||||
poly.mVertCount = npoints;
|
||||
poly.mVertIndex = (int)mVertices.Reserve(npoints);
|
||||
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, color0);
|
||||
}
|
||||
poly.mIndexIndex = mIndices.Size();
|
||||
poly.mIndexCount += (npoints - 2) * 3;
|
||||
|
||||
if (indices == nullptr || indexcount == 0)
|
||||
{
|
||||
for (int i = 2; i < npoints; ++i)
|
||||
{
|
||||
AddIndices(poly.mVertIndex, 3, 0, i - 1, i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int addr = mIndices.Reserve(indexcount);
|
||||
for (size_t i = 0; i < indexcount; i++)
|
||||
{
|
||||
mIndices[addr + i] = addr + indices[i];
|
||||
}
|
||||
}
|
||||
|
||||
AddCommand(&poly);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin)
|
||||
{
|
||||
float fU1, fU2, fV1, fV2;
|
||||
|
||||
RenderCommand dg;
|
||||
|
||||
dg.mType = DrawTypeTriangles;
|
||||
dg.mRenderStyle = DefaultRenderStyle();
|
||||
dg.mTexture = src;
|
||||
dg.mVertCount = 4;
|
||||
dg.mTexture = src;
|
||||
dg.mFlags = DTF_Wrap;
|
||||
|
||||
// scaling is not used here.
|
||||
if (!local_origin)
|
||||
{
|
||||
fU1 = float(left) / src->GetDisplayWidth();
|
||||
fV1 = float(top) / src->GetDisplayHeight();
|
||||
fU2 = float(right) / src->GetDisplayWidth();
|
||||
fV2 = float(bottom) / src->GetDisplayHeight();
|
||||
}
|
||||
else
|
||||
{
|
||||
fU1 = 0;
|
||||
fV1 = 0;
|
||||
fU2 = float(right - left) / src->GetDisplayWidth();
|
||||
fV2 = float(bottom - top) / src->GetDisplayHeight();
|
||||
}
|
||||
dg.mVertIndex = (int)mVertices.Reserve(4);
|
||||
auto ptr = &mVertices[dg.mVertIndex];
|
||||
|
||||
ptr->Set(left, top, 0, fU1, fV1, 0xffffffff); ptr++;
|
||||
ptr->Set(left, bottom, 0, fU1, fV2, 0xffffffff); ptr++;
|
||||
ptr->Set(right, top, 0, fU2, fV1, 0xffffffff); ptr++;
|
||||
ptr->Set(right, bottom, 0, fU2, fV2, 0xffffffff); ptr++;
|
||||
dg.mIndexIndex = mIndices.Size();
|
||||
dg.mIndexCount += 6;
|
||||
AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2);
|
||||
AddCommand(&dg);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void F2DDrawer::AddColorOnlyQuad(int x1, int y1, int w, int h, PalEntry color, FRenderStyle *style)
|
||||
{
|
||||
RenderCommand dg;
|
||||
|
||||
dg.mType = DrawTypeTriangles;
|
||||
dg.mVertCount = 4;
|
||||
dg.mVertIndex = (int)mVertices.Reserve(4);
|
||||
dg.mRenderStyle = style? *style : LegacyRenderStyles[STYLE_Translucent];
|
||||
auto 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, 0, 0, 0, color); ptr++;
|
||||
ptr->Set(x1 + w, y1 + h, 0, 0, 0, color); ptr++;
|
||||
dg.mIndexIndex = mIndices.Size();
|
||||
dg.mIndexCount += 6;
|
||||
AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2);
|
||||
AddCommand(&dg);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color, uint8_t alpha)
|
||||
{
|
||||
PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor];
|
||||
p.a = alpha;
|
||||
|
||||
RenderCommand dg;
|
||||
|
||||
dg.mType = DrawTypeLines;
|
||||
dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent];
|
||||
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);
|
||||
AddCommand(&dg);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::AddThickLine(int x1, int y1, int x2, int y2, double thickness, uint32_t color, uint8_t alpha)
|
||||
{
|
||||
PalEntry p = (PalEntry)color;
|
||||
p.a = alpha;
|
||||
|
||||
DVector2 point0(x1, y1);
|
||||
DVector2 point1(x2, y2);
|
||||
|
||||
DVector2 delta = point1 - point0;
|
||||
DVector2 perp(-delta.Y, delta.X);
|
||||
perp.MakeUnit();
|
||||
perp *= thickness / 2;
|
||||
|
||||
DVector2 corner0 = point0 + perp;
|
||||
DVector2 corner1 = point0 - perp;
|
||||
DVector2 corner2 = point1 + perp;
|
||||
DVector2 corner3 = point1 - perp;
|
||||
|
||||
RenderCommand dg;
|
||||
|
||||
dg.mType = DrawTypeTriangles;
|
||||
dg.mVertCount = 4;
|
||||
dg.mVertIndex = (int)mVertices.Reserve(4);
|
||||
dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent];
|
||||
auto ptr = &mVertices[dg.mVertIndex];
|
||||
ptr->Set(corner0.X, corner0.Y, 0, 0, 0, p); ptr++;
|
||||
ptr->Set(corner1.X, corner1.Y, 0, 0, 0, p); ptr++;
|
||||
ptr->Set(corner2.X, corner2.Y, 0, 0, 0, p); ptr++;
|
||||
ptr->Set(corner3.X, corner3.Y, 0, 0, 0, p); ptr++;
|
||||
dg.mIndexIndex = mIndices.Size();
|
||||
dg.mIndexCount += 6;
|
||||
AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2);
|
||||
AddCommand(&dg);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32_t color)
|
||||
{
|
||||
PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor];
|
||||
p.a = 255;
|
||||
|
||||
RenderCommand dg;
|
||||
|
||||
dg.mType = DrawTypePoints;
|
||||
dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent];
|
||||
dg.mVertCount = 1;
|
||||
dg.mVertIndex = (int)mVertices.Reserve(1);
|
||||
mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p);
|
||||
AddCommand(&dg);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void F2DDrawer::Clear()
|
||||
{
|
||||
mVertices.Clear();
|
||||
mIndices.Clear();
|
||||
mData.Clear();
|
||||
}
|
150
src/rendering/2D/v_2ddrawer.h
Normal file
150
src/rendering/2D/v_2ddrawer.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
#ifndef __2DDRAWER_H
|
||||
#define __2DDRAWER_H
|
||||
|
||||
#include "tarray.h"
|
||||
#include "textures.h"
|
||||
#include "v_palette.h"
|
||||
#include "r_data/renderstyle.h"
|
||||
#include "r_data/colormaps.h"
|
||||
|
||||
struct DrawParms;
|
||||
|
||||
// intermediate struct for shape drawing
|
||||
|
||||
enum EClearWhich
|
||||
{
|
||||
C_Verts = 1,
|
||||
C_Coords = 2,
|
||||
C_Indices = 4,
|
||||
};
|
||||
|
||||
class DShape2D : public DObject
|
||||
{
|
||||
|
||||
DECLARE_CLASS(DShape2D,DObject)
|
||||
public:
|
||||
TArray<int> mIndices;
|
||||
TArray<DVector2> mVertices;
|
||||
TArray<DVector2> mCoords;
|
||||
};
|
||||
|
||||
class F2DDrawer
|
||||
{
|
||||
public:
|
||||
|
||||
enum EDrawType : uint8_t
|
||||
{
|
||||
DrawTypeTriangles,
|
||||
DrawTypeLines,
|
||||
DrawTypePoints,
|
||||
};
|
||||
|
||||
enum ETextureFlags : uint8_t
|
||||
{
|
||||
DTF_Wrap = 1,
|
||||
DTF_Scissor = 2,
|
||||
DTF_Burn = 4,
|
||||
};
|
||||
|
||||
|
||||
// This vertex type is hardware independent and needs conversion when put into a buffer.
|
||||
struct TwoDVertex
|
||||
{
|
||||
float x, y, z;
|
||||
float u, v;
|
||||
PalEntry color0;
|
||||
|
||||
void Set(float xx, float yy, float zz)
|
||||
{
|
||||
x = xx;
|
||||
z = zz;
|
||||
y = yy;
|
||||
u = 0;
|
||||
v = 0;
|
||||
color0 = 0;
|
||||
}
|
||||
|
||||
void Set(double xx, double yy, double zz, double uu, double vv, PalEntry col)
|
||||
{
|
||||
x = (float)xx;
|
||||
z = (float)zz;
|
||||
y = (float)yy;
|
||||
u = (float)uu;
|
||||
v = (float)vv;
|
||||
color0 = col;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct RenderCommand
|
||||
{
|
||||
EDrawType mType;
|
||||
int mVertIndex;
|
||||
int mVertCount;
|
||||
int mIndexIndex;
|
||||
int mIndexCount;
|
||||
|
||||
FTexture *mTexture;
|
||||
FRemapTable *mTranslation;
|
||||
PalEntry mSpecialColormap[2];
|
||||
int mScissor[4];
|
||||
int mDesaturate;
|
||||
FRenderStyle mRenderStyle;
|
||||
PalEntry mColor1; // Overlay color
|
||||
ETexMode mDrawMode;
|
||||
uint8_t mFlags;
|
||||
|
||||
RenderCommand()
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
// If these fields match, two draw commands can be batched.
|
||||
bool isCompatible(const RenderCommand &other) const
|
||||
{
|
||||
return mTexture == other.mTexture &&
|
||||
mType == other.mType &&
|
||||
mTranslation == other.mTranslation &&
|
||||
mSpecialColormap == other.mSpecialColormap &&
|
||||
!memcmp(mScissor, other.mScissor, sizeof(mScissor)) &&
|
||||
mDesaturate == other.mDesaturate &&
|
||||
mRenderStyle == other.mRenderStyle &&
|
||||
mDrawMode == other.mDrawMode &&
|
||||
mFlags == other.mFlags &&
|
||||
mColor1 == other.mColor1;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
TArray<int> mIndices;
|
||||
TArray<TwoDVertex> mVertices;
|
||||
TArray<RenderCommand> mData;
|
||||
|
||||
int AddCommand(const RenderCommand *data);
|
||||
void AddIndices(int firstvert, int count, ...);
|
||||
bool SetStyle(FTexture *tex, DrawParms &parms, PalEntry &color0, RenderCommand &quad);
|
||||
void SetColorOverlay(PalEntry color, float alpha, PalEntry &vertexcolor, PalEntry &overlaycolor);
|
||||
|
||||
public:
|
||||
void AddTexture(FTexture *img, DrawParms &parms);
|
||||
void AddShape(FTexture *img, DShape2D *shape, DrawParms &parms);
|
||||
void AddPoly(FTexture *texture, FVector2 *points, int npoints,
|
||||
double originx, double originy, double scalex, double scaley,
|
||||
DAngle rotation, const FColormap &colormap, PalEntry flatcolor, int lightlevel, uint32_t *indices, size_t indexcount);
|
||||
void AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin);
|
||||
|
||||
void AddColorOnlyQuad(int left, int top, int width, int height, PalEntry color, FRenderStyle *style);
|
||||
|
||||
void AddDim(PalEntry color, float damount, int x1, int y1, int w, int h);
|
||||
void AddClear(int left, int top, int right, int bottom, int palcolor, uint32_t color);
|
||||
|
||||
|
||||
void AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color, uint8_t alpha = 255);
|
||||
void AddThickLine(int x1, int y1, int x2, int y2, double thickness, uint32_t color, uint8_t alpha = 255);
|
||||
void AddPixel(int x1, int y1, int palcolor, uint32_t color);
|
||||
|
||||
void Clear();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
209
src/rendering/2D/v_blend.cpp
Normal file
209
src/rendering/2D/v_blend.cpp
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
** v_blend.cpp
|
||||
** Screen blending stuff
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "templates.h"
|
||||
#include "sbar.h"
|
||||
#include "c_cvars.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "v_video.h"
|
||||
#include "s_sound.h"
|
||||
#include "gi.h"
|
||||
#include "d_player.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "vm.h"
|
||||
|
||||
CVAR( Float, blood_fade_scalar, 1.0f, CVAR_ARCHIVE ) // [SP] Pulled from Skulltag - changed default from 0.5 to 1.0
|
||||
CVAR( Float, pickup_fade_scalar, 1.0f, CVAR_ARCHIVE ) // [SP] Uses same logic as blood_fade_scalar except for pickups
|
||||
|
||||
// [RH] Amount of red flash for up to 114 damage points. Calculated by hand
|
||||
// using a logarithmic scale and my trusty HP48G.
|
||||
static uint8_t DamageToAlpha[114] =
|
||||
{
|
||||
0, 8, 16, 23, 30, 36, 42, 47, 53, 58, 62, 67, 71, 75, 79,
|
||||
83, 87, 90, 94, 97, 100, 103, 107, 109, 112, 115, 118, 120, 123, 125,
|
||||
128, 130, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157,
|
||||
159, 160, 162, 164, 165, 167, 169, 170, 172, 173, 175, 176, 178, 179, 181,
|
||||
182, 183, 185, 186, 187, 189, 190, 191, 192, 194, 195, 196, 197, 198, 200,
|
||||
201, 202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 215, 216,
|
||||
217, 218, 219, 220, 221, 221, 222, 223, 224, 225, 226, 227, 228, 229, 229,
|
||||
230, 231, 232, 233, 234, 235, 235, 236, 237
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
SV_AddBlend
|
||||
[RH] This is from Q2.
|
||||
=============
|
||||
*/
|
||||
void V_AddBlend (float r, float g, float b, float a, float v_blend[4])
|
||||
{
|
||||
float a2, a3;
|
||||
|
||||
if (a <= 0)
|
||||
return;
|
||||
a2 = v_blend[3] + (1-v_blend[3])*a; // new total alpha
|
||||
a3 = v_blend[3]/a2; // fraction of color from old
|
||||
|
||||
v_blend[0] = v_blend[0]*a3 + r*(1-a3);
|
||||
v_blend[1] = v_blend[1]*a3 + g*(1-a3);
|
||||
v_blend[2] = v_blend[2]*a3 + b*(1-a3);
|
||||
v_blend[3] = a2;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// BlendView
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void V_AddPlayerBlend (player_t *CPlayer, float blend[4], float maxinvalpha, int maxpainblend)
|
||||
{
|
||||
int cnt;
|
||||
auto Level = CPlayer->mo->Level;
|
||||
|
||||
// [RH] All powerups can affect the screen blending now
|
||||
for (AActor *item = CPlayer->mo->Inventory; item != NULL; item = item->Inventory)
|
||||
{
|
||||
PalEntry color = 0;
|
||||
|
||||
IFVIRTUALPTRNAME(item, NAME_Inventory, GetBlend)
|
||||
{
|
||||
VMValue params[1] = { item };
|
||||
VMReturn ret((int*)&color.d);
|
||||
VMCall(func, params, 1, &ret, 1);
|
||||
}
|
||||
|
||||
|
||||
if (color.a != 0)
|
||||
{
|
||||
V_AddBlend (color.r/255.f, color.g/255.f, color.b/255.f, color.a/255.f, blend);
|
||||
if (color.a/255.f > maxinvalpha) maxinvalpha = color.a/255.f;
|
||||
}
|
||||
}
|
||||
if (CPlayer->bonuscount)
|
||||
{
|
||||
cnt = CPlayer->bonuscount << 3;
|
||||
|
||||
// [SP] Allow player to tone down intensity of pickup flash.
|
||||
cnt = (int)( cnt * pickup_fade_scalar );
|
||||
|
||||
V_AddBlend (RPART(gameinfo.pickupcolor)/255.f, GPART(gameinfo.pickupcolor)/255.f,
|
||||
BPART(gameinfo.pickupcolor)/255.f, cnt > 128 ? 0.5f : cnt / 255.f, blend);
|
||||
}
|
||||
|
||||
PalEntry painFlash = 0;
|
||||
IFVIRTUALPTRNAME(CPlayer->mo, NAME_PlayerPawn, GetPainFlash)
|
||||
{
|
||||
VMValue param = CPlayer->mo;
|
||||
VMReturn ret((int*)&painFlash.d);
|
||||
VMCall(func, ¶m, 1, &ret, 1);
|
||||
}
|
||||
|
||||
if (painFlash.a != 0)
|
||||
{
|
||||
cnt = DamageToAlpha[MIN (113, CPlayer->damagecount * painFlash.a / 255)];
|
||||
|
||||
// [BC] Allow users to tone down the intensity of the blood on the screen.
|
||||
cnt = (int)( cnt * blood_fade_scalar );
|
||||
|
||||
if (cnt)
|
||||
{
|
||||
if (cnt > maxpainblend)
|
||||
cnt = maxpainblend;
|
||||
|
||||
V_AddBlend (painFlash.r / 255.f, painFlash.g / 255.f, painFlash.b / 255.f, cnt / 255.f, blend);
|
||||
}
|
||||
}
|
||||
|
||||
// Unlike Doom, I did not have any utility source to look at to find the
|
||||
// exact numbers to use here, so I've had to guess by looking at how they
|
||||
// affect the white color in Hexen's palette and picking an alpha value
|
||||
// that seems reasonable.
|
||||
// [Gez] The exact values could be obtained by looking how they affect
|
||||
// each color channel in Hexen's palette.
|
||||
|
||||
if (CPlayer->poisoncount)
|
||||
{
|
||||
cnt = MIN (CPlayer->poisoncount, 64);
|
||||
if (paletteflash & PF_POISON)
|
||||
{
|
||||
V_AddBlend(44/255.f, 92/255.f, 36/255.f, ((cnt + 7) >> 3) * 0.1f, blend);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_AddBlend (0.04f, 0.2571f, 0.f, cnt/93.2571428571f, blend);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (CPlayer->hazardcount)
|
||||
{
|
||||
if (paletteflash & PF_HAZARD)
|
||||
{
|
||||
if (CPlayer->hazardcount > 16*TICRATE || (CPlayer->hazardcount & 8))
|
||||
{
|
||||
float r = ((Level->hazardflash & 0xff0000) >> 16) / 255.f;
|
||||
float g = ((Level->hazardflash & 0xff00) >> 8) / 255.f;
|
||||
float b = ((Level->hazardflash & 0xff)) / 255.f;
|
||||
V_AddBlend (r, g, b, 0.125f, blend);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cnt= MIN(CPlayer->hazardcount/8, 64);
|
||||
float r = ((Level->hazardcolor & 0xff0000) >> 16) / 255.f;
|
||||
float g = ((Level->hazardcolor & 0xff00) >> 8) / 255.f;
|
||||
float b = ((Level->hazardcolor & 0xff)) / 255.f;
|
||||
V_AddBlend (r, g, b, cnt/93.2571428571f, blend);
|
||||
}
|
||||
}
|
||||
|
||||
if (CPlayer->mo->DamageType == NAME_Ice)
|
||||
{
|
||||
if (paletteflash & PF_ICE)
|
||||
{
|
||||
V_AddBlend(0.f, 0.f, 224/255.f, 0.5f, blend);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_AddBlend (0.25f, 0.25f, 0.853f, 0.4f, blend);
|
||||
}
|
||||
}
|
||||
|
||||
// cap opacity if desired
|
||||
if (blend[3] > maxinvalpha) blend[3] = maxinvalpha;
|
||||
}
|
1495
src/rendering/2D/v_draw.cpp
Normal file
1495
src/rendering/2D/v_draw.cpp
Normal file
File diff suppressed because it is too large
Load diff
376
src/rendering/2D/v_drawtext.cpp
Normal file
376
src/rendering/2D/v_drawtext.cpp
Normal file
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
** v_text.cpp
|
||||
** Draws text to a canvas. Also has a text line-breaker thingy.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#include "v_text.h"
|
||||
|
||||
|
||||
#include "v_video.h"
|
||||
#include "w_wad.h"
|
||||
#include "image.h"
|
||||
#include "textures/formats/multipatchtexture.h"
|
||||
|
||||
#include "gstrings.h"
|
||||
#include "vm.h"
|
||||
#include "serializer.h"
|
||||
|
||||
|
||||
int ListGetInt(VMVa_List &tags);
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Create a texture from a text in a given font.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FTexture * BuildTextTexture(FFont *font, const char *string, int textcolor)
|
||||
{
|
||||
int w;
|
||||
const uint8_t *ch;
|
||||
int cx;
|
||||
int cy;
|
||||
FRemapTable *range;
|
||||
int kerning;
|
||||
FTexture *pic;
|
||||
|
||||
kerning = font->GetDefaultKerning();
|
||||
|
||||
ch = (const uint8_t *)string;
|
||||
cx = 0;
|
||||
cy = 0;
|
||||
|
||||
|
||||
IntRect box;
|
||||
|
||||
while (auto c = GetCharFromString(ch))
|
||||
{
|
||||
if (c == TEXTCOLOR_ESCAPE)
|
||||
{
|
||||
// Here we only want to measure the texture so just parse over the color.
|
||||
V_ParseFontColor(ch, 0, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
cx = 0;
|
||||
cy += font->GetHeight();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nullptr != (pic = font->GetChar(c, CR_UNTRANSLATED, &w, nullptr)))
|
||||
{
|
||||
auto img = pic->GetImage();
|
||||
auto offsets = img->GetOffsets();
|
||||
int x = cx - offsets.first;
|
||||
int y = cy - offsets.second;
|
||||
int ww = img->GetWidth();
|
||||
int h = img->GetHeight();
|
||||
|
||||
box.AddToRect(x, y);
|
||||
box.AddToRect(x + ww, y + h);
|
||||
}
|
||||
cx += (w + kerning);
|
||||
}
|
||||
|
||||
cx = -box.left;
|
||||
cy = -box.top;
|
||||
|
||||
TArray<TexPart> part(strlen(string));
|
||||
|
||||
while (auto c = GetCharFromString(ch))
|
||||
{
|
||||
if (c == TEXTCOLOR_ESCAPE)
|
||||
{
|
||||
EColorRange newcolor = V_ParseFontColor(ch, textcolor, textcolor);
|
||||
if (newcolor != CR_UNDEFINED)
|
||||
{
|
||||
range = font->GetColorTranslation(newcolor);
|
||||
textcolor = newcolor;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
cx = 0;
|
||||
cy += font->GetHeight();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nullptr != (pic = font->GetChar(c, textcolor, &w, nullptr)))
|
||||
{
|
||||
auto img = pic->GetImage();
|
||||
auto offsets = img->GetOffsets();
|
||||
int x = cx - offsets.first;
|
||||
int y = cy - offsets.second;
|
||||
|
||||
auto &tp = part[part.Reserve(1)];
|
||||
|
||||
tp.OriginX = x;
|
||||
tp.OriginY = y;
|
||||
tp.Image = img;
|
||||
tp.Translation = range;
|
||||
}
|
||||
cx += (w + kerning);
|
||||
}
|
||||
FMultiPatchTexture *image = new FMultiPatchTexture(box.width, box.height, part, false, false);
|
||||
image->SetOffsets(-box.left, -box.top);
|
||||
FImageTexture *tex = new FImageTexture(image, "");
|
||||
tex->SetUseType(ETextureType::MiscPatch);
|
||||
TexMan.AddTexture(tex);
|
||||
return tex;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DrawChar
|
||||
//
|
||||
// Write a single character using the given font
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFrameBuffer::DrawChar (FFont *font, int normalcolor, double x, double y, int character, int tag_first, ...)
|
||||
{
|
||||
if (font == NULL)
|
||||
return;
|
||||
|
||||
if (normalcolor >= NumTextColors)
|
||||
normalcolor = CR_UNTRANSLATED;
|
||||
|
||||
FTexture *pic;
|
||||
int dummy;
|
||||
bool redirected;
|
||||
|
||||
if (NULL != (pic = font->GetChar (character, normalcolor, &dummy, &redirected)))
|
||||
{
|
||||
DrawParms parms;
|
||||
Va_List tags;
|
||||
va_start(tags.list, tag_first);
|
||||
bool res = ParseDrawTextureTags(pic, x, y, tag_first, tags, &parms, false);
|
||||
va_end(tags.list);
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PalEntry color = 0xffffffff;
|
||||
parms.remap = redirected? nullptr : font->GetColorTranslation((EColorRange)normalcolor, &color);
|
||||
parms.color = PalEntry((color.a * parms.color.a) / 255, (color.r * parms.color.r) / 255, (color.g * parms.color.g) / 255, (color.b * parms.color.b) / 255);
|
||||
DrawTextureParms(pic, parms);
|
||||
}
|
||||
}
|
||||
|
||||
void DFrameBuffer::DrawChar(FFont *font, int normalcolor, double x, double y, int character, VMVa_List &args)
|
||||
{
|
||||
if (font == NULL)
|
||||
return;
|
||||
|
||||
if (normalcolor >= NumTextColors)
|
||||
normalcolor = CR_UNTRANSLATED;
|
||||
|
||||
FTexture *pic;
|
||||
int dummy;
|
||||
bool redirected;
|
||||
|
||||
if (NULL != (pic = font->GetChar(character, normalcolor, &dummy, &redirected)))
|
||||
{
|
||||
DrawParms parms;
|
||||
uint32_t tag = ListGetInt(args);
|
||||
bool res = ParseDrawTextureTags(pic, x, y, tag, args, &parms, false);
|
||||
if (!res) return;
|
||||
PalEntry color = 0xffffffff;
|
||||
parms.remap = redirected ? nullptr : font->GetColorTranslation((EColorRange)normalcolor, &color);
|
||||
parms.color = PalEntry((color.a * parms.color.a) / 255, (color.r * parms.color.r) / 255, (color.g * parms.color.g) / 255, (color.b * parms.color.b) / 255);
|
||||
DrawTextureParms(pic, parms);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Screen, DrawChar)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_POINTER(font, FFont);
|
||||
PARAM_INT(cr);
|
||||
PARAM_FLOAT(x);
|
||||
PARAM_FLOAT(y);
|
||||
PARAM_INT(chr);
|
||||
|
||||
PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array
|
||||
|
||||
if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
|
||||
VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo + 5 };
|
||||
screen->DrawChar(font, cr, x, y, chr, args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DrawText
|
||||
//
|
||||
// Write a string using the given font
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFrameBuffer::DrawTextCommon(FFont *font, int normalcolor, double x, double y, const char *string, DrawParms &parms)
|
||||
{
|
||||
int w;
|
||||
const uint8_t *ch;
|
||||
int c;
|
||||
double cx;
|
||||
double cy;
|
||||
int boldcolor;
|
||||
FRemapTable *range;
|
||||
int kerning;
|
||||
FTexture *pic;
|
||||
|
||||
if (parms.celly == 0) parms.celly = font->GetHeight() + 1;
|
||||
parms.celly *= parms.scaley;
|
||||
|
||||
if (normalcolor >= NumTextColors)
|
||||
normalcolor = CR_UNTRANSLATED;
|
||||
boldcolor = normalcolor ? normalcolor - 1 : NumTextColors - 1;
|
||||
|
||||
PalEntry colorparm = parms.color;
|
||||
PalEntry color = 0xffffffff;
|
||||
range = font->GetColorTranslation((EColorRange)normalcolor, &color);
|
||||
parms.color = PalEntry(colorparm.a, (color.r * colorparm.r) / 255, (color.g * colorparm.g) / 255, (color.b * colorparm.b) / 255);
|
||||
|
||||
kerning = font->GetDefaultKerning();
|
||||
|
||||
ch = (const uint8_t *)string;
|
||||
cx = x;
|
||||
cy = y;
|
||||
|
||||
|
||||
auto currentcolor = normalcolor;
|
||||
while ((const char *)ch - string < parms.maxstrlen)
|
||||
{
|
||||
c = GetCharFromString(ch);
|
||||
if (!c)
|
||||
break;
|
||||
|
||||
if (c == TEXTCOLOR_ESCAPE)
|
||||
{
|
||||
EColorRange newcolor = V_ParseFontColor(ch, normalcolor, boldcolor);
|
||||
if (newcolor != CR_UNDEFINED)
|
||||
{
|
||||
range = font->GetColorTranslation(newcolor, &color);
|
||||
parms.color = PalEntry(colorparm.a, (color.r * colorparm.r) / 255, (color.g * colorparm.g) / 255, (color.b * colorparm.b) / 255);
|
||||
currentcolor = newcolor;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
cx = x;
|
||||
cy += parms.celly;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool redirected = false;
|
||||
if (NULL != (pic = font->GetChar(c, currentcolor, &w, &redirected)))
|
||||
{
|
||||
parms.remap = redirected? nullptr : range;
|
||||
SetTextureParms(&parms, pic, cx, cy);
|
||||
if (parms.cellx)
|
||||
{
|
||||
w = parms.cellx;
|
||||
parms.destwidth = parms.cellx;
|
||||
parms.destheight = parms.celly;
|
||||
}
|
||||
DrawTextureParms(pic, parms);
|
||||
}
|
||||
cx += (w + kerning) * parms.scalex;
|
||||
}
|
||||
}
|
||||
|
||||
void DFrameBuffer::DrawText(FFont *font, int normalcolor, double x, double y, const char *string, int tag_first, ...)
|
||||
{
|
||||
Va_List tags;
|
||||
DrawParms parms;
|
||||
|
||||
if (font == NULL || string == NULL)
|
||||
return;
|
||||
|
||||
va_start(tags.list, tag_first);
|
||||
bool res = ParseDrawTextureTags(nullptr, 0, 0, tag_first, tags, &parms, true);
|
||||
va_end(tags.list);
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DrawTextCommon(font, normalcolor, x, y, string, parms);
|
||||
}
|
||||
|
||||
void DFrameBuffer::DrawText(FFont *font, int normalcolor, double x, double y, const char *string, VMVa_List &args)
|
||||
{
|
||||
DrawParms parms;
|
||||
|
||||
if (font == NULL || string == NULL)
|
||||
return;
|
||||
|
||||
uint32_t tag = ListGetInt(args);
|
||||
bool res = ParseDrawTextureTags(nullptr, 0, 0, tag, args, &parms, true);
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DrawTextCommon(font, normalcolor, x, y, string, parms);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Screen, DrawText)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_POINTER(font, FFont);
|
||||
PARAM_INT(cr);
|
||||
PARAM_FLOAT(x);
|
||||
PARAM_FLOAT(y);
|
||||
PARAM_STRING(chr);
|
||||
|
||||
PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array
|
||||
|
||||
if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
|
||||
VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo + 5 };
|
||||
const char *txt = chr[0] == '$' ? GStrings(&chr[1]) : chr.GetChars();
|
||||
screen->DrawText(font, cr, x, y, txt, args);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue