mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-20 06:58:20 +00:00
Merge #3274
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:
commit
8dd1cd50ed
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,8 @@ public:
|
|||
virtual void OnRender();
|
||||
virtual void OnMessage(int MsgType, void *pRawMsg);
|
||||
virtual void OnInit();
|
||||
|
||||
void RefindSkins();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
Loading…
Reference in a new issue