ddnet/src/game/client/render_map.cpp
Patiga a4867d29c6 Make tileflag names consistent and intuitive
- `TILEFLAG_VFLIP` -> `TILEFLAG_FLIP_HORIZONTAL`
- `TILEFLAG_HFLIP` -> `TILEFLAG_FLIP_VERTICAL`

According to the native editor, the "Tiled" editor and image search, a
horizontal flip should be associated with switching left and right, modifying
the x coordinate.

I did not just switch the letters `H` and `V` to create compiler errors
where the original constants are used.

Whenever I was working with tileflags, the naming caused me to have no
idea what I was doing. I mostly had to resort to opening the resulting
map in the editor to see what the code does. This change aims to make
the naming intuitive and also consistent with the map editor.
2022-12-14 13:54:11 +01:00

1056 lines
30 KiB
C++

/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/math.h>
#include <engine/graphics.h>
#include <engine/textrender.h>
#include <engine/shared/config.h>
#include "render.h"
#include <game/generated/client_data.h>
#include <game/mapitems.h>
#include <chrono>
#include <cmath>
using namespace std::chrono_literals;
void CRenderTools::RenderEvalEnvelope(CEnvPoint *pPoints, int NumPoints, int Channels, std::chrono::nanoseconds TimeNanos, ColorRGBA &Result)
{
if(NumPoints == 0)
{
Result = ColorRGBA();
return;
}
if(NumPoints == 1)
{
Result.r = fx2f(pPoints[0].m_aValues[0]);
Result.g = fx2f(pPoints[0].m_aValues[1]);
Result.b = fx2f(pPoints[0].m_aValues[2]);
Result.a = fx2f(pPoints[0].m_aValues[3]);
return;
}
int64_t MaxPointTime = (int64_t)pPoints[NumPoints - 1].m_Time * std::chrono::nanoseconds(1ms).count();
if(MaxPointTime > 0) // TODO: remove this check when implementing a IO check for maps(in this case broken envelopes)
TimeNanos = std::chrono::nanoseconds(TimeNanos.count() % MaxPointTime);
else
TimeNanos = decltype(TimeNanos)::zero();
int TimeMillis = (int)(TimeNanos / std::chrono::nanoseconds(1ms).count()).count();
for(int i = 0; i < NumPoints - 1; i++)
{
if(TimeMillis >= pPoints[i].m_Time && TimeMillis <= pPoints[i + 1].m_Time)
{
float Delta = pPoints[i + 1].m_Time - pPoints[i].m_Time;
float a = (float)(((double)TimeNanos.count() / (double)std::chrono::nanoseconds(1ms).count()) - pPoints[i].m_Time) / Delta;
if(pPoints[i].m_Curvetype == CURVETYPE_SMOOTH)
a = -2 * a * a * a + 3 * a * a; // second hermite basis
else if(pPoints[i].m_Curvetype == CURVETYPE_SLOW)
a = a * a * a;
else if(pPoints[i].m_Curvetype == CURVETYPE_FAST)
{
a = 1 - a;
a = 1 - a * a * a;
}
else if(pPoints[i].m_Curvetype == CURVETYPE_STEP)
a = 0;
else
{
// linear
}
for(int c = 0; c < Channels; c++)
{
float v0 = fx2f(pPoints[i].m_aValues[c]);
float v1 = fx2f(pPoints[i + 1].m_aValues[c]);
Result[c] = v0 + (v1 - v0) * a;
}
return;
}
}
Result.r = fx2f(pPoints[NumPoints - 1].m_aValues[0]);
Result.g = fx2f(pPoints[NumPoints - 1].m_aValues[1]);
Result.b = fx2f(pPoints[NumPoints - 1].m_aValues[2]);
Result.a = fx2f(pPoints[NumPoints - 1].m_aValues[3]);
}
static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation)
{
int x = pPoint->x - pCenter->x;
int y = pPoint->y - pCenter->y;
pPoint->x = (int)(x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x);
pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y);
}
void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser)
{
if(!g_Config.m_ClShowQuads || g_Config.m_ClOverlayEntities == 100)
return;
ForceRenderQuads(pQuads, NumQuads, RenderFlags, pfnEval, pUser, (100 - g_Config.m_ClOverlayEntities) / 100.0f);
}
void CRenderTools::ForceRenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, float Alpha)
{
Graphics()->TrianglesBegin();
float Conv = 1 / 255.0f;
for(int i = 0; i < NumQuads; i++)
{
CQuad *pQuad = &pQuads[i];
ColorRGBA Color(1.f, 1.f, 1.f, 1.f);
if(pQuad->m_ColorEnv >= 0)
{
pfnEval(pQuad->m_ColorEnvOffset, pQuad->m_ColorEnv, Color, pUser);
}
if(Color.a <= 0)
continue;
bool Opaque = false;
/* TODO: Analyze quadtexture
if(a < 0.01f || (q->m_aColors[0].a < 0.01f && q->m_aColors[1].a < 0.01f && q->m_aColors[2].a < 0.01f && q->m_aColors[3].a < 0.01f))
Opaque = true;
*/
if(Opaque && !(RenderFlags & LAYERRENDERFLAG_OPAQUE))
continue;
if(!Opaque && !(RenderFlags & LAYERRENDERFLAG_TRANSPARENT))
continue;
Graphics()->QuadsSetSubsetFree(
fx2f(pQuad->m_aTexcoords[0].x), fx2f(pQuad->m_aTexcoords[0].y),
fx2f(pQuad->m_aTexcoords[1].x), fx2f(pQuad->m_aTexcoords[1].y),
fx2f(pQuad->m_aTexcoords[2].x), fx2f(pQuad->m_aTexcoords[2].y),
fx2f(pQuad->m_aTexcoords[3].x), fx2f(pQuad->m_aTexcoords[3].y));
float OffsetX = 0;
float OffsetY = 0;
float Rot = 0;
// TODO: fix this
if(pQuad->m_PosEnv >= 0)
{
ColorRGBA Channels;
pfnEval(pQuad->m_PosEnvOffset, pQuad->m_PosEnv, Channels, pUser);
OffsetX = Channels.r;
OffsetY = Channels.g;
Rot = Channels.b / 360.0f * pi * 2;
}
IGraphics::CColorVertex Array[4] = {
IGraphics::CColorVertex(0, pQuad->m_aColors[0].r * Conv * Color.r, pQuad->m_aColors[0].g * Conv * Color.g, pQuad->m_aColors[0].b * Conv * Color.b, pQuad->m_aColors[0].a * Conv * Color.a * Alpha),
IGraphics::CColorVertex(1, pQuad->m_aColors[1].r * Conv * Color.r, pQuad->m_aColors[1].g * Conv * Color.g, pQuad->m_aColors[1].b * Conv * Color.b, pQuad->m_aColors[1].a * Conv * Color.a * Alpha),
IGraphics::CColorVertex(2, pQuad->m_aColors[2].r * Conv * Color.r, pQuad->m_aColors[2].g * Conv * Color.g, pQuad->m_aColors[2].b * Conv * Color.b, pQuad->m_aColors[2].a * Conv * Color.a * Alpha),
IGraphics::CColorVertex(3, pQuad->m_aColors[3].r * Conv * Color.r, pQuad->m_aColors[3].g * Conv * Color.g, pQuad->m_aColors[3].b * Conv * Color.b, pQuad->m_aColors[3].a * Conv * Color.a * Alpha)};
Graphics()->SetColorVertex(Array, 4);
CPoint *pPoints = pQuad->m_aPoints;
if(Rot != 0)
{
static CPoint aRotated[4];
aRotated[0] = pQuad->m_aPoints[0];
aRotated[1] = pQuad->m_aPoints[1];
aRotated[2] = pQuad->m_aPoints[2];
aRotated[3] = pQuad->m_aPoints[3];
pPoints = aRotated;
Rotate(&pQuad->m_aPoints[4], &aRotated[0], Rot);
Rotate(&pQuad->m_aPoints[4], &aRotated[1], Rot);
Rotate(&pQuad->m_aPoints[4], &aRotated[2], Rot);
Rotate(&pQuad->m_aPoints[4], &aRotated[3], Rot);
}
IGraphics::CFreeformItem Freeform(
fx2f(pPoints[0].x) + OffsetX, fx2f(pPoints[0].y) + OffsetY,
fx2f(pPoints[1].x) + OffsetX, fx2f(pPoints[1].y) + OffsetY,
fx2f(pPoints[2].x) + OffsetX, fx2f(pPoints[2].y) + OffsetY,
fx2f(pPoints[3].x) + OffsetX, fx2f(pPoints[3].y) + OffsetY);
Graphics()->QuadsDrawFreeform(&Freeform, 1);
}
Graphics()->TrianglesEnd();
}
void CRenderTools::RenderTileRectangle(int RectX, int RectY, int RectW, int RectH,
unsigned char IndexIn, unsigned char IndexOut,
float Scale, ColorRGBA Color, int RenderFlags,
ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset)
{
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
// calculate the final pixelsize for the tiles
float TilePixelSize = 1024 / 32.0f;
float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
float FinalTilesetScale = FinalTileSize / TilePixelSize;
ColorRGBA Channels(1.f, 1.f, 1.f, 1.f);
if(ColorEnv >= 0)
{
pfnEval(ColorEnvOffset, ColorEnv, Channels, pUser);
}
Graphics()->QuadsBegin();
Graphics()->SetColor(Color.r * Channels.r, Color.g * Channels.g, Color.b * Channels.b, Color.a * Channels.a);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
// adjust the texture shift according to mipmap level
float TexSize = 1024.0f;
float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
for(int y = StartY; y < EndY; y++)
{
for(int x = StartX; x < EndX; x++)
{
unsigned char Index = (x >= RectX && x < RectX + RectW && y >= RectY && y < RectY + RectH) ? IndexIn : IndexOut;
if(Index)
{
bool Render = false;
if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
Render = true;
if(Render)
{
int tx = Index % 16;
int ty = Index / 16;
int Px0 = tx * (1024 / 16);
int Py0 = ty * (1024 / 16);
int Px1 = Px0 + (1024 / 16) - 1;
int Py1 = Py0 + (1024 / 16) - 1;
float x0 = Nudge + Px0 / TexSize + Frac;
float y0 = Nudge + Py0 / TexSize + Frac;
float x1 = Nudge + Px1 / TexSize - Frac;
float y1 = Nudge + Py0 / TexSize + Frac;
float x2 = Nudge + Px1 / TexSize - Frac;
float y2 = Nudge + Py1 / TexSize - Frac;
float x3 = Nudge + Px0 / TexSize + Frac;
float y3 = Nudge + Py1 / TexSize - Frac;
Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
Graphics()->QuadsDrawTL(&QuadItem, 1);
}
}
}
}
Graphics()->QuadsEnd();
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, ColorRGBA Color, int RenderFlags,
ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset)
{
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
// calculate the final pixelsize for the tiles
float TilePixelSize = 1024 / 32.0f;
float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
float FinalTilesetScale = FinalTileSize / TilePixelSize;
ColorRGBA Channels(1.f, 1.f, 1.f, 1.f);
if(ColorEnv >= 0)
{
pfnEval(ColorEnvOffset, ColorEnv, Channels, pUser);
}
if(Graphics()->IsTileBufferingEnabled())
Graphics()->QuadsTex3DBegin();
else
Graphics()->QuadsBegin();
Graphics()->SetColor(Color.r * Channels.r, Color.g * Channels.g, Color.b * Channels.b, Color.a * Channels.a);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
// adjust the texture shift according to mipmap level
float TexSize = 1024.0f;
float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
for(int y = StartY; y < EndY; y++)
{
for(int x = StartX; x < EndX; x++)
{
int mx = x;
int my = y;
if(RenderFlags & TILERENDERFLAG_EXTEND)
{
if(mx < 0)
mx = 0;
if(mx >= w)
mx = w - 1;
if(my < 0)
my = 0;
if(my >= h)
my = h - 1;
}
else
{
if(mx < 0)
continue; // mx = 0;
if(mx >= w)
continue; // mx = w-1;
if(my < 0)
continue; // my = 0;
if(my >= h)
continue; // my = h-1;
}
int c = mx + my * w;
unsigned char Index = pTiles[c].m_Index;
if(Index)
{
unsigned char Flags = pTiles[c].m_Flags;
bool Render = false;
if(Flags & TILEFLAG_OPAQUE && Color.a * Channels.a > 254.0f / 255.0f)
{
if(RenderFlags & LAYERRENDERFLAG_OPAQUE)
Render = true;
}
else
{
if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
Render = true;
}
if(Render)
{
int tx = Index % 16;
int ty = Index / 16;
int Px0 = tx * (1024 / 16);
int Py0 = ty * (1024 / 16);
int Px1 = Px0 + (1024 / 16) - 1;
int Py1 = Py0 + (1024 / 16) - 1;
float x0 = Nudge + Px0 / TexSize + Frac;
float y0 = Nudge + Py0 / TexSize + Frac;
float x1 = Nudge + Px1 / TexSize - Frac;
float y1 = Nudge + Py0 / TexSize + Frac;
float x2 = Nudge + Px1 / TexSize - Frac;
float y2 = Nudge + Py1 / TexSize - Frac;
float x3 = Nudge + Px0 / TexSize + Frac;
float y3 = Nudge + Py1 / TexSize - Frac;
if(Graphics()->IsTileBufferingEnabled())
{
x0 = 0;
y0 = 0;
x1 = x0 + 1;
y1 = y0;
x2 = x0 + 1;
y2 = y0 + 1;
x3 = x0;
y3 = y0 + 1;
}
if(Flags & TILEFLAG_FLIP_HORIZONTAL)
{
x0 = x2;
x1 = x3;
x2 = x3;
x3 = x0;
}
if(Flags & TILEFLAG_FLIP_VERTICAL)
{
y0 = y3;
y2 = y1;
y3 = y1;
y1 = y0;
}
if(Flags & TILEFLAG_ROTATE)
{
float Tmp = x0;
x0 = x3;
x3 = x2;
x2 = x1;
x1 = Tmp;
Tmp = y0;
y0 = y3;
y3 = y2;
y2 = y1;
y1 = Tmp;
}
if(Graphics()->IsTileBufferingEnabled())
{
Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3, Index);
IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
Graphics()->QuadsTex3DDrawTL(&QuadItem, 1);
}
else
{
Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
Graphics()->QuadsDrawTL(&QuadItem, 1);
}
}
}
x += pTiles[c].m_Skip;
}
}
if(Graphics()->IsTileBufferingEnabled())
Graphics()->QuadsTex3DEnd();
else
Graphics()->QuadsEnd();
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
void CRenderTools::RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale, float Alpha)
{
if(!g_Config.m_ClTextEntities)
return;
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay)
return; // its useless to render text at this distance
float Size = g_Config.m_ClTextEntitiesSize / 100.f;
float ToCenterOffset = (1 - Size) / 2.f;
for(int y = StartY; y < EndY; y++)
for(int x = StartX; x < EndX; x++)
{
int mx = x;
int my = y;
if(mx < 0)
continue; // mx = 0;
if(mx >= w)
continue; // mx = w-1;
if(my < 0)
continue; // my = 0;
if(my >= h)
continue; // my = h-1;
int c = mx + my * w;
unsigned char Index = pTele[c].m_Number;
if(Index && pTele[c].m_Type != TILE_TELECHECKIN && pTele[c].m_Type != TILE_TELECHECKINEVIL)
{
char aBuf[16];
str_format(aBuf, sizeof(aBuf), "%d", Index);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha);
TextRender()->Text(0, mx * Scale - 3.f, (my + ToCenterOffset) * Scale, Size * Scale, aBuf, -1.0f);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
void CRenderTools::RenderSpeedupOverlay(CSpeedupTile *pSpeedup, int w, int h, float Scale, float Alpha)
{
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay)
return; // its useless to render text at this distance
float Size = g_Config.m_ClTextEntitiesSize / 100.f;
float ToCenterOffset = (1 - Size) / 2.f;
for(int y = StartY; y < EndY; y++)
for(int x = StartX; x < EndX; x++)
{
int mx = x;
int my = y;
if(mx < 0)
continue; // mx = 0;
if(mx >= w)
continue; // mx = w-1;
if(my < 0)
continue; // my = 0;
if(my >= h)
continue; // my = h-1;
int c = mx + my * w;
int Force = (int)pSpeedup[c].m_Force;
int MaxSpeed = (int)pSpeedup[c].m_MaxSpeed;
if(Force)
{
// draw arrow
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_SPEEDUP_ARROW].m_Id);
Graphics()->QuadsBegin();
Graphics()->SetColor(255.0f, 255.0f, 255.0f, Alpha);
SelectSprite(SPRITE_SPEEDUP_ARROW);
Graphics()->QuadsSetRotation(pSpeedup[c].m_Angle * (pi / 180.0f));
DrawSprite(mx * Scale + 16, my * Scale + 16, 35.0f);
Graphics()->QuadsEnd();
if(g_Config.m_ClTextEntities)
{
// draw force
char aBuf[16];
str_format(aBuf, sizeof(aBuf), "%d", Force);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha);
TextRender()->Text(0, mx * Scale, (my + 0.5f + ToCenterOffset / 2) * Scale, Size * Scale / 2.f, aBuf, -1.0f);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
if(MaxSpeed)
{
str_format(aBuf, sizeof(aBuf), "%d", MaxSpeed);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha);
TextRender()->Text(0, mx * Scale, (my + ToCenterOffset / 2) * Scale, Size * Scale / 2.f, aBuf, -1.0f);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}
}
}
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
void CRenderTools::RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float Scale, float Alpha)
{
if(!g_Config.m_ClTextEntities)
return;
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay)
return; // its useless to render text at this distance
float Size = g_Config.m_ClTextEntitiesSize / 100.f;
float ToCenterOffset = (1 - Size) / 2.f;
for(int y = StartY; y < EndY; y++)
for(int x = StartX; x < EndX; x++)
{
int mx = x;
int my = y;
if(mx < 0)
continue; // mx = 0;
if(mx >= w)
continue; // mx = w-1;
if(my < 0)
continue; // my = 0;
if(my >= h)
continue; // my = h-1;
int c = mx + my * w;
unsigned char Index = pSwitch[c].m_Number;
if(Index)
{
char aBuf[16];
str_format(aBuf, sizeof(aBuf), "%d", Index);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha);
TextRender()->Text(0, mx * Scale, (my + ToCenterOffset / 2) * Scale, Size * Scale / 2.f, aBuf, -1.0f);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
unsigned char Delay = pSwitch[c].m_Delay;
if(Delay)
{
char aBuf[16];
str_format(aBuf, sizeof(aBuf), "%d", Delay);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha);
TextRender()->Text(0, mx * Scale, (my + 0.5f + ToCenterOffset / 2) * Scale, Size * Scale / 2.f, aBuf, -1.0f);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
void CRenderTools::RenderTuneOverlay(CTuneTile *pTune, int w, int h, float Scale, float Alpha)
{
if(!g_Config.m_ClTextEntities)
return;
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay)
return; // its useless to render text at this distance
float Size = g_Config.m_ClTextEntitiesSize / 100.f;
for(int y = StartY; y < EndY; y++)
for(int x = StartX; x < EndX; x++)
{
int mx = x;
int my = y;
if(mx < 0)
continue; // mx = 0;
if(mx >= w)
continue; // mx = w-1;
if(my < 0)
continue; // my = 0;
if(my >= h)
continue; // my = h-1;
int c = mx + my * w;
unsigned char Index = pTune[c].m_Number;
if(Index)
{
char aBuf[16];
str_format(aBuf, sizeof(aBuf), "%d", Index);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha);
TextRender()->Text(0, mx * Scale + 11.f, my * Scale + 6.f, Size * Scale / 1.5f - 5.f, aBuf, -1.0f); // numbers shouldn't be too big and in the center of the tile
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
void CRenderTools::RenderTelemap(CTeleTile *pTele, int w, int h, float Scale, ColorRGBA Color, int RenderFlags)
{
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
// calculate the final pixelsize for the tiles
float TilePixelSize = 1024 / 32.0f;
float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
float FinalTilesetScale = FinalTileSize / TilePixelSize;
Graphics()->QuadsBegin();
Graphics()->SetColor(Color);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
// adjust the texture shift according to mipmap level
float TexSize = 1024.0f;
float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
for(int y = StartY; y < EndY; y++)
for(int x = StartX; x < EndX; x++)
{
int mx = x;
int my = y;
if(RenderFlags & TILERENDERFLAG_EXTEND)
{
if(mx < 0)
mx = 0;
if(mx >= w)
mx = w - 1;
if(my < 0)
my = 0;
if(my >= h)
my = h - 1;
}
else
{
if(mx < 0)
continue; // mx = 0;
if(mx >= w)
continue; // mx = w-1;
if(my < 0)
continue; // my = 0;
if(my >= h)
continue; // my = h-1;
}
int c = mx + my * w;
unsigned char Index = pTele[c].m_Type;
if(Index)
{
bool Render = false;
if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
Render = true;
if(Render)
{
int tx = Index % 16;
int ty = Index / 16;
int Px0 = tx * (1024 / 16);
int Py0 = ty * (1024 / 16);
int Px1 = Px0 + (1024 / 16) - 1;
int Py1 = Py0 + (1024 / 16) - 1;
float x0 = Nudge + Px0 / TexSize + Frac;
float y0 = Nudge + Py0 / TexSize + Frac;
float x1 = Nudge + Px1 / TexSize - Frac;
float y1 = Nudge + Py0 / TexSize + Frac;
float x2 = Nudge + Px1 / TexSize - Frac;
float y2 = Nudge + Py1 / TexSize - Frac;
float x3 = Nudge + Px0 / TexSize + Frac;
float y3 = Nudge + Py1 / TexSize - Frac;
Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
Graphics()->QuadsDrawTL(&QuadItem, 1);
}
}
}
Graphics()->QuadsEnd();
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
void CRenderTools::RenderSpeedupmap(CSpeedupTile *pSpeedupTile, int w, int h, float Scale, ColorRGBA Color, int RenderFlags)
{
//Graphics()->TextureSet(img_get(tmap->image));
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
//Graphics()->MapScreen(screen_x0-50, screen_y0-50, screen_x1+50, screen_y1+50);
// calculate the final pixelsize for the tiles
float TilePixelSize = 1024 / 32.0f;
float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
float FinalTilesetScale = FinalTileSize / TilePixelSize;
Graphics()->QuadsBegin();
Graphics()->SetColor(Color);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
// adjust the texture shift according to mipmap level
float TexSize = 1024.0f;
float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
for(int y = StartY; y < EndY; y++)
for(int x = StartX; x < EndX; x++)
{
int mx = x;
int my = y;
if(RenderFlags & TILERENDERFLAG_EXTEND)
{
if(mx < 0)
mx = 0;
if(mx >= w)
mx = w - 1;
if(my < 0)
my = 0;
if(my >= h)
my = h - 1;
}
else
{
if(mx < 0)
continue; // mx = 0;
if(mx >= w)
continue; // mx = w-1;
if(my < 0)
continue; // my = 0;
if(my >= h)
continue; // my = h-1;
}
int c = mx + my * w;
unsigned char Index = pSpeedupTile[c].m_Type;
if(Index)
{
bool Render = false;
if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
Render = true;
if(Render)
{
int tx = Index % 16;
int ty = Index / 16;
int Px0 = tx * (1024 / 16);
int Py0 = ty * (1024 / 16);
int Px1 = Px0 + (1024 / 16) - 1;
int Py1 = Py0 + (1024 / 16) - 1;
float x0 = Nudge + Px0 / TexSize + Frac;
float y0 = Nudge + Py0 / TexSize + Frac;
float x1 = Nudge + Px1 / TexSize - Frac;
float y1 = Nudge + Py0 / TexSize + Frac;
float x2 = Nudge + Px1 / TexSize - Frac;
float y2 = Nudge + Py1 / TexSize - Frac;
float x3 = Nudge + Px0 / TexSize + Frac;
float y3 = Nudge + Py1 / TexSize - Frac;
Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
Graphics()->QuadsDrawTL(&QuadItem, 1);
}
}
}
Graphics()->QuadsEnd();
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
void CRenderTools::RenderSwitchmap(CSwitchTile *pSwitchTile, int w, int h, float Scale, ColorRGBA Color, int RenderFlags)
{
//Graphics()->TextureSet(img_get(tmap->image));
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
//Graphics()->MapScreen(screen_x0-50, screen_y0-50, screen_x1+50, screen_y1+50);
// calculate the final pixelsize for the tiles
float TilePixelSize = 1024 / 32.0f;
float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
float FinalTilesetScale = FinalTileSize / TilePixelSize;
Graphics()->QuadsBegin();
Graphics()->SetColor(Color);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
// adjust the texture shift according to mipmap level
float TexSize = 1024.0f;
float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
for(int y = StartY; y < EndY; y++)
for(int x = StartX; x < EndX; x++)
{
int mx = x;
int my = y;
if(RenderFlags & TILERENDERFLAG_EXTEND)
{
if(mx < 0)
mx = 0;
if(mx >= w)
mx = w - 1;
if(my < 0)
my = 0;
if(my >= h)
my = h - 1;
}
else
{
if(mx < 0)
continue; // mx = 0;
if(mx >= w)
continue; // mx = w-1;
if(my < 0)
continue; // my = 0;
if(my >= h)
continue; // my = h-1;
}
int c = mx + my * w;
unsigned char Index = pSwitchTile[c].m_Type;
if(Index)
{
if(Index == TILE_SWITCHTIMEDOPEN)
Index = 8;
unsigned char Flags = pSwitchTile[c].m_Flags;
bool Render = false;
if(Flags & TILEFLAG_OPAQUE)
{
if(RenderFlags & LAYERRENDERFLAG_OPAQUE)
Render = true;
}
else
{
if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
Render = true;
}
if(Render)
{
int tx = Index % 16;
int ty = Index / 16;
int Px0 = tx * (1024 / 16);
int Py0 = ty * (1024 / 16);
int Px1 = Px0 + (1024 / 16) - 1;
int Py1 = Py0 + (1024 / 16) - 1;
float x0 = Nudge + Px0 / TexSize + Frac;
float y0 = Nudge + Py0 / TexSize + Frac;
float x1 = Nudge + Px1 / TexSize - Frac;
float y1 = Nudge + Py0 / TexSize + Frac;
float x2 = Nudge + Px1 / TexSize - Frac;
float y2 = Nudge + Py1 / TexSize - Frac;
float x3 = Nudge + Px0 / TexSize + Frac;
float y3 = Nudge + Py1 / TexSize - Frac;
if(Flags & TILEFLAG_FLIP_HORIZONTAL)
{
x0 = x2;
x1 = x3;
x2 = x3;
x3 = x0;
}
if(Flags & TILEFLAG_FLIP_VERTICAL)
{
y0 = y3;
y2 = y1;
y3 = y1;
y1 = y0;
}
if(Flags & TILEFLAG_ROTATE)
{
float Tmp = x0;
x0 = x3;
x3 = x2;
x2 = x1;
x1 = Tmp;
Tmp = y0;
y0 = y3;
y3 = y2;
y2 = y1;
y1 = Tmp;
}
Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
Graphics()->QuadsDrawTL(&QuadItem, 1);
}
}
}
Graphics()->QuadsEnd();
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
void CRenderTools::RenderTunemap(CTuneTile *pTune, int w, int h, float Scale, ColorRGBA Color, int RenderFlags)
{
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
// calculate the final pixelsize for the tiles
float TilePixelSize = 1024 / 32.0f;
float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
float FinalTilesetScale = FinalTileSize / TilePixelSize;
Graphics()->QuadsBegin();
Graphics()->SetColor(Color);
int StartY = (int)(ScreenY0 / Scale) - 1;
int StartX = (int)(ScreenX0 / Scale) - 1;
int EndY = (int)(ScreenY1 / Scale) + 1;
int EndX = (int)(ScreenX1 / Scale) + 1;
// adjust the texture shift according to mipmap level
float TexSize = 1024.0f;
float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
for(int y = StartY; y < EndY; y++)
for(int x = StartX; x < EndX; x++)
{
int mx = x;
int my = y;
if(RenderFlags & TILERENDERFLAG_EXTEND)
{
if(mx < 0)
mx = 0;
if(mx >= w)
mx = w - 1;
if(my < 0)
my = 0;
if(my >= h)
my = h - 1;
}
else
{
if(mx < 0)
continue; // mx = 0;
if(mx >= w)
continue; // mx = w-1;
if(my < 0)
continue; // my = 0;
if(my >= h)
continue; // my = h-1;
}
int c = mx + my * w;
unsigned char Index = pTune[c].m_Type;
if(Index)
{
bool Render = false;
if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
Render = true;
if(Render)
{
int tx = Index % 16;
int ty = Index / 16;
int Px0 = tx * (1024 / 16);
int Py0 = ty * (1024 / 16);
int Px1 = Px0 + (1024 / 16) - 1;
int Py1 = Py0 + (1024 / 16) - 1;
float x0 = Nudge + Px0 / TexSize + Frac;
float y0 = Nudge + Py0 / TexSize + Frac;
float x1 = Nudge + Px1 / TexSize - Frac;
float y1 = Nudge + Py0 / TexSize + Frac;
float x2 = Nudge + Px1 / TexSize - Frac;
float y2 = Nudge + Py1 / TexSize - Frac;
float x3 = Nudge + Px0 / TexSize + Frac;
float y3 = Nudge + Py1 / TexSize - Frac;
Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
Graphics()->QuadsDrawTL(&QuadItem, 1);
}
}
}
Graphics()->QuadsEnd();
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}