3274: Add skin metrics r=def- a=Jupeyy

Finally i had time for this.
This addresses the problem with skin sizes( yes @Banana090 I care for you :* )

Finally a way to calculate skin sizes correctly:

**new**
![image](https://user-images.githubusercontent.com/6654924/98458133-b1154a80-218d-11eb-9c80-1dccec36a250.png)

old:
![image](https://user-images.githubusercontent.com/6654924/98458263-1ddd1480-218f-11eb-9ff2-25c318e8e89a.png)

Maybe more obvious(left new, right old):
![image](https://user-images.githubusercontent.com/6654924/98458338-c5f2dd80-218f-11eb-83f0-d3020a39f1a2.png)


So we can center skins always.

Currently I only applied this to chat, but it should be used everywhere, where a skin is rendered(except the actual game skin)
- Scoreboard
- Skin selection
- and so on

@def- i'd really like to get this into this next release, because I know ppl with smaller skins will complain about the offset.
I'll test it a bit tomorrow, maybe others can test it too already.

## Checklist

- [x] Tested the change ingame
- [x] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test if it works standalone, system.c especially
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Jupeyy <jupjopjap@gmail.com>
This commit is contained in:
bors[bot] 2020-11-09 10:44:56 +00:00 committed by GitHub
commit 8dd1cd50ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 304 additions and 10 deletions

View file

@ -810,6 +810,8 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine)
str_copy(pCurrentLine->m_aSkinName, m_pClient->m_aClients[pCurrentLine->m_ClientID].m_aSkinName, sizeof(pCurrentLine->m_aSkinName));
pCurrentLine->m_ColorBody = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_ColorBody;
pCurrentLine->m_ColorFeet = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_ColorFeet;
pCurrentLine->m_RenderSkinMetrics = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_SkinMetrics;
pCurrentLine->m_HasRenderTee = true;
}
}
@ -883,6 +885,8 @@ void CChat::RefindSkins()
m_aLines[r].m_RenderSkin = pSkin->m_ColorableSkin;
else
m_aLines[r].m_RenderSkin = pSkin->m_OriginalSkin;
m_aLines[r].m_RenderSkinMetrics = pSkin->m_Metrics;
}
}
}
@ -1281,6 +1285,7 @@ void CChat::OnRender()
RenderInfo.m_ColorableRenderSkin = m_aLines[r].m_RenderSkin;
else
RenderInfo.m_OriginalRenderSkin = m_aLines[r].m_RenderSkin;
RenderInfo.m_SkinMetrics = m_aLines[r].m_RenderSkinMetrics;
RenderInfo.m_ColorBody = m_aLines[r].m_ColorBody;
RenderInfo.m_ColorFeet = m_aLines[r].m_ColorFeet;
@ -1289,10 +1294,12 @@ void CChat::OnRender()
float RowHeight = FONT_SIZE + RealMsgPaddingY;
float OffsetTeeY = MESSAGE_TEE_SIZE / 2.0f;
float FullHeightMinusTee = RowHeight - MESSAGE_TEE_SIZE;
float TWSkinUnreliableOffset = 1.0f; // teeworlds skins were always a bit in the ground
CAnimState *pIdleState = CAnimState::GetIdle();
RenderTools()->RenderTee(pIdleState, &RenderInfo, EMOTE_NORMAL, vec2(1, 0.1f), vec2(x + (RealMsgPaddingX + MESSAGE_TEE_SIZE) / 2.0f, y + OffsetTeeY + FullHeightMinusTee / 2.0f + TWSkinUnreliableOffset), Blend);
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &RenderInfo, OffsetToMid);
vec2 TeeRenderPos(x + (RealMsgPaddingX + MESSAGE_TEE_SIZE) / 2.0f, y + OffsetTeeY + FullHeightMinusTee / 2.0f + OffsetToMid.y);
RenderTools()->RenderTee(pIdleState, &RenderInfo, EMOTE_NORMAL, vec2(1, 0.1f), TeeRenderPos, Blend);
}
STextRenderColor TextOutline(0.f, 0.f, 0.f, 0.3f * Blend);

