Improved skin paper doll draw function

This commit is contained in:
MayNL
2026-06-16 16:26:38 -04:00
parent ecec8dbd16
commit 2a83292807
+135 -40
View File
@@ -7,11 +7,7 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace PckStudio.Core.Extensions
{
@@ -95,6 +91,7 @@ namespace PckStudio.Core.Extensions
gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
gfx.SmoothingMode = SmoothingMode.None;
gfx.CompositingMode = CompositingMode.SourceOver;
SkinBOX baseHeadBox = new SkinBOX("HEAD", new(-4, -8, -4), new(8), new(0, 0));
SkinBOX baseTorsoBox = new SkinBOX("BODY", new(-4, 0, -2), new(8, 12, 4), new(16, 16));
@@ -165,17 +162,29 @@ namespace PckStudio.Core.Extensions
baseBoxes.Add(new SkinBOX("PANTS1", new(-2, 0, -2), new(4, 12, 4), new(0, 48), scale: 0.25f));
}
skinBoxes.AddRange(baseBoxes);
HashSet<SkinBOX> baseBoxSet = new(baseBoxes);
baseBoxes.Clear();
skinBoxes.AddRange(baseBoxSet);
skinBoxes.AddRange(skin.Model.AdditionalBoxes);
skinBoxes = skinBoxes
.OrderByDescending(box =>
isStatueOfLiberty && box.Type == "ARM0"
? -box.Pos.Z
: box.Pos.Z
)
.ThenBy(box => box.Scale)
.ToList();
skinBoxes.Sort((first, second) =>
{
float aZPos = isStatueOfLiberty && first.Type == "ARM0"
? -first.Pos.Z
: first.Pos.Z;
float bZPos = isStatueOfLiberty && second.Type == "ARM0"
? -second.Pos.Z
: second.Pos.Z;
int result = bZPos.CompareTo(aZPos);
return result != 0
? result
: first.Scale.CompareTo(second.Scale);
});
float canvasWidth = 0;
float canvasHeight = 0;
@@ -190,9 +199,17 @@ namespace PckStudio.Core.Extensions
float defaultDrawOffsetX = paperDoll.Width / 2f; // draw at center of the canvas
float defaultDrawOffsetY = paperDoll.Height / 2f - torsoCenterY * pixelScale; // draw from center of the torso box
var drawFunctions = new List<(
Image BackFace,
Image FrontFace,
RectangleF DrawRect,
bool StatueOfLibertyArm
)>();
var partOffsets = skin.Model.PartOffsets
.ToDictionary(x => x.Type, x => x.Value);
foreach (SkinBOX box in skinBoxes)
{
try
{
float boxWidth = box.Size.X;
float boxHeight = box.Size.Y;
@@ -213,13 +230,35 @@ namespace PckStudio.Core.Extensions
(int)(boxHeight * textureScaleY)
);
// get back of the face to display behind just incase transparent parts exist on the front layer
Rectangle backFaceRect = new Rectangle(
!isStatueOfLibertyArm // get front of ARM 0 box if Statue of Liberty
? (int)((box.UV.X + boxWidth + 2 * boxDepth) * textureScaleX) // depth + width + depth = offset for back of box
: (int)((box.UV.X + boxDepth) * textureScaleX),
(int)((box.UV.Y + boxDepth) * textureScaleY),
(int)(boxWidth * textureScaleX),
(int)(boxHeight * textureScaleY)
);
Image boxFace = skin.Texture.GetArea(faceRect);
if (box.Mirror) boxFace.RotateFlip(RotateFlipType.RotateNoneFlipX);
Image backBoxFace = skin.Texture.GetArea(backFaceRect);
if (box.Mirror)
{
boxFace.RotateFlip(RotateFlipType.RotateNoneFlipX);
}
else
{
backBoxFace.RotateFlip(RotateFlipType.RotateNoneFlipX);
}
if (isStatueOfLibertyArm)
{
boxFace.RotateFlip(RotateFlipType.RotateNoneFlipY);
backBoxFace.RotateFlip(RotateFlipType.RotateNoneFlipY);
}
// these values are added to later
float drawOffsetX = defaultDrawOffsetX; // draw at center of the canvas
float drawOffsetY = defaultDrawOffsetY;
@@ -232,49 +271,62 @@ namespace PckStudio.Core.Extensions
float legXOffset = GameConstants.SkinLeftLegTranslation.X * pixelScale;
float legYOffset = GameConstants.SkinLeftLegTranslation.Y * pixelScale;
float offset = 0;
switch (box.Type)
{
case "HEAD":
case "HEADWEAR":
drawOffsetY += skin.Model.PartOffsets.Find(x => x.Type == "HEAD").Value * pixelScale;
partOffsets.TryGetValue("HEAD", out offset);
break;
case "BODY":
case "WAIST":
case "JACKET":
case "BELT":
case "BODYARMOR":
drawOffsetY += skin.Model.PartOffsets.Find(x => x.Type == "BODY").Value * pixelScale;
partOffsets.TryGetValue("BODY", out offset);
break;
case "ARM0":
case "SLEEVE0":
case "ARMARMOR0":
partOffsets.TryGetValue("ARM0", out offset);
drawOffsetX -= armXOffset;
drawOffsetY += armYOffset + skin.Model.PartOffsets.Find(x => x.Type == "ARM0").Value * pixelScale;
drawOffsetY += armYOffset;
break;
case "ARM1":
case "SLEEVE1":
case "ARMARMOR1":
partOffsets.TryGetValue("ARM1", out offset);
drawOffsetX += armXOffset;
drawOffsetY += armYOffset + skin.Model.PartOffsets.Find(x => x.Type == "ARM1").Value * pixelScale;
drawOffsetY += armYOffset;
break;
case "LEG0":
case "PANTS0":
case "LEGGING0":
case "SOCK0":
case "BOOT0":
partOffsets.TryGetValue("BOOT0", out offset);
drawOffsetX -= legXOffset;
drawOffsetY += legYOffset + skin.Model.PartOffsets.Find(x => x.Type == "LEG0").Value * pixelScale;
drawOffsetY += legYOffset;
break;
case "LEG1":
case "PANTS1":
case "LEGGING1":
case "SOCK1":
case "BOOT1":
partOffsets.TryGetValue("BOOT1", out offset);
drawOffsetX += legXOffset;
drawOffsetY += legYOffset + skin.Model.PartOffsets.Find(x => x.Type == "LEG1").Value * pixelScale;
drawOffsetY += legYOffset;
break;
}
drawOffsetY += offset * pixelScale;
float boxPosY = box.Pos.Y;
if (isStatueOfLibertyArm)
@@ -291,7 +343,7 @@ namespace PckStudio.Core.Extensions
// handle Box scale
float boxInflate =
// if XMLVersion 3 or a base box; then return box scale
(xmlVersion == 3 || baseBoxes.Contains(box)) ? box.Scale
(xmlVersion == 3 || baseBoxSet.Contains(box)) ? box.Scale
// if not; then check that it is an overlay box
: (xmlVersion > 0 && xmlVersion < 3 && SkinBOX.OverlayTypes.Contains(box.Type))
// if so, return hardcoded box inflate value
@@ -314,7 +366,10 @@ namespace PckStudio.Core.Extensions
float pivotX = defaultDrawOffsetX + GameConstants.SkinRightArmPivot.X * pixelScale;
float pivotY = defaultDrawOffsetY + GameConstants.SkinRightArmPivot.Y * pixelScale;
float angle = -8f * (float)Math.PI / 180f;
const float Angle = -8f * (float)Math.PI / 180f;
float cos = (float)Math.Cos(Angle);
float sin = (float)Math.Sin(Angle);
// used to calculate if the bounds of the arm go beyond the current bounds of the image so that the entire skin is shown
PointF[] armBounds =
@@ -330,24 +385,14 @@ namespace PckStudio.Core.Extensions
float finalX = bound.X - pivotX;
float finalY = bound.Y - pivotY;
float rotateX = pivotX + (finalX * (float)Math.Cos(angle) - finalY * (float)Math.Sin(angle));
float rotateY = pivotY + (finalX * (float)Math.Sin(angle) + finalY * (float)Math.Cos(angle));
float rotateX = pivotX + (finalX * cos - finalY * sin);
float rotateY = pivotY + (finalX * sin + finalY * cos);
canvasMinX = Math.Min(canvasMinX, rotateX);
canvasMinY = Math.Min(canvasMinY, rotateY);
canvasMaxX = Math.Max(canvasMaxX, rotateX);
canvasMaxY = Math.Max(canvasMaxY, rotateY);
}
var state = gfx.Save();
gfx.TranslateTransform(pivotX, pivotY);
gfx.RotateTransform(-8f);
gfx.TranslateTransform(-pivotX, -pivotY);
gfx.DrawImage(boxFace, boxRect);
gfx.Restore(state);
}
else
{
@@ -355,13 +400,63 @@ namespace PckStudio.Core.Extensions
canvasMinY = Math.Min(canvasMinY, finalDrawY);
canvasMaxX = Math.Max(canvasMaxX, finalDrawX + finalDrawWidth);
canvasMaxY = Math.Max(canvasMaxY, finalDrawY + finalDrawHeight);
}
gfx.DrawImage(boxFace, boxRect);
drawFunctions.Add((
backBoxFace,
boxFace,
boxRect,
isStatueOfLiberty && box.Type == "ARM0"
));
}
}
catch (Exception ex)
// draw back faces first
for (int i = drawFunctions.Count - 1; i >= 0; i--)
{
Console.WriteLine(ex.Message);
var function = drawFunctions[i];
if (function.StatueOfLibertyArm)
{
float pivotX = defaultDrawOffsetX + GameConstants.SkinRightArmPivot.X * pixelScale;
float pivotY = defaultDrawOffsetY + GameConstants.SkinRightArmPivot.Y * pixelScale;
var state = gfx.Save();
gfx.TranslateTransform(pivotX, pivotY);
gfx.RotateTransform(-8f);
gfx.TranslateTransform(-pivotX, -pivotY);
gfx.DrawImage(function.BackFace, function.DrawRect);
gfx.Restore(state);
}
else
{
gfx.DrawImage(function.BackFace, function.DrawRect);
}
}
// then draw all fronts overtop
foreach (var function in drawFunctions)
{
if (function.StatueOfLibertyArm)
{
float pivotX = defaultDrawOffsetX + GameConstants.SkinRightArmPivot.X * pixelScale;
float pivotY = defaultDrawOffsetY + GameConstants.SkinRightArmPivot.Y * pixelScale;
var state = gfx.Save();
gfx.TranslateTransform(pivotX, pivotY);
gfx.RotateTransform(-8f);
gfx.TranslateTransform(-pivotX, -pivotY);
gfx.DrawImage(function.FrontFace, function.DrawRect);
gfx.Restore(state);
}
else
{
gfx.DrawImage(function.FrontFace, function.DrawRect);
}
}