ddnet/src/game/client/render.cpp
Fireball a90c86e9a5 Parallax-aware zoom
How this works: parallax values configure perceived distance from camera
when it's moving along x and y axes. Assume that zoom is moving the
camera away and scale layers accordingly, with background layers
(furtherst away) changing the least.

New per-ItemGroup (LayerGroup) setting allows to set the new parallax
value independently from the other two. This can be used to do tricks
like on Time Shop zoom correctly or make it feel like the camera is
changing the field of view at the same time as moving in space.
2022-08-05 00:40:58 +01:00

818 lines
28 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 <cmath>
#include <base/math.h>
#include "animstate.h"
#include "render.h"
#include <engine/graphics.h>
#include <engine/shared/config.h>
#include <game/generated/client_data.h>
#include <game/generated/client_data7.h>
#include <game/generated/protocol.h>
#include <game/mapitems.h>
static float gs_SpriteWScale;
static float gs_SpriteHScale;
void CRenderTools::Init(IGraphics *pGraphics, ITextRender *pTextRender)
{
m_pGraphics = pGraphics;
m_pTextRender = pTextRender;
m_TeeQuadContainerIndex = Graphics()->CreateQuadContainer(false);
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
QuadContainerAddSprite(m_TeeQuadContainerIndex, 64.f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
QuadContainerAddSprite(m_TeeQuadContainerIndex, 64.f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
QuadContainerAddSprite(m_TeeQuadContainerIndex, 64.f * 0.4f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
QuadContainerAddSprite(m_TeeQuadContainerIndex, 64.f * 0.4f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
QuadContainerAddSprite(m_TeeQuadContainerIndex, 64.f * 0.4f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
QuadContainerAddSprite(m_TeeQuadContainerIndex, 64.f * 0.4f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
QuadContainerAddSprite(m_TeeQuadContainerIndex, 64.f * 0.4f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
QuadContainerAddSprite(m_TeeQuadContainerIndex, -32.f, -16.f, 64.f, 32.f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
QuadContainerAddSprite(m_TeeQuadContainerIndex, -32.f, -16.f, 64.f, 32.f);
Graphics()->QuadContainerUpload(m_TeeQuadContainerIndex);
}
void CRenderTools::SelectSprite(CDataSprite *pSpr, int Flags, int sx, int sy)
{
int x = pSpr->m_X + sx;
int y = pSpr->m_Y + sy;
int w = pSpr->m_W;
int h = pSpr->m_H;
int cx = pSpr->m_pSet->m_Gridx;
int cy = pSpr->m_pSet->m_Gridy;
GetSpriteScaleImpl(w, h, gs_SpriteWScale, gs_SpriteHScale);
float x1 = x / (float)cx + 0.5f / (float)(cx * 32);
float x2 = (x + w) / (float)cx - 0.5f / (float)(cx * 32);
float y1 = y / (float)cy + 0.5f / (float)(cy * 32);
float y2 = (y + h) / (float)cy - 0.5f / (float)(cy * 32);
if(Flags & SPRITE_FLAG_FLIP_Y)
std::swap(y1, y2);
if(Flags & SPRITE_FLAG_FLIP_X)
std::swap(x1, x2);
Graphics()->QuadsSetSubset(x1, y1, x2, y2);
}
void CRenderTools::SelectSprite(int Id, int Flags, int sx, int sy)
{
if(Id < 0 || Id >= g_pData->m_NumSprites)
return;
SelectSprite(&g_pData->m_aSprites[Id], Flags, sx, sy);
}
void CRenderTools::GetSpriteScale(client_data7::CDataSprite *pSprite, float &ScaleX, float &ScaleY)
{
int w = pSprite->m_W;
int h = pSprite->m_H;
GetSpriteScaleImpl(w, h, ScaleX, ScaleY);
}
void CRenderTools::GetSpriteScale(struct CDataSprite *pSprite, float &ScaleX, float &ScaleY)
{
int w = pSprite->m_W;
int h = pSprite->m_H;
GetSpriteScaleImpl(w, h, ScaleX, ScaleY);
}
void CRenderTools::GetSpriteScale(int Id, float &ScaleX, float &ScaleY)
{
GetSpriteScale(&g_pData->m_aSprites[Id], ScaleX, ScaleY);
}
void CRenderTools::GetSpriteScaleImpl(int Width, int Height, float &ScaleX, float &ScaleY)
{
float f = sqrtf(Height * Height + Width * Width);
ScaleX = Width / f;
ScaleY = Height / f;
}
void CRenderTools::DrawSprite(float x, float y, float Size)
{
IGraphics::CQuadItem QuadItem(x, y, Size * gs_SpriteWScale, Size * gs_SpriteHScale);
Graphics()->QuadsDraw(&QuadItem, 1);
}
void CRenderTools::DrawSprite(float x, float y, float ScaledWidth, float ScaledHeight)
{
IGraphics::CQuadItem QuadItem(x, y, ScaledWidth, ScaledHeight);
Graphics()->QuadsDraw(&QuadItem, 1);
}
void CRenderTools::RenderCursor(vec2 Center, float Size)
{
Graphics()->WrapClamp();
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_CURSOR].m_Id);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
IGraphics::CQuadItem QuadItem(Center.x, Center.y, Size, Size);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
Graphics()->WrapNormal();
}
void CRenderTools::RenderIcon(int ImageId, int SpriteId, const CUIRect *pRect, const ColorRGBA *pColor)
{
Graphics()->TextureSet(g_pData->m_aImages[ImageId].m_Id);
Graphics()->QuadsBegin();
SelectSprite(SpriteId);
if(pColor)
Graphics()->SetColor(pColor->r * pColor->a, pColor->g * pColor->a, pColor->b * pColor->a, pColor->a);
IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
}
int CRenderTools::QuadContainerAddSprite(int QuadContainerIndex, float x, float y, float Size)
{
IGraphics::CQuadItem QuadItem(x, y, Size, Size);
return Graphics()->QuadContainerAddQuads(QuadContainerIndex, &QuadItem, 1);
}
int CRenderTools::QuadContainerAddSprite(int QuadContainerIndex, float Size)
{
IGraphics::CQuadItem QuadItem(-(Size) / 2.f, -(Size) / 2.f, (Size), (Size));
return Graphics()->QuadContainerAddQuads(QuadContainerIndex, &QuadItem, 1);
}
int CRenderTools::QuadContainerAddSprite(int QuadContainerIndex, float Width, float Height)
{
IGraphics::CQuadItem QuadItem(-(Width) / 2.f, -(Height) / 2.f, (Width), (Height));
return Graphics()->QuadContainerAddQuads(QuadContainerIndex, &QuadItem, 1);
}
int CRenderTools::QuadContainerAddSprite(int QuadContainerIndex, float X, float Y, float Width, float Height)
{
IGraphics::CQuadItem QuadItem(X, Y, Width, Height);
return Graphics()->QuadContainerAddQuads(QuadContainerIndex, &QuadItem, 1);
}
void CRenderTools::DrawRoundRectExt(float x, float y, float w, float h, float r, int Corners)
{
int NumItems = 0;
const int Num = 8;
IGraphics::CFreeformItem ArrayF[Num * 4];
for(int i = 0; i < Num; i += 2)
{
float a1 = i / (float)Num * pi / 2;
float a2 = (i + 1) / (float)Num * pi / 2;
float a3 = (i + 2) / (float)Num * pi / 2;
float Ca1 = cosf(a1);
float Ca2 = cosf(a2);
float Ca3 = cosf(a3);
float Sa1 = sinf(a1);
float Sa2 = sinf(a2);
float Sa3 = sinf(a3);
if(Corners & 1) // TL
ArrayF[NumItems++] = IGraphics::CFreeformItem(
x + r, y + r,
x + (1 - Ca1) * r, y + (1 - Sa1) * r,
x + (1 - Ca3) * r, y + (1 - Sa3) * r,
x + (1 - Ca2) * r, y + (1 - Sa2) * r);
if(Corners & 2) // TR
ArrayF[NumItems++] = IGraphics::CFreeformItem(
x + w - r, y + r,
x + w - r + Ca1 * r, y + (1 - Sa1) * r,
x + w - r + Ca3 * r, y + (1 - Sa3) * r,
x + w - r + Ca2 * r, y + (1 - Sa2) * r);
if(Corners & 4) // BL
ArrayF[NumItems++] = IGraphics::CFreeformItem(
x + r, y + h - r,
x + (1 - Ca1) * r, y + h - r + Sa1 * r,
x + (1 - Ca3) * r, y + h - r + Sa3 * r,
x + (1 - Ca2) * r, y + h - r + Sa2 * r);
if(Corners & 8) // BR
ArrayF[NumItems++] = IGraphics::CFreeformItem(
x + w - r, y + h - r,
x + w - r + Ca1 * r, y + h - r + Sa1 * r,
x + w - r + Ca3 * r, y + h - r + Sa3 * r,
x + w - r + Ca2 * r, y + h - r + Sa2 * r);
}
Graphics()->QuadsDrawFreeform(ArrayF, NumItems);
IGraphics::CQuadItem ArrayQ[9];
NumItems = 0;
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + r, y + r, w - r * 2, h - r * 2); // center
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + r, y, w - r * 2, r); // top
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + r, y + h - r, w - r * 2, r); // bottom
ArrayQ[NumItems++] = IGraphics::CQuadItem(x, y + r, r, h - r * 2); // left
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + w - r, y + r, r, h - r * 2); // right
if(!(Corners & 1))
ArrayQ[NumItems++] = IGraphics::CQuadItem(x, y, r, r); // TL
if(!(Corners & 2))
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + w, y, -r, r); // TR
if(!(Corners & 4))
ArrayQ[NumItems++] = IGraphics::CQuadItem(x, y + h, r, -r); // BL
if(!(Corners & 8))
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + w, y + h, -r, -r); // BR
Graphics()->QuadsDrawTL(ArrayQ, NumItems);
}
void CRenderTools::DrawRoundRectExt4(float x, float y, float w, float h, vec4 ColorTopLeft, vec4 ColorTopRight, vec4 ColorBottomLeft, vec4 ColorBottomRight, float r, int Corners)
{
if(Corners == 0 || r == 0.0f)
{
Graphics()->SetColor4(ColorTopLeft, ColorTopRight, ColorBottomLeft, ColorBottomRight);
IGraphics::CQuadItem ItemQ = IGraphics::CQuadItem(x, y, w, h);
Graphics()->QuadsDrawTL(&ItemQ, 1);
return;
}
int Num = 8;
for(int i = 0; i < Num; i += 2)
{
float a1 = i / (float)Num * pi / 2;
float a2 = (i + 1) / (float)Num * pi / 2;
float a3 = (i + 2) / (float)Num * pi / 2;
float Ca1 = cosf(a1);
float Ca2 = cosf(a2);
float Ca3 = cosf(a3);
float Sa1 = sinf(a1);
float Sa2 = sinf(a2);
float Sa3 = sinf(a3);
if(Corners & 1) // TL
{
Graphics()->SetColor(ColorTopLeft.r, ColorTopLeft.g, ColorTopLeft.b, ColorTopLeft.a);
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
x + r, y + r,
x + (1 - Ca1) * r, y + (1 - Sa1) * r,
x + (1 - Ca3) * r, y + (1 - Sa3) * r,
x + (1 - Ca2) * r, y + (1 - Sa2) * r);
Graphics()->QuadsDrawFreeform(&ItemF, 1);
}
if(Corners & 2) // TR
{
Graphics()->SetColor(ColorTopRight.r, ColorTopRight.g, ColorTopRight.b, ColorTopRight.a);
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
x + w - r, y + r,
x + w - r + Ca1 * r, y + (1 - Sa1) * r,
x + w - r + Ca3 * r, y + (1 - Sa3) * r,
x + w - r + Ca2 * r, y + (1 - Sa2) * r);
Graphics()->QuadsDrawFreeform(&ItemF, 1);
}
if(Corners & 4) // BL
{
Graphics()->SetColor(ColorBottomLeft.r, ColorBottomLeft.g, ColorBottomLeft.b, ColorBottomLeft.a);
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
x + r, y + h - r,
x + (1 - Ca1) * r, y + h - r + Sa1 * r,
x + (1 - Ca3) * r, y + h - r + Sa3 * r,
x + (1 - Ca2) * r, y + h - r + Sa2 * r);
Graphics()->QuadsDrawFreeform(&ItemF, 1);
}
if(Corners & 8) // BR
{
Graphics()->SetColor(ColorBottomRight.r, ColorBottomRight.g, ColorBottomRight.b, ColorBottomRight.a);
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
x + w - r, y + h - r,
x + w - r + Ca1 * r, y + h - r + Sa1 * r,
x + w - r + Ca3 * r, y + h - r + Sa3 * r,
x + w - r + Ca2 * r, y + h - r + Sa2 * r);
Graphics()->QuadsDrawFreeform(&ItemF, 1);
}
if(Corners & 16) // ITL
{
Graphics()->SetColor(ColorTopLeft.r, ColorTopLeft.g, ColorTopLeft.b, ColorTopLeft.a);
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
x, y,
x + (1 - Ca1) * r, y - r + Sa1 * r,
x + (1 - Ca3) * r, y - r + Sa3 * r,
x + (1 - Ca2) * r, y - r + Sa2 * r);
Graphics()->QuadsDrawFreeform(&ItemF, 1);
}
if(Corners & 32) // ITR
{
Graphics()->SetColor(ColorTopRight.r, ColorTopRight.g, ColorTopRight.b, ColorTopRight.a);
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
x + w, y,
x + w - r + Ca1 * r, y - r + Sa1 * r,
x + w - r + Ca3 * r, y - r + Sa3 * r,
x + w - r + Ca2 * r, y - r + Sa2 * r);
Graphics()->QuadsDrawFreeform(&ItemF, 1);
}
if(Corners & 64) // IBL
{
Graphics()->SetColor(ColorBottomLeft.r, ColorBottomLeft.g, ColorBottomLeft.b, ColorBottomLeft.a);
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
x, y + h,
x + (1 - Ca1) * r, y + h + (1 - Sa1) * r,
x + (1 - Ca3) * r, y + h + (1 - Sa3) * r,
x + (1 - Ca2) * r, y + h + (1 - Sa2) * r);
Graphics()->QuadsDrawFreeform(&ItemF, 1);
}
if(Corners & 128) // IBR
{
Graphics()->SetColor(ColorBottomRight.r, ColorBottomRight.g, ColorBottomRight.b, ColorBottomRight.a);
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
x + w, y + h,
x + w - r + Ca1 * r, y + h + (1 - Sa1) * r,
x + w - r + Ca3 * r, y + h + (1 - Sa3) * r,
x + w - r + Ca2 * r, y + h + (1 - Sa2) * r);
Graphics()->QuadsDrawFreeform(&ItemF, 1);
}
}
Graphics()->SetColor4(ColorTopLeft, ColorTopRight, ColorBottomLeft, ColorBottomRight);
IGraphics::CQuadItem ItemQ = IGraphics::CQuadItem(x + r, y + r, w - r * 2, h - r * 2); // center
Graphics()->QuadsDrawTL(&ItemQ, 1);
Graphics()->SetColor4(ColorTopLeft, ColorTopRight, ColorTopLeft, ColorTopRight);
ItemQ = IGraphics::CQuadItem(x + r, y, w - r * 2, r); // top
Graphics()->QuadsDrawTL(&ItemQ, 1);
Graphics()->SetColor4(ColorBottomLeft, ColorBottomRight, ColorBottomLeft, ColorBottomRight);
ItemQ = IGraphics::CQuadItem(x + r, y + h - r, w - r * 2, r); // bottom
Graphics()->QuadsDrawTL(&ItemQ, 1);
Graphics()->SetColor4(ColorTopLeft, ColorTopLeft, ColorBottomLeft, ColorBottomLeft);
ItemQ = IGraphics::CQuadItem(x, y + r, r, h - r * 2); // left
Graphics()->QuadsDrawTL(&ItemQ, 1);
Graphics()->SetColor4(ColorTopRight, ColorTopRight, ColorBottomRight, ColorBottomRight);
ItemQ = IGraphics::CQuadItem(x + w - r, y + r, r, h - r * 2); // right
Graphics()->QuadsDrawTL(&ItemQ, 1);
if(!(Corners & 1))
{
Graphics()->SetColor(ColorTopLeft.r, ColorTopLeft.g, ColorTopLeft.b, ColorTopLeft.a);
ItemQ = IGraphics::CQuadItem(x, y, r, r); // TL
Graphics()->QuadsDrawTL(&ItemQ, 1);
}
if(!(Corners & 2))
{
Graphics()->SetColor(ColorTopRight.r, ColorTopRight.g, ColorTopRight.b, ColorTopRight.a);
ItemQ = IGraphics::CQuadItem(x + w, y, -r, r); // TR
Graphics()->QuadsDrawTL(&ItemQ, 1);
}
if(!(Corners & 4))
{
Graphics()->SetColor(ColorBottomLeft.r, ColorBottomLeft.g, ColorBottomLeft.b, ColorBottomLeft.a);
ItemQ = IGraphics::CQuadItem(x, y + h, r, -r); // BL
Graphics()->QuadsDrawTL(&ItemQ, 1);
}
if(!(Corners & 8))
{
Graphics()->SetColor(ColorBottomRight.r, ColorBottomRight.g, ColorBottomRight.b, ColorBottomRight.a);
ItemQ = IGraphics::CQuadItem(x + w, y + h, -r, -r); // BR
Graphics()->QuadsDrawTL(&ItemQ, 1);
}
}
int CRenderTools::CreateRoundRectQuadContainer(float x, float y, float w, float h, float r, int Corners)
{
int ContainerIndex = Graphics()->CreateQuadContainer(false);
if(Corners == 0 || r == 0.0f)
{
IGraphics::CQuadItem ItemQ = IGraphics::CQuadItem(x, y, w, h);
Graphics()->QuadContainerAddQuads(ContainerIndex, &ItemQ, 1);
Graphics()->QuadContainerUpload(ContainerIndex);
Graphics()->QuadContainerChangeAutomaticUpload(ContainerIndex, true);
return ContainerIndex;
}
IGraphics::CFreeformItem ArrayF[32];
int NumItems = 0;
int Num = 8;
for(int i = 0; i < Num; i += 2)
{
float a1 = i / (float)Num * pi / 2;
float a2 = (i + 1) / (float)Num * pi / 2;
float a3 = (i + 2) / (float)Num * pi / 2;
float Ca1 = cosf(a1);
float Ca2 = cosf(a2);
float Ca3 = cosf(a3);
float Sa1 = sinf(a1);
float Sa2 = sinf(a2);
float Sa3 = sinf(a3);
if(Corners & 1) // TL
ArrayF[NumItems++] = IGraphics::CFreeformItem(
x + r, y + r,
x + (1 - Ca1) * r, y + (1 - Sa1) * r,
x + (1 - Ca3) * r, y + (1 - Sa3) * r,
x + (1 - Ca2) * r, y + (1 - Sa2) * r);
if(Corners & 2) // TR
ArrayF[NumItems++] = IGraphics::CFreeformItem(
x + w - r, y + r,
x + w - r + Ca1 * r, y + (1 - Sa1) * r,
x + w - r + Ca3 * r, y + (1 - Sa3) * r,
x + w - r + Ca2 * r, y + (1 - Sa2) * r);
if(Corners & 4) // BL
ArrayF[NumItems++] = IGraphics::CFreeformItem(
x + r, y + h - r,
x + (1 - Ca1) * r, y + h - r + Sa1 * r,
x + (1 - Ca3) * r, y + h - r + Sa3 * r,
x + (1 - Ca2) * r, y + h - r + Sa2 * r);
if(Corners & 8) // BR
ArrayF[NumItems++] = IGraphics::CFreeformItem(
x + w - r, y + h - r,
x + w - r + Ca1 * r, y + h - r + Sa1 * r,
x + w - r + Ca3 * r, y + h - r + Sa3 * r,
x + w - r + Ca2 * r, y + h - r + Sa2 * r);
}
if(NumItems > 0)
Graphics()->QuadContainerAddQuads(ContainerIndex, ArrayF, NumItems);
IGraphics::CQuadItem ArrayQ[9];
NumItems = 0;
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + r, y + r, w - r * 2, h - r * 2); // center
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + r, y, w - r * 2, r); // top
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + r, y + h - r, w - r * 2, r); // bottom
ArrayQ[NumItems++] = IGraphics::CQuadItem(x, y + r, r, h - r * 2); // left
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + w - r, y + r, r, h - r * 2); // right
if(!(Corners & 1))
ArrayQ[NumItems++] = IGraphics::CQuadItem(x, y, r, r); // TL
if(!(Corners & 2))
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + w, y, -r, r); // TR
if(!(Corners & 4))
ArrayQ[NumItems++] = IGraphics::CQuadItem(x, y + h, r, -r); // BL
if(!(Corners & 8))
ArrayQ[NumItems++] = IGraphics::CQuadItem(x + w, y + h, -r, -r); // BR
if(NumItems > 0)
Graphics()->QuadContainerAddQuads(ContainerIndex, ArrayQ, NumItems);
Graphics()->QuadContainerUpload(ContainerIndex);
Graphics()->QuadContainerChangeAutomaticUpload(ContainerIndex, true);
return ContainerIndex;
}
void CRenderTools::DrawUIElRect(CUIElement::SUIElementRect &ElUIRect, const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding)
{
bool NeedsRecreate = false;
if(ElUIRect.m_UIRectQuadContainer == -1 || ElUIRect.m_X != pRect->x || ElUIRect.m_Y != pRect->y || ElUIRect.m_Width != pRect->w || ElUIRect.m_Height != pRect->h || mem_comp(&ElUIRect.m_QuadColor, &Color, sizeof(Color)) != 0)
{
if(ElUIRect.m_UIRectQuadContainer != -1)
Graphics()->DeleteQuadContainer(ElUIRect.m_UIRectQuadContainer);
NeedsRecreate = true;
}
if(NeedsRecreate)
{
ElUIRect.m_X = pRect->x;
ElUIRect.m_Y = pRect->y;
ElUIRect.m_Width = pRect->w;
ElUIRect.m_Height = pRect->h;
ElUIRect.m_QuadColor = Color;
Graphics()->SetColor(Color);
ElUIRect.m_UIRectQuadContainer = CreateRoundRectQuadContainer(pRect->x, pRect->y, pRect->w, pRect->h, Rounding, Corners);
Graphics()->SetColor(1, 1, 1, 1);
}
Graphics()->TextureClear();
Graphics()->RenderQuadContainer(ElUIRect.m_UIRectQuadContainer, -1);
}
void CRenderTools::DrawRect(float x, float y, float w, float h, ColorRGBA Color, int Corners, float Rounding)
{
Graphics()->TextureClear();
Graphics()->QuadsBegin();
Graphics()->SetColor(Color);
DrawRoundRectExt(x, y, w, h, Rounding, Corners);
Graphics()->QuadsEnd();
}
void CRenderTools::DrawUIRect(const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding)
{
DrawRect(pRect->x, pRect->y, pRect->w, pRect->h, Color, Corners, Rounding);
}
void CRenderTools::DrawRect4(float x, float y, float w, float h, vec4 ColorTopLeft, vec4 ColorTopRight, vec4 ColorBottomLeft, vec4 ColorBottomRight, int Corners, float Rounding)
{
Graphics()->TextureClear();
Graphics()->QuadsBegin();
DrawRoundRectExt4(x, y, w, h, ColorTopLeft, ColorTopRight, ColorBottomLeft, ColorBottomRight, Rounding, Corners);
Graphics()->QuadsEnd();
}
void CRenderTools::DrawUIRect4(const CUIRect *pRect, vec4 ColorTopLeft, vec4 ColorTopRight, vec4 ColorBottomLeft, vec4 ColorBottomRight, int Corners, float Rounding)
{
DrawRect4(pRect->x, pRect->y, pRect->w, pRect->h, ColorTopLeft, ColorTopRight, ColorBottomLeft, ColorBottomRight, Corners, Rounding);
}
void CRenderTools::DrawCircle(float x, float y, float r, int Segments)
{
IGraphics::CFreeformItem Array[32];
int NumItems = 0;
float FSegments = (float)Segments;
for(int i = 0; i < Segments; i += 2)
{
float a1 = i / FSegments * 2 * pi;
float a2 = (i + 1) / FSegments * 2 * pi;
float a3 = (i + 2) / FSegments * 2 * pi;
float Ca1 = cosf(a1);
float Ca2 = cosf(a2);
float Ca3 = cosf(a3);
float Sa1 = sinf(a1);
float Sa2 = sinf(a2);
float Sa3 = sinf(a3);
Array[NumItems++] = IGraphics::CFreeformItem(
x, y,
x + Ca1 * r, y + Sa1 * r,
x + Ca3 * r, y + Sa3 * r,
x + Ca2 * r, y + Sa2 * r);
if(NumItems == 32)
{
Graphics()->QuadsDrawFreeform(Array, 32);
NumItems = 0;
}
}
if(NumItems)
Graphics()->QuadsDrawFreeform(Array, NumItems);
}
void CRenderTools::GetRenderTeeAnimScaleAndBaseSize(CAnimState *pAnim, CTeeRenderInfo *pInfo, float &AnimScale, float &BaseSize)
{
AnimScale = pInfo->m_Size * 1.0f / 64.0f;
BaseSize = pInfo->m_Size;
}
void CRenderTools::GetRenderTeeBodyScale(float BaseSize, float &BodyScale)
{
BodyScale = g_Config.m_ClFatSkins ? BaseSize * 1.3f : BaseSize;
BodyScale /= 64.0f;
}
void CRenderTools::GetRenderTeeFeetScale(float BaseSize, float &FeetScaleWidth, float &FeetScaleHeight)
{
FeetScaleWidth = BaseSize / 64.0f;
FeetScaleHeight = (BaseSize / 2) / 32.0f;
}
void CRenderTools::GetRenderTeeBodySize(CAnimState *pAnim, CTeeRenderInfo *pInfo, vec2 &BodyOffset, float &Width, float &Height)
{
float AnimScale, BaseSize;
GetRenderTeeAnimScaleAndBaseSize(pAnim, pInfo, AnimScale, BaseSize);
float BodyScale;
GetRenderTeeBodyScale(BaseSize, BodyScale);
Width = pInfo->m_SkinMetrics.m_Body.WidthNormalized() * 64.0f * BodyScale;
Height = pInfo->m_SkinMetrics.m_Body.HeightNormalized() * 64.0f * BodyScale;
BodyOffset.x = pInfo->m_SkinMetrics.m_Body.OffsetXNormalized() * 64.0f * BodyScale;
BodyOffset.y = pInfo->m_SkinMetrics.m_Body.OffsetYNormalized() * 64.0f * BodyScale;
}
void CRenderTools::GetRenderTeeFeetSize(CAnimState *pAnim, CTeeRenderInfo *pInfo, vec2 &FeetOffset, float &Width, float &Height)
{
float AnimScale, BaseSize;
GetRenderTeeAnimScaleAndBaseSize(pAnim, pInfo, AnimScale, BaseSize);
float FeetScaleWidth, FeetScaleHeight;
GetRenderTeeFeetScale(BaseSize, FeetScaleWidth, FeetScaleHeight);
Width = pInfo->m_SkinMetrics.m_Feet.WidthNormalized() * 64.0f * FeetScaleWidth;
Height = pInfo->m_SkinMetrics.m_Feet.HeightNormalized() * 32.0f * FeetScaleHeight;
FeetOffset.x = pInfo->m_SkinMetrics.m_Feet.OffsetXNormalized() * 64.0f * FeetScaleWidth;
FeetOffset.y = pInfo->m_SkinMetrics.m_Feet.OffsetYNormalized() * 32.0f * FeetScaleHeight;
}
void CRenderTools::GetRenderTeeOffsetToRenderedTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, vec2 &TeeOffsetToMid)
{
float AnimScale, BaseSize;
GetRenderTeeAnimScaleAndBaseSize(pAnim, pInfo, AnimScale, BaseSize);
vec2 BodyPos = vec2(pAnim->GetBody()->m_X, pAnim->GetBody()->m_Y) * AnimScale;
float AssumedScale = BaseSize / 64.0f;
// just use the lowest feet
vec2 FeetPos;
CAnimKeyframe *pFoot = pAnim->GetFrontFoot();
FeetPos = vec2(pFoot->m_X * AnimScale, pFoot->m_Y * AnimScale);
pFoot = pAnim->GetBackFoot();
FeetPos = vec2(FeetPos.x, maximum(FeetPos.y, pFoot->m_Y * AnimScale));
vec2 BodyOffset;
float BodyWidth, BodyHeight;
GetRenderTeeBodySize(pAnim, pInfo, BodyOffset, BodyWidth, BodyHeight);
// -32 is the assumed min relative position for the quad
float MinY = -32.0f * AssumedScale;
// the body pos shifts the body away from center
MinY += BodyPos.y;
// the actual body is smaller tho, bcs it doesnt use the full skin image in most cases
MinY += BodyOffset.y;
vec2 FeetOffset;
float FeetWidth, FeetHeight;
GetRenderTeeFeetSize(pAnim, pInfo, FeetOffset, FeetWidth, FeetHeight);
// MaxY builds up from the MinY
float MaxY = MinY + BodyHeight;
// if the body is smaller than the total feet offset, use feet
// since feets are smaller in height, respect the assumed relative position
MaxY = maximum(MaxY, (-16.0f * AssumedScale + FeetPos.y) + FeetOffset.y + FeetHeight);
// now we got the full rendered size
float FullHeight = (MaxY - MinY);
// next step is to calculate the offset that was created compared to the assumed relative positon
float MidOfRendered = MinY + FullHeight / 2.0f;
// TODO: x coordinate is ignored for now, bcs it's not really used yet anyway
TeeOffsetToMid.x = 0;
// negative value, because the calculation that uses this offset should work with addition.
TeeOffsetToMid.y = -MidOfRendered;
}
void CRenderTools::RenderTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos, float Alpha)
{
vec2 Direction = Dir;
vec2 Position = Pos;
const CSkin::SSkinTextures *pSkinTextures = pInfo->m_CustomColoredSkin ? &pInfo->m_ColorableRenderSkin : &pInfo->m_OriginalRenderSkin;
// first pass we draw the outline
// second pass we draw the filling
for(int p = 0; p < 2; p++)
{
int OutLine = p == 0 ? 1 : 0;
for(int f = 0; f < 2; f++)
{
float AnimScale, BaseSize;
GetRenderTeeAnimScaleAndBaseSize(pAnim, pInfo, AnimScale, BaseSize);
if(f == 1)
{
Graphics()->QuadsSetRotation(pAnim->GetBody()->m_Angle * pi * 2);
// draw body
Graphics()->SetColor(pInfo->m_ColorBody.r, pInfo->m_ColorBody.g, pInfo->m_ColorBody.b, Alpha);
vec2 BodyPos = Position + vec2(pAnim->GetBody()->m_X, pAnim->GetBody()->m_Y) * AnimScale;
float BodyScale;
GetRenderTeeBodyScale(BaseSize, BodyScale);
Graphics()->TextureSet(OutLine == 1 ? pSkinTextures->m_BodyOutline : pSkinTextures->m_Body);
Graphics()->RenderQuadContainerAsSprite(m_TeeQuadContainerIndex, OutLine, BodyPos.x, BodyPos.y, BodyScale, BodyScale);
// draw eyes
if(p == 1)
{
int QuadOffset = 2;
int EyeQuadOffset = 0;
int TeeEye = 0;
switch(Emote)
{
case EMOTE_PAIN:
EyeQuadOffset = 0;
TeeEye = SPRITE_TEE_EYE_PAIN - SPRITE_TEE_EYE_NORMAL;
break;
case EMOTE_HAPPY:
EyeQuadOffset = 1;
TeeEye = SPRITE_TEE_EYE_HAPPY - SPRITE_TEE_EYE_NORMAL;
break;
case EMOTE_SURPRISE:
EyeQuadOffset = 2;
TeeEye = SPRITE_TEE_EYE_SURPRISE - SPRITE_TEE_EYE_NORMAL;
break;
case EMOTE_ANGRY:
EyeQuadOffset = 3;
TeeEye = SPRITE_TEE_EYE_ANGRY - SPRITE_TEE_EYE_NORMAL;
break;
default:
EyeQuadOffset = 4;
break;
}
float EyeScale = BaseSize * 0.40f;
float h = Emote == EMOTE_BLINK ? BaseSize * 0.15f : EyeScale;
float EyeSeparation = (0.075f - 0.010f * absolute(Direction.x)) * BaseSize;
vec2 Offset = vec2(Direction.x * 0.125f, -0.05f + Direction.y * 0.10f) * BaseSize;
Graphics()->TextureSet(pSkinTextures->m_aEyes[TeeEye]);
Graphics()->RenderQuadContainerAsSprite(m_TeeQuadContainerIndex, QuadOffset + EyeQuadOffset, BodyPos.x - EyeSeparation + Offset.x, BodyPos.y + Offset.y, EyeScale / (64.f * 0.4f), h / (64.f * 0.4f));
Graphics()->RenderQuadContainerAsSprite(m_TeeQuadContainerIndex, QuadOffset + EyeQuadOffset, BodyPos.x + EyeSeparation + Offset.x, BodyPos.y + Offset.y, -EyeScale / (64.f * 0.4f), h / (64.f * 0.4f));
}
}
// draw feet
CAnimKeyframe *pFoot = f ? pAnim->GetFrontFoot() : pAnim->GetBackFoot();
float w = BaseSize;
float h = BaseSize / 2;
int QuadOffset = 7;
Graphics()->QuadsSetRotation(pFoot->m_Angle * pi * 2);
bool Indicate = !pInfo->m_GotAirJump && g_Config.m_ClAirjumpindicator;
float ColorScale = 1.0f;
if(!OutLine)
{
++QuadOffset;
if(Indicate)
ColorScale = 0.5f;
}
Graphics()->SetColor(pInfo->m_ColorFeet.r * ColorScale, pInfo->m_ColorFeet.g * ColorScale, pInfo->m_ColorFeet.b * ColorScale, Alpha);
Graphics()->TextureSet(OutLine == 1 ? pSkinTextures->m_FeetOutline : pSkinTextures->m_Feet);
Graphics()->RenderQuadContainerAsSprite(m_TeeQuadContainerIndex, QuadOffset, Position.x + pFoot->m_X * AnimScale, Position.y + pFoot->m_Y * AnimScale, w / 64.f, h / 32.f);
}
}
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
Graphics()->QuadsSetRotation(0);
}
void CRenderTools::CalcScreenParams(float Aspect, float Zoom, float *pWidth, float *pHeight)
{
const float Amount = 1150 * 1000;
const float WMax = 1500;
const float HMax = 1050;
float f = sqrtf(Amount) / sqrtf(Aspect);
*pWidth = f * Aspect;
*pHeight = f;
// limit the view
if(*pWidth > WMax)
{
*pWidth = WMax;
*pHeight = *pWidth / Aspect;
}
if(*pHeight > HMax)
{
*pHeight = HMax;
*pWidth = *pHeight * Aspect;
}
*pWidth *= Zoom;
*pHeight *= Zoom;
}
void CRenderTools::MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY,
float ParallaxZoom, float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints)
{
float Width, Height;
CalcScreenParams(Aspect, Zoom, &Width, &Height);
float Scale = (ParallaxZoom * (Zoom - 1.0f) + 100.0f) / 100.0f / Zoom;
Width *= Scale;
Height *= Scale;
CenterX *= ParallaxX / 100.0f;
CenterY *= ParallaxY / 100.0f;
pPoints[0] = OffsetX + CenterX - Width / 2;
pPoints[1] = OffsetY + CenterY - Height / 2;
pPoints[2] = pPoints[0] + Width;
pPoints[3] = pPoints[1] + Height;
}
void CRenderTools::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom)
{
float aPoints[4];
MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, pGroup->GetParallaxZoom(),
pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), Zoom, aPoints);
Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]);
}
void CRenderTools::MapScreenToInterface(float CenterX, float CenterY)
{
float aPoints[4];
MapScreenToWorld(CenterX, CenterY, 100.0f, 100.0f, 100.0f,
0, 0, Graphics()->ScreenAspect(), 1.0f, aPoints);
Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]);
}