mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-20 06:58:20 +00:00
Respect outline in skin metrics
This commit is contained in:
parent
3d26162375
commit
0c231c3668
|
@ -55,6 +55,41 @@ int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
|
||||||
return pSelf->LoadSkin(aNameWithoutPng, aBuf, DirType);
|
return pSelf->LoadSkin(aNameWithoutPng, aBuf, DirType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void CheckMetrics(CSkin::SSkinMetricVariable &Metrics, uint8_t *pImg, int ImgWidth, int ImgX, int ImgY, int CheckWidth, int CheckHeight)
|
||||||
|
{
|
||||||
|
int MaxY = -1;
|
||||||
|
int MinY = CheckHeight + 1;
|
||||||
|
int MaxX = -1;
|
||||||
|
int MinX = CheckWidth + 1;
|
||||||
|
|
||||||
|
for(int y = 0; y < CheckHeight; y++)
|
||||||
|
{
|
||||||
|
for(int x = 0; x < CheckWidth; x++)
|
||||||
|
{
|
||||||
|
int OffsetAlpha = (y + ImgY) * ImgWidth + (x + ImgX) * 4 + 3;
|
||||||
|
uint8_t AlphaValue = pImg[OffsetAlpha];
|
||||||
|
if(AlphaValue > 0)
|
||||||
|
{
|
||||||
|
if(MaxY < y)
|
||||||
|
MaxY = y;
|
||||||
|
if(MinY > y)
|
||||||
|
MinY = y;
|
||||||
|
if(MaxX < x)
|
||||||
|
MaxX = x;
|
||||||
|
if(MinX > x)
|
||||||
|
MinX = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Metrics.m_Width = clamp((MaxX - MinX) + 1, 1, CheckWidth);
|
||||||
|
Metrics.m_Height = clamp((MaxY - MinY) + 1, 1, CheckHeight);
|
||||||
|
Metrics.m_OffsetX = clamp(MinX, 0, CheckWidth - 1);
|
||||||
|
Metrics.m_OffsetY = clamp(MinY, 0, CheckHeight - 1);
|
||||||
|
Metrics.m_MaxWidth = CheckWidth;
|
||||||
|
Metrics.m_MaxHeight = CheckHeight;
|
||||||
|
}
|
||||||
|
|
||||||
int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType)
|
int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType)
|
||||||
{
|
{
|
||||||
char aBuf[512];
|
char aBuf[512];
|
||||||
|
@ -86,17 +121,27 @@ int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType)
|
||||||
int FeetOffsetX = g_pData->m_aSprites[SPRITE_TEE_FOOT].m_X * FeetGridPixelsWidth;
|
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 FeetOffsetY = g_pData->m_aSprites[SPRITE_TEE_FOOT].m_Y * FeetGridPixelsHeight;
|
||||||
|
|
||||||
|
int FeetOutlineGridPixelsWidth = (Info.m_Width / g_pData->m_aSprites[SPRITE_TEE_FOOT_OUTLINE].m_pSet->m_Gridx);
|
||||||
|
int FeetOutlineGridPixelsHeight = (Info.m_Height / g_pData->m_aSprites[SPRITE_TEE_FOOT_OUTLINE].m_pSet->m_Gridy);
|
||||||
|
int FeetOutlineWidth = g_pData->m_aSprites[SPRITE_TEE_FOOT_OUTLINE].m_W * FeetOutlineGridPixelsWidth;
|
||||||
|
int FeetOutlineHeight = g_pData->m_aSprites[SPRITE_TEE_FOOT_OUTLINE].m_H * FeetOutlineGridPixelsHeight;
|
||||||
|
|
||||||
|
int FeetOutlineOffsetX = g_pData->m_aSprites[SPRITE_TEE_FOOT_OUTLINE].m_X * FeetOutlineGridPixelsWidth;
|
||||||
|
int FeetOutlineOffsetY = g_pData->m_aSprites[SPRITE_TEE_FOOT_OUTLINE].m_Y * FeetOutlineGridPixelsHeight;
|
||||||
|
|
||||||
|
int BodyOutlineGridPixelsWidth = (Info.m_Width / g_pData->m_aSprites[SPRITE_TEE_BODY_OUTLINE].m_pSet->m_Gridx);
|
||||||
|
int BodyOutlineGridPixelsHeight = (Info.m_Height / g_pData->m_aSprites[SPRITE_TEE_BODY_OUTLINE].m_pSet->m_Gridy);
|
||||||
|
int BodyOutlineSize = g_pData->m_aSprites[SPRITE_TEE_BODY_OUTLINE].m_H * BodyOutlineGridPixelsHeight;
|
||||||
|
|
||||||
|
int BodyOutlineOffsetX = g_pData->m_aSprites[SPRITE_TEE_BODY_OUTLINE].m_X * BodyOutlineGridPixelsWidth;
|
||||||
|
int BodyOutlineOffsetY = g_pData->m_aSprites[SPRITE_TEE_BODY_OUTLINE].m_Y * BodyOutlineGridPixelsHeight;
|
||||||
|
|
||||||
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
|
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)
|
if(BodySize > Info.m_Height)
|
||||||
return 0;
|
return 0;
|
||||||
unsigned char *d = (unsigned char *)Info.m_pData;
|
unsigned char *d = (unsigned char *)Info.m_pData;
|
||||||
int Pitch = Info.m_Width * 4;
|
int Pitch = Info.m_Width * 4;
|
||||||
|
|
||||||
int MaxBodyY = -1;
|
|
||||||
int MinBodyY = BodySize + 1;
|
|
||||||
int MaxBodyX = -1;
|
|
||||||
int MinBodyX = BodySize + 1;
|
|
||||||
|
|
||||||
// dig out blood color
|
// dig out blood color
|
||||||
{
|
{
|
||||||
int aColors[3] = {0};
|
int aColors[3] = {0};
|
||||||
|
@ -110,17 +155,6 @@ int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType)
|
||||||
aColors[1] += d[y * Pitch + x * 4 + 1];
|
aColors[1] += d[y * Pitch + x * 4 + 1];
|
||||||
aColors[2] += d[y * Pitch + x * 4 + 2];
|
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)
|
if(aColors[0] != 0 && aColors[1] != 0 && aColors[2] != 0)
|
||||||
Skin.m_BloodColor = ColorRGBA(normalize(vec3(aColors[0], aColors[1], aColors[2])));
|
Skin.m_BloodColor = ColorRGBA(normalize(vec3(aColors[0], aColors[1], aColors[2])));
|
||||||
|
@ -128,48 +162,16 @@ int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType)
|
||||||
Skin.m_BloodColor = ColorRGBA(0, 0, 0, 1);
|
Skin.m_BloodColor = ColorRGBA(0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Skin.m_Metrics.m_BodyHeight = clamp((MaxBodyY - MinBodyY) + 1, 1, BodySize);
|
CheckMetrics(Skin.m_Metrics.m_Body, d, Pitch, 0, 0, BodySize, 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;
|
// body outline metrics
|
||||||
Skin.m_Metrics.m_BodyMaxHeight = BodySize;
|
CheckMetrics(Skin.m_Metrics.m_Body, d, Pitch, BodyOutlineOffsetX, BodyOutlineOffsetY, BodyOutlineSize, BodyOutlineSize);
|
||||||
|
|
||||||
// get feet size
|
// get feet size
|
||||||
{
|
CheckMetrics(Skin.m_Metrics.m_Feet, d, Pitch, FeetOffsetX, FeetOffsetY, FeetWidth, FeetHeight);
|
||||||
int MaxFeetY = -1;
|
|
||||||
int MinFeetY = FeetHeight + 1;
|
|
||||||
int MaxFeetX = -1;
|
|
||||||
int MinFeetX = FeetWidth + 1;
|
|
||||||
|
|
||||||
for(int y = 0; y < FeetHeight; y++)
|
// get feet outline size
|
||||||
{
|
CheckMetrics(Skin.m_Metrics.m_Feet, d, Pitch, FeetOutlineOffsetX, FeetOutlineOffsetY, FeetOutlineWidth, FeetOutlineHeight);
|
||||||
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
|
// create colorless version
|
||||||
int Step = Info.m_Format == CImageInfo::FORMAT_RGBA ? 4 : 3;
|
int Step = Info.m_Format == CImageInfo::FORMAT_RGBA ? 4 : 3;
|
||||||
|
|
|
@ -566,10 +566,10 @@ void CRenderTools::GetRenderTeeBodySize(CAnimState *pAnim, CTeeRenderInfo *pInfo
|
||||||
float BodyScale;
|
float BodyScale;
|
||||||
GetRenderTeeBodyScale(BaseSize, BodyScale);
|
GetRenderTeeBodyScale(BaseSize, BodyScale);
|
||||||
|
|
||||||
Width = pInfo->m_SkinMetrics.BodyWidthNormalized() * 64.0f * BodyScale;
|
Width = pInfo->m_SkinMetrics.m_Body.WidthNormalized() * 64.0f * BodyScale;
|
||||||
Height = pInfo->m_SkinMetrics.BodyHeightNormalized() * 64.0f * BodyScale;
|
Height = pInfo->m_SkinMetrics.m_Body.HeightNormalized() * 64.0f * BodyScale;
|
||||||
BodyOffset.x = pInfo->m_SkinMetrics.BodyOffsetXNormalized() * 64.0f * BodyScale;
|
BodyOffset.x = pInfo->m_SkinMetrics.m_Body.OffsetXNormalized() * 64.0f * BodyScale;
|
||||||
BodyOffset.y = pInfo->m_SkinMetrics.BodyOffsetYNormalized() * 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)
|
void CRenderTools::GetRenderTeeFeetSize(CAnimState *pAnim, CTeeRenderInfo *pInfo, vec2 &FeetOffset, float &Width, float &Height)
|
||||||
|
@ -580,10 +580,10 @@ void CRenderTools::GetRenderTeeFeetSize(CAnimState *pAnim, CTeeRenderInfo *pInfo
|
||||||
float FeetScaleWidth, FeetScaleHeight;
|
float FeetScaleWidth, FeetScaleHeight;
|
||||||
GetRenderTeeFeetScale(BaseSize, FeetScaleWidth, FeetScaleHeight);
|
GetRenderTeeFeetScale(BaseSize, FeetScaleWidth, FeetScaleHeight);
|
||||||
|
|
||||||
Width = pInfo->m_SkinMetrics.FeetWidthNormalized() * 64.0f * FeetScaleWidth;
|
Width = pInfo->m_SkinMetrics.m_Feet.WidthNormalized() * 64.0f * FeetScaleWidth;
|
||||||
Height = pInfo->m_SkinMetrics.FeetHeightNormalized() * 32.0f * FeetScaleHeight;
|
Height = pInfo->m_SkinMetrics.m_Feet.HeightNormalized() * 32.0f * FeetScaleHeight;
|
||||||
FeetOffset.x = pInfo->m_SkinMetrics.FeetOffsetXNormalized() * 64.0f * FeetScaleWidth;
|
FeetOffset.x = pInfo->m_SkinMetrics.m_Feet.OffsetXNormalized() * 64.0f * FeetScaleWidth;
|
||||||
FeetOffset.y = pInfo->m_SkinMetrics.FeetOffsetYNormalized() * 32.0f * FeetScaleHeight;
|
FeetOffset.y = pInfo->m_SkinMetrics.m_Feet.OffsetYNormalized() * 32.0f * FeetScaleHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRenderTools::GetRenderTeeOffsetToRenderedTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, vec2 &TeeOffsetToMid)
|
void CRenderTools::GetRenderTeeOffsetToRenderedTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, vec2 &TeeOffsetToMid)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <base/tl/sorted_array.h>
|
#include <base/tl/sorted_array.h>
|
||||||
#include <base/vmath.h>
|
#include <base/vmath.h>
|
||||||
#include <engine/graphics.h>
|
#include <engine/graphics.h>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
// do this better and nicer
|
// do this better and nicer
|
||||||
struct CSkin
|
struct CSkin
|
||||||
|
@ -42,16 +43,80 @@ struct CSkin
|
||||||
char m_aName[24];
|
char m_aName[24];
|
||||||
ColorRGBA m_BloodColor;
|
ColorRGBA m_BloodColor;
|
||||||
|
|
||||||
struct SSkinMetrics
|
template<bool IsSizeType>
|
||||||
|
struct SSkinMetricVariableInt
|
||||||
{
|
{
|
||||||
int m_BodyWidth;
|
int m_Value;
|
||||||
int m_BodyHeight;
|
operator int() { return m_Value; }
|
||||||
int m_BodyOffsetX;
|
SSkinMetricVariableInt &operator=(int NewVal)
|
||||||
int m_BodyOffsetY;
|
{
|
||||||
|
if(IsSizeType)
|
||||||
|
m_Value = maximum(m_Value, NewVal);
|
||||||
|
else
|
||||||
|
m_Value = minimum(m_Value, NewVal);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSkinMetricVariableInt()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
if(IsSizeType)
|
||||||
|
m_Value = std::numeric_limits<int>::lowest();
|
||||||
|
else
|
||||||
|
m_Value = std::numeric_limits<int>::max();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SSkinMetricVariable
|
||||||
|
{
|
||||||
|
SSkinMetricVariableInt<true> m_Width;
|
||||||
|
SSkinMetricVariableInt<true> m_Height;
|
||||||
|
SSkinMetricVariableInt<false> m_OffsetX;
|
||||||
|
SSkinMetricVariableInt<false> m_OffsetY;
|
||||||
|
|
||||||
// these can be used to normalize the metrics
|
// these can be used to normalize the metrics
|
||||||
int m_BodyMaxWidth;
|
SSkinMetricVariableInt<true> m_MaxWidth;
|
||||||
int m_BodyMaxHeight;
|
SSkinMetricVariableInt<true> m_MaxHeight;
|
||||||
|
|
||||||
|
float WidthNormalized()
|
||||||
|
{
|
||||||
|
return (float)m_Width / (float)m_MaxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HeightNormalized()
|
||||||
|
{
|
||||||
|
return (float)m_Height / (float)m_MaxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
float OffsetXNormalized()
|
||||||
|
{
|
||||||
|
return (float)m_OffsetX / (float)m_MaxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
float OffsetYNormalized()
|
||||||
|
{
|
||||||
|
return (float)m_OffsetY / (float)m_MaxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
m_Width.Reset();
|
||||||
|
m_Height.Reset();
|
||||||
|
m_OffsetX.Reset();
|
||||||
|
m_OffsetY.Reset();
|
||||||
|
m_MaxWidth.Reset();
|
||||||
|
m_MaxHeight.Reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SSkinMetrics
|
||||||
|
{
|
||||||
|
SSkinMetricVariable m_Body;
|
||||||
|
SSkinMetricVariable m_Feet;
|
||||||
|
|
||||||
int m_FeetWidth;
|
int m_FeetWidth;
|
||||||
int m_FeetHeight;
|
int m_FeetHeight;
|
||||||
|
@ -64,47 +129,13 @@ struct CSkin
|
||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
{
|
{
|
||||||
m_BodyWidth = m_BodyHeight = m_BodyOffsetX = m_BodyOffsetY = m_FeetWidth = m_FeetHeight = m_FeetOffsetX = m_FeetOffsetY = -1;
|
m_Body.Reset();
|
||||||
|
m_Feet.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
float BodyWidthNormalized()
|
SSkinMetrics()
|
||||||
{
|
{
|
||||||
return (float)m_BodyWidth / (float)m_BodyMaxWidth;
|
Reset();
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
SSkinMetrics m_Metrics;
|
||||||
|
|
Loading…
Reference in a new issue