View file

@ -41,6 +41,7 @@ class CChat : public CComponent
char m_aSkinName[sizeof(g_Config.m_ClPlayerSkin) / sizeof(g_Config.m_ClPlayerSkin[0])];
CSkin::SSkinTextures m_RenderSkin;
CSkin::SSkinMetrics m_RenderSkinMetrics;
bool m_CustomColoredSkin;
ColorRGBA m_ColorBody;
ColorRGBA m_ColorFeet;

View file

@ -357,6 +357,7 @@ void CGhost::InitRenderInfos(CGhostItem *pGhost)
pRenderInfo->m_OriginalRenderSkin = pSkin->m_OriginalSkin;
pRenderInfo->m_ColorableRenderSkin = pSkin->m_ColorableSkin;
pRenderInfo->m_BloodColor = pSkin->m_BloodColor;
pRenderInfo->m_SkinMetrics = pSkin->m_Metrics;
pRenderInfo->m_CustomColoredSkin = pGhost->m_Skin.m_UseCustomColor;
if(pGhost->m_Skin.m_UseCustomColor)
{
@ -642,6 +643,7 @@ void CGhost::RefindSkin()
const CSkin *pSkin = m_pClient->m_pSkins->Get(SkinId);
pRenderInfo->m_OriginalRenderSkin = pSkin->m_OriginalSkin;
pRenderInfo->m_ColorableRenderSkin = pSkin->m_ColorableSkin;
pRenderInfo->m_SkinMetrics = pSkin->m_Metrics;
}
}
IntsToStr(&m_CurGhost.m_Skin.m_Skin0, 6, aSkinName);
@ -653,5 +655,6 @@ void CGhost::RefindSkin()
const CSkin *pSkin = m_pClient->m_pSkins->Get(SkinId);
pRenderInfo->m_OriginalRenderSkin = pSkin->m_OriginalSkin;
pRenderInfo->m_ColorableRenderSkin = pSkin->m_ColorableSkin;
pRenderInfo->m_SkinMetrics = pSkin->m_Metrics;
}
}

View file

@ -196,7 +196,8 @@ void CKillMessages::OnRender()
}
}
RenderTools()->RenderTee(CAnimState::GetIdle(), &m_aKillmsgs[r].m_VictimRenderInfo, EMOTE_PAIN, vec2(-1, 0), vec2(x, y + 28));
if(m_aKillmsgs[r].m_VictimID >= 0)
RenderTools()->RenderTee(CAnimState::GetIdle(), &m_aKillmsgs[r].m_VictimRenderInfo, EMOTE_PAIN, vec2(-1, 0), vec2(x, y + 28));
x -= 32.0f;
// render weapon
@ -229,7 +230,8 @@ void CKillMessages::OnRender()
// render killer tee
x -= 24.0f;
RenderTools()->RenderTee(CAnimState::GetIdle(), &m_aKillmsgs[r].m_KillerRenderInfo, EMOTE_ANGRY, vec2(1, 0), vec2(x, y + 28));
if(m_aKillmsgs[r].m_KillerID >= 0)
RenderTools()->RenderTee(CAnimState::GetIdle(), &m_aKillmsgs[r].m_KillerRenderInfo, EMOTE_ANGRY, vec2(1, 0), vec2(x, y + 28));
x -= 32.0f;
// render killer name
@ -242,3 +244,31 @@ void CKillMessages::OnRender()
y += 46.0f;
}
}
void CKillMessages::RefindSkins()
{
for(int i = 0; i < MAX_KILLMSGS; i++)
{
int r = i % MAX_KILLMSGS;
if(Client()->GameTick(g_Config.m_ClDummy) > m_aKillmsgs[r].m_Tick + 50 * 10)
continue;
if(m_aKillmsgs[r].m_KillerID >= 0)
{
CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_KillerID];
if(Client.m_aSkinName[0] != '\0')
m_aKillmsgs[r].m_KillerRenderInfo = Client.m_RenderInfo;
else
m_aKillmsgs[r].m_KillerID = -1;
}
if(m_aKillmsgs[r].m_VictimID >= 0)
{
CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_VictimID];
if(Client.m_aSkinName[0] != '\0')
m_aKillmsgs[r].m_VictimRenderInfo = Client.m_RenderInfo;
else
m_aKillmsgs[r].m_VictimID = -1;
}
}
}

