mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-14 03:58:18 +00:00
439 lines
15 KiB
C++
439 lines
15 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>
|
|
#include <game/mapitems_ex.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);
|
|
|
|
// Feet
|
|
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);
|
|
|
|
// Mirrored Feet
|
|
Graphics()->QuadsSetSubsetFree(1, 0, 0, 0, 0, 1, 1, 1);
|
|
QuadContainerAddSprite(m_TeeQuadContainerIndex, -32.f, -16.f, 64.f, 32.f);
|
|
Graphics()->QuadsSetSubsetFree(1, 0, 0, 0, 0, 1, 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::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 though, because it doesn't 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 feet 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 position
|
|
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;
|
|
if(Dir.x < 0 && pInfo->m_FeetFlipped)
|
|
{
|
|
QuadOffset += 2;
|
|
}
|
|
|
|
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, CMapItemGroupEx *pGroupEx, float Zoom)
|
|
{
|
|
float ParallaxZoom = GetParallaxZoom(pGroup, pGroupEx);
|
|
float aPoints[4];
|
|
MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, ParallaxZoom,
|
|
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]);
|
|
}
|