mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Merge #3303
3303: Respect outline in skin metrics r=def- a=Jupeyy beast.png has no body at all, so for this skin we need to calculate the metrics with the outline. Sadly comes with more overheat ofc old: ![screenshot_2020-11-12_08-42-52](https://user-images.githubusercontent.com/6654924/98910148-15544900-24c3-11eb-8cde-3c5bfb847527.png) new: ![screenshot_2020-11-12_08-41-30](https://user-images.githubusercontent.com/6654924/98910094-0077b580-24c3-11eb-804b-7849a7d18d14.png) ## 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
cb9e546440
|
@ -55,6 +55,41 @@ int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
|
|||
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)
|
||||
{
|
||||
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 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
|
||||
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};
|
||||
|
@ -110,17 +155,6 @@ int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType)
|
|||
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])));
|
||||
|
@ -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_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);
|
||||
CheckMetrics(Skin.m_Metrics.m_Body, d, Pitch, 0, 0, BodySize, BodySize);
|
||||
|
||||
Skin.m_Metrics.m_BodyMaxWidth = BodySize;
|
||||
Skin.m_Metrics.m_BodyMaxHeight = BodySize;
|
||||
// body outline metrics
|
||||
CheckMetrics(Skin.m_Metrics.m_Body, d, Pitch, BodyOutlineOffsetX, BodyOutlineOffsetY, BodyOutlineSize, BodyOutlineSize);
|
||||
|
||||
// get feet size
|
||||
{
|
||||
int MaxFeetY = -1;
|
||||
int MinFeetY = FeetHeight + 1;
|
||||
int MaxFeetX = -1;
|
||||
int MinFeetX = FeetWidth + 1;
|
||||
CheckMetrics(Skin.m_Metrics.m_Feet, d, Pitch, FeetOffsetX, FeetOffsetY, FeetWidth, FeetHeight);
|
||||
|
||||
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;
|
||||
}
|
||||
// get feet outline size
|
||||
CheckMetrics(Skin.m_Metrics.m_Feet, d, Pitch, FeetOutlineOffsetX, FeetOutlineOffsetY, FeetOutlineWidth, FeetOutlineHeight);
|
||||
|
||||
// create colorless version
|
||||
int Step = Info.m_Format == CImageInfo::FORMAT_RGBA ? 4 : 3;
|
||||
|
|
|
@ -566,10 +566,10 @@ void CRenderTools::GetRenderTeeBodySize(CAnimState *pAnim, CTeeRenderInfo *pInfo
|
|||
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;
|
||||
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)
|
||||
|
@ -580,10 +580,10 @@ void CRenderTools::GetRenderTeeFeetSize(CAnimState *pAnim, CTeeRenderInfo *pInfo
|
|||
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;
|
||||
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)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <base/tl/sorted_array.h>
|
||||
#include <base/vmath.h>
|
||||
#include <engine/graphics.h>
|
||||
#include <limits>
|
||||
|
||||
// do this better and nicer
|
||||
struct CSkin
|
||||
|
@ -42,16 +43,80 @@ struct CSkin
|
|||
char m_aName[24];
|
||||
ColorRGBA m_BloodColor;
|
||||
|
||||
struct SSkinMetrics
|
||||
template<bool IsSizeType>
|
||||
struct SSkinMetricVariableInt
|
||||
{
|
||||
int m_BodyWidth;
|
||||
int m_BodyHeight;
|
||||
int m_BodyOffsetX;
|
||||
int m_BodyOffsetY;
|
||||
int m_Value;
|
||||
operator int() { return m_Value; }
|
||||
SSkinMetricVariableInt &operator=(int NewVal)
|
||||
{
|
||||
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
|
||||
int m_BodyMaxWidth;
|
||||
int m_BodyMaxHeight;
|
||||
SSkinMetricVariableInt<true> m_MaxWidth;
|
||||
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_FeetHeight;
|
||||
|
@ -64,47 +129,13 @@ struct CSkin
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
Reset();
|
||||
}
|
||||
};
|
||||
SSkinMetrics m_Metrics;
|
||||
|
|
Loading…
Reference in a new issue