View file

@ -52,6 +52,8 @@ public:
virtual void OnRender();
virtual void OnMessage(int MsgType, void *pRawMsg);
virtual void OnInit();
void RefindSkins();
};
#endif

View file

@ -469,6 +469,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
const CSkin *pSkin = m_pClient->m_pSkins->Get(m_pClient->m_pSkins->Find(Skin));
OwnSkinInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin;
OwnSkinInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin;
OwnSkinInfo.m_SkinMetrics = pSkin->m_Metrics;
OwnSkinInfo.m_CustomColoredSkin = *UseCustomColor;
if(*UseCustomColor)
{
@ -649,6 +650,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
Info.m_OriginalRenderSkin = s->m_OriginalSkin;
Info.m_ColorableRenderSkin = s->m_ColorableSkin;
Info.m_SkinMetrics = s->m_Metrics;
Item.m_Rect.HSplitTop(5.0f, 0, &Item.m_Rect); // some margin from the top
RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, 0, vec2(1.0f, 0.0f), vec2(Item.m_Rect.x + 30, Item.m_Rect.y + Item.m_Rect.h / 2));

View file

@ -654,6 +654,7 @@ void CPlayers::OnRender()
m_aRenderInfo[i].m_OriginalRenderSkin = pSkin->m_OriginalSkin;
m_aRenderInfo[i].m_ColorableRenderSkin = pSkin->m_ColorableSkin;
m_aRenderInfo[i].m_BloodColor = pSkin->m_BloodColor;
m_aRenderInfo[i].m_SkinMetrics = pSkin->m_Metrics;
m_aRenderInfo[i].m_CustomColoredSkin = IsTeamplay;
if(!IsTeamplay)
{
@ -668,6 +669,7 @@ void CPlayers::OnRender()
m_RenderInfoSpec.m_OriginalRenderSkin = pSkin->m_OriginalSkin;
m_RenderInfoSpec.m_ColorableRenderSkin = pSkin->m_ColorableSkin;
m_RenderInfoSpec.m_BloodColor = pSkin->m_BloodColor;
m_RenderInfoSpec.m_SkinMetrics = pSkin->m_Metrics;
m_RenderInfoSpec.m_CustomColoredSkin = false;
m_RenderInfoSpec.m_Size = 64.0f;

View file

@ -78,24 +78,49 @@ int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType)
for(int i = 0; i < 6; ++i)
Skin.m_OriginalSkin.m_Eyes[i] = Graphics()->LoadSpriteTexture(Info, &g_pData->m_aSprites[SPRITE_TEE_EYE_NORMAL + i]);
int BodySize = 96; // body size
int FeetGridPixelsWidth = (Info.m_Width / g_pData->m_aSprites[SPRITE_TEE_FOOT].m_pSet->m_Gridx);
int FeetGridPixelsHeight = (Info.m_Height / g_pData->m_aSprites[SPRITE_TEE_FOOT].m_pSet->m_Gridy);
int FeetWidth = g_pData->m_aSprites[SPRITE_TEE_FOOT].m_W * FeetGridPixelsWidth;
int FeetHeight = g_pData->m_aSprites[SPRITE_TEE_FOOT].m_H * FeetGridPixelsHeight;
int FeetOffsetX = g_pData->m_aSprites[SPRITE_TEE_FOOT].m_X * FeetGridPixelsWidth;
int FeetOffsetY = g_pData->m_aSprites[SPRITE_TEE_FOOT].m_Y * FeetGridPixelsHeight;
int BodySize = g_pData->m_aSprites[SPRITE_TEE_BODY].m_H * (Info.m_Height / g_pData->m_aSprites[SPRITE_TEE_BODY].m_pSet->m_Gridy); // body size
if(BodySize > Info.m_Height)
return 0;
unsigned char *d = (unsigned char *)Info.m_pData;
int Pitch = Info.m_Width * 4;
int MaxBodyY = -1;
int MinBodyY = BodySize + 1;
int MaxBodyX = -1;
int MinBodyX = BodySize + 1;
// dig out blood color
{
int aColors[3] = {0};
for(int y = 0; y < BodySize; y++)
for(int x = 0; x < BodySize; x++)
{
if(d[y * Pitch + x * 4 + 3] > 128)
uint8_t AlphaValue = d[y * Pitch + x * 4 + 3];
if(AlphaValue > 128)
{
aColors[0] += d[y * Pitch + x * 4 + 0];
aColors[1] += d[y * Pitch + x * 4 + 1];
aColors[2] += d[y * Pitch + x * 4 + 2];
}
if(AlphaValue > 0)
{
if(MaxBodyY < y)
MaxBodyY = y;
if(MinBodyY > y)
MinBodyY = y;
if(MaxBodyX < x)
MaxBodyX = x;
if(MinBodyX > x)
MinBodyX = x;
}
}
if(aColors[0] != 0 && aColors[1] != 0 && aColors[2] != 0)
Skin.m_BloodColor = ColorRGBA(normalize(vec3(aColors[0], aColors[1], aColors[2])));
@ -103,6 +128,49 @@ int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType)
Skin.m_BloodColor = ColorRGBA(0, 0, 0, 1);
}
Skin.m_Metrics.m_BodyHeight = clamp((MaxBodyY - MinBodyY) + 1, 1, BodySize);
Skin.m_Metrics.m_BodyWidth = clamp((MaxBodyX - MinBodyX) + 1, 1, BodySize);
Skin.m_Metrics.m_BodyOffsetX = clamp(MinBodyX, 0, BodySize - 1);
Skin.m_Metrics.m_BodyOffsetY = clamp(MinBodyY, 0, BodySize - 1);
Skin.m_Metrics.m_BodyMaxWidth = BodySize;
Skin.m_Metrics.m_BodyMaxHeight = BodySize;
// get feet size
{
int MaxFeetY = -1;
int MinFeetY = FeetHeight + 1;
int MaxFeetX = -1;
int MinFeetX = FeetWidth + 1;
for(int y = 0; y < FeetHeight; y++)
{
for(int x = 0; x < FeetWidth; x++)
{
int OffsetAlpha = (y + FeetOffsetY) * Pitch + (x + FeetOffsetX) * 4 + 3;
uint8_t AlphaValue = d[OffsetAlpha];
if(AlphaValue > 0)
{
if(MaxFeetY < y)
MaxFeetY = y;
if(MinFeetY > y)
MinFeetY = y;
if(MaxFeetX < x)
MaxFeetX = x;
if(MinFeetX > x)
MinFeetX = x;
}
}
}
Skin.m_Metrics.m_FeetWidth = clamp((MaxFeetX - MinFeetX) + 1, 1, FeetWidth);
Skin.m_Metrics.m_FeetHeight = clamp((MaxFeetY - MinFeetY) + 1, 1, FeetHeight);
Skin.m_Metrics.m_FeetOffsetX = clamp(MinFeetX, 0, FeetWidth - 1);
Skin.m_Metrics.m_FeetOffsetY = clamp(MinFeetY, 0, FeetHeight - 1);
Skin.m_Metrics.m_FeetMaxWidth = FeetWidth;
Skin.m_Metrics.m_FeetMaxHeight = FeetHeight;
}
// create colorless version
int Step = Info.m_Format == CImageInfo::FORMAT_RGBA ? 4 : 3;

View file

@ -1252,6 +1252,7 @@ void CGameClient::OnNewSnapshot()
const CSkin *pSkin = m_pSkins->Get(m_pSkins->Find(pClient->m_aSkinName));
pClient->m_SkinInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin;
pClient->m_SkinInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin;
pClient->m_SkinInfo.m_SkinMetrics = pSkin->m_Metrics;
pClient->m_SkinInfo.m_BloodColor = pSkin->m_BloodColor;
pClient->m_SkinInfo.m_CustomColoredSkin = pClient->m_UseCustomColor;
@ -1974,6 +1975,7 @@ void CGameClient::CClientData::Reset()
m_SkinInfo.m_CustomColoredSkin = false;
m_SkinInfo.m_ColorBody = ColorRGBA(1, 1, 1);
m_SkinInfo.m_ColorFeet = ColorRGBA(1, 1, 1);
m_SkinInfo.m_SkinMetrics.Reset();
m_Solo = false;
m_Jetpack = false;
@ -2895,6 +2897,8 @@ void CGameClient::RefindSkins()
{
for(auto &Client : m_aClients)
{
Client.m_SkinInfo.m_OriginalRenderSkin.Reset();
Client.m_SkinInfo.m_ColorableRenderSkin.Reset();
if(Client.m_aSkinName[0] != '\0')
{
const CSkin *pSkin = m_pSkins->Get(m_pSkins->Find(Client.m_aSkinName));
@ -2905,6 +2909,7 @@ void CGameClient::RefindSkins()
}
m_pGhost->RefindSkin();
m_pChat->RefindSkins();
gs_KillMessages.RefindSkins();
}
void CGameClient::LoadMapSettings()

View file

@ -540,6 +540,100 @@ void CRenderTools::DrawCircle(float x, float y, float r, int Segments)
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.BodyWidthNormalized() * 64.0f * BodyScale;
Height = pInfo->m_SkinMetrics.BodyHeightNormalized() * 64.0f * BodyScale;
BodyOffset.x = pInfo->m_SkinMetrics.BodyOffsetXNormalized() * 64.0f * BodyScale;
BodyOffset.y = pInfo->m_SkinMetrics.BodyOffsetYNormalized() * 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.FeetWidthNormalized() * 64.0f * FeetScaleWidth;
Height = pInfo->m_SkinMetrics.FeetHeightNormalized() * 32.0f * FeetScaleHeight;
FeetOffset.x = pInfo->m_SkinMetrics.FeetOffsetXNormalized() * 64.0f * FeetScaleWidth;
FeetOffset.y = pInfo->m_SkinMetrics.FeetOffsetYNormalized() * 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;
@ -555,8 +649,8 @@ void CRenderTools::RenderTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote
for(int f = 0; f < 2; f++)
{
float AnimScale = pInfo->m_Size * 1.0f / 64.0f;
float BaseSize = pInfo->m_Size;
float AnimScale, BaseSize;
GetRenderTeeAnimScaleAndBaseSize(pAnim, pInfo, AnimScale, BaseSize);
if(f == 1)
{
Graphics()->QuadsSetRotation(pAnim->GetBody()->m_Angle * pi * 2);
@ -564,9 +658,10 @@ void CRenderTools::RenderTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote
// 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 BodySize = g_Config.m_ClFatSkins ? BaseSize * 1.3f : BaseSize;
float BodyScale;
GetRenderTeeBodyScale(BaseSize, BodyScale);
Graphics()->TextureSet(OutLine == 1 ? pSkinTextures->m_BodyOutline : pSkinTextures->m_Body);
Graphics()->RenderQuadContainerAsSprite(m_TeeQuadContainerIndex, OutLine, BodyPos.x, BodyPos.y, BodySize / 64.f, BodySize / 64.f);
Graphics()->RenderQuadContainerAsSprite(m_TeeQuadContainerIndex, OutLine, BodyPos.x, BodyPos.y, BodyScale, BodyScale);
// draw eyes
if(p == 1)

View file

@ -23,6 +23,9 @@ public:
CSkin::SSkinTextures m_OriginalRenderSkin;
CSkin::SSkinTextures m_ColorableRenderSkin;
CSkin::SSkinMetrics m_SkinMetrics;
bool m_CustomColoredSkin;
ColorRGBA m_BloodColor;
@ -50,6 +53,10 @@ class CRenderTools
{
int m_TeeQuadContainerIndex;
void GetRenderTeeAnimScaleAndBaseSize(class CAnimState *pAnim, CTeeRenderInfo *pInfo, float &AnimScale, float &BaseSize);
void GetRenderTeeBodyScale(float BaseSize, float &BodyScale);
void GetRenderTeeFeetScale(float BaseSize, float &FeetScaleWidth, float &FeetScaleHeight);
public:
class IGraphics *m_pGraphics;
class CUI *m_pUI;
@ -95,6 +102,11 @@ public:
// larger rendering methods
void RenderTilemapGenerateSkip(class CLayers *pLayers);
void GetRenderTeeBodySize(class CAnimState *pAnim, CTeeRenderInfo *pInfo, vec2 &BodyOffset, float &Width, float &Height);
void GetRenderTeeFeetSize(class CAnimState *pAnim, CTeeRenderInfo *pInfo, vec2 &FeetOffset, float &Width, float &Height);
// returns the offset to use, to render the tee with @see RenderTee exactly in the mid
void GetRenderTeeOffsetToRenderedTee(class CAnimState *pAnim, CTeeRenderInfo *pInfo, vec2 &TeeOffsetToMid);
// object render methods (gc_render_obj.cpp)
void RenderTee(class CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos, float Alpha = 1.0f);

View file

@ -42,6 +42,73 @@ struct CSkin
char m_aName[24];
ColorRGBA m_BloodColor;
struct SSkinMetrics
{
int m_BodyWidth;
int m_BodyHeight;
int m_BodyOffsetX;
int m_BodyOffsetY;
// these can be used to normalize the metrics
int m_BodyMaxWidth;
int m_BodyMaxHeight;
int m_FeetWidth;
int m_FeetHeight;
int m_FeetOffsetX;
int m_FeetOffsetY;
// these can be used to normalize the metrics
int m_FeetMaxWidth;
int m_FeetMaxHeight;
void Reset()
{
m_BodyWidth = m_BodyHeight = m_BodyOffsetX = m_BodyOffsetY = m_FeetWidth = m_FeetHeight = m_FeetOffsetX = m_FeetOffsetY = -1;
}
float BodyWidthNormalized()
{
return (float)m_BodyWidth / (float)m_BodyMaxWidth;
}
float BodyHeightNormalized()
{
return (float)m_BodyHeight / (float)m_BodyMaxHeight;
}
float BodyOffsetXNormalized()
{
return (float)m_BodyOffsetX / (float)m_BodyMaxWidth;
}
float BodyOffsetYNormalized()
{
return (float)m_BodyOffsetY / (float)m_BodyMaxHeight;
}
float FeetWidthNormalized()
{
return (float)m_FeetWidth / (float)m_FeetMaxWidth;
}
float FeetHeightNormalized()
{
return (float)m_FeetHeight / (float)m_FeetMaxHeight;
}
float FeetOffsetXNormalized()
{
return (float)m_FeetOffsetX / (float)m_FeetMaxWidth;
}
float FeetOffsetYNormalized()
{
return (float)m_FeetOffsetY / (float)m_FeetMaxHeight;
}
};
SSkinMetrics m_Metrics;
bool operator<(const CSkin &Other) const { return str_comp_nocase(m_aName, Other.m_aName) < 0; }
bool operator<(const char *pOther) const { return str_comp_nocase(m_aName, pOther) < 0; }