2024-12-08 10:12:01 -08:00

356 lines
11 KiB
C#

using AssetRipper.Assets.Generics;
using AssetRipper.Numerics;
using AssetRipper.SourceGenerated.Classes.ClassID_213;
using AssetRipper.SourceGenerated.Classes.ClassID_687078895;
using AssetRipper.SourceGenerated.Enums;
using AssetRipper.SourceGenerated.Subclasses.SpriteAtlasData;
using AssetRipper.SourceGenerated.Subclasses.SpriteBone;
using AssetRipper.SourceGenerated.Subclasses.SpriteMetaData;
using AssetRipper.SourceGenerated.Subclasses.SpriteRenderData;
using AssetRipper.SourceGenerated.Subclasses.SpriteVertex;
using AssetRipper.SourceGenerated.Subclasses.SubMesh;
using AssetRipper.SourceGenerated.Subclasses.Vector2f;
using System.Buffers.Binary;
using System.Drawing;
using System.Numerics;
namespace AssetRipper.SourceGenerated.Extensions
{
public static class SpriteMetaDataExtensions
{
public static SpriteAlignment GetAlignment(this ISpriteMetaData data)
{
return (SpriteAlignment)data.Alignment;
}
public static void FillSpriteMetaData(this ISpriteMetaData instance, ISprite sprite, ISpriteAtlas? atlas)
{
sprite.GetSpriteCoordinatesInAtlas(atlas, out RectangleF rect, out Vector2 pivot, out Vector4 border);
instance.Name = sprite.Name;
instance.Rect.CopyValues(rect);
instance.Alignment = (int)SpriteAlignment.Custom;
instance.Pivot.CopyValues(pivot);
instance.Border?.CopyValues(border);
if (instance.Has_Outline())
{
GenerateOutline(sprite, atlas, rect, pivot, instance.Outline);
}
if (instance.Has_PhysicsShape() && sprite.Has_PhysicsShape())
{
GeneratePhysicsShape(sprite, atlas, rect, pivot, instance.PhysicsShape);
}
instance.TessellationDetail = 0;
if (instance.Has_Bones() && sprite.Has_Bones() && instance.Has_SpriteID())
{
// Scale bones based off of the sprite's PPU
foreach (ISpriteBone bone in sprite.Bones)
{
bone.Position.Scale(sprite.PixelsToUnits);
bone.Length *= sprite.PixelsToUnits;
// Set root bone position
if (bone.ParentId == -1)
{
bone.Position.X += sprite.Rect.Width / 2;
bone.Position.Y += sprite.Rect.Height / 2;
}
}
instance.Bones.Clear();
instance.Bones.Capacity = sprite.Bones.Count;
foreach (ISpriteBone bone in sprite.Bones)
{
instance.Bones.AddNew().CopyValues(bone);
}
// NOTE: sprite ID is generated by sprite binary content, but we just generate a random value
instance.SpriteID = Guid.NewGuid().ToString("N");
instance.SetBoneGeometry(sprite);
}
}
private static void SetBoneGeometry(this ISpriteMetaData instance, ISprite origin)
{
Vector3[]? vertices = null;
BoneWeight4[]? skin = null;
if (origin.RD.Has_VertexData())
{
VertexDataBlob.Create(origin.RD.VertexData, origin.Collection.Version, origin.Collection.EndianType).ReadData(
out vertices,
out Vector3[]? _,//normals,
out Vector4[]? _,//tangents,
out ColorFloat[]? _,//colors,
out skin,
out Vector2[]? _,//uv0,
out Vector2[]? _,//uv1,
out Vector2[]? _,//uv2,
out Vector2[]? _,//uv3,
out Vector2[]? _,//uv4,
out Vector2[]? _,//uv5,
out Vector2[]? _,//uv6,
out Vector2[]? _);//uv7);
}
if (instance.Has_Vertices())
{
instance.Vertices.Clear();
// Convert Vector3f into Vector2f
if (vertices is null)
{
instance.Vertices.Capacity = 0;
}
else
{
instance.Vertices.Capacity = vertices.Length;
for (int i = 0; i < vertices.Length; i++)
{
Vector2f vertex = instance.Vertices.AddNew();
// Scale and translate vertices properly
vertex.X = vertices[i].X * origin.PixelsToUnits + origin.Rect.Width / 2;
vertex.Y = vertices[i].Y * origin.PixelsToUnits + origin.Rect.Height / 2;
}
}
}
if (instance.Has_Indices())
{
instance.Indices.Clear();
if (origin.RD.Has_IndexBuffer() && origin.RD.IndexBuffer.Length != 0)
{
instance.Indices.Capacity = origin.RD.IndexBuffer.Length / 2;
for (int i = 0, j = 0; i < origin.RD.IndexBuffer.Length / 2; i++, j += 2)
{
//Endianness might matter here
instance.Indices.Add(BinaryPrimitives.ReadInt16LittleEndian(origin.RD.IndexBuffer.AsSpan(j, 2)));
}
}
}
#warning TODO: SpriteConverter does not generate instance.Edges
if (instance.Has_Weights())
{
instance.Weights.Clear();
if (skin is not null)
{
instance.Weights.EnsureCapacity(skin.Length);
for (int i = 0; i < skin.Length; i++)
{
instance.Weights.AddNew().CopyValues(skin[i]);
}
}
}
}
private static void GeneratePhysicsShape(
ISprite sprite,
ISpriteAtlas? atlas,
RectangleF rect,
Vector2 pivot,
AssetList<AssetList<Vector2f>> shape)
{
if (sprite.Has_PhysicsShape() && sprite.PhysicsShape.Count > 0)
{
shape.Clear();
shape.Capacity = sprite.PhysicsShape.Count;
float pivotShiftX = rect.Width * pivot.X - rect.Width * 0.5f;
float pivotShiftY = rect.Height * pivot.Y - rect.Height * 0.5f;
Vector2 pivotShift = new Vector2(pivotShiftX, pivotShiftY);
for (int i = 0; i < sprite.PhysicsShape.Count; i++)
{
AssetList<Vector2f> sourceList = sprite.PhysicsShape[i];
AssetList<Vector2f> targetList = shape.AddNew();
targetList.Capacity = sourceList.Count;
for (int j = 0; j < sprite.PhysicsShape[i].Count; j++)
{
Vector2 point = (Vector2)sourceList[j] * sprite.PixelsToUnits;
targetList.AddNew().CopyValues(point + pivotShift);
}
}
shape.FixRotation(sprite, atlas);
}
}
private static void FixRotation(this AssetList<AssetList<Vector2f>> outlines, ISprite sprite, ISpriteAtlas? atlas)
{
GetPacking(sprite, atlas, out bool isPacked, out SpritePackingRotation rotation);
if (isPacked)
{
switch (rotation)
{
case SpritePackingRotation.FlipHorizontal:
{
foreach (AssetList<Vector2f> outline in outlines)
{
for (int i = 0; i < outline.Count; i++)
{
Vector2f vertex = outline[i];
outline[i].SetValues(-vertex.X, vertex.Y);
}
}
}
break;
case SpritePackingRotation.FlipVertical:
{
foreach (AssetList<Vector2f> outline in outlines)
{
for (int i = 0; i < outline.Count; i++)
{
Vector2f vertex = outline[i];
outline[i].SetValues(vertex.X, -vertex.Y);
}
}
}
break;
case SpritePackingRotation.Rotate90:
{
foreach (AssetList<Vector2f> outline in outlines)
{
for (int i = 0; i < outline.Count; i++)
{
Vector2f vertex = outline[i];
outline[i].SetValues(vertex.Y, vertex.X);
}
}
}
break;
case SpritePackingRotation.Rotate180:
{
foreach (AssetList<Vector2f> outline in outlines)
{
for (int i = 0; i < outline.Count; i++)
{
Vector2f vertex = outline[i];
outline[i].SetValues(-vertex.X, -vertex.Y);
}
}
}
break;
}
}
}
/// <summary>
/// Pure
/// </summary>
/// <param name="sprite"></param>
/// <param name="atlas"></param>
/// <param name="isPacked"></param>
/// <param name="rotation"></param>
private static void GetPacking(ISprite sprite, ISpriteAtlas? atlas, out bool isPacked, out SpritePackingRotation rotation)
{
if (atlas is not null && sprite.Has_RenderDataKey())
{
ISpriteAtlasData atlasData = atlas.RenderDataMap[sprite.RenderDataKey];
isPacked = atlasData.IsPacked();
rotation = atlasData.GetPackingRotation();
}
else
{
isPacked = sprite.RD.IsPacked();
rotation = sprite.RD.GetPackingRotation();
}
}
private static void GenerateOutline(
ISprite sprite,
ISpriteAtlas? atlas,
RectangleF rect,
Vector2 pivot,
AssetList<AssetList<Vector2f>> outlines)
{
GenerateOutline(sprite.RD, sprite.Collection.Version, outlines);
float pivotShiftX = rect.Width * pivot.X - rect.Width * 0.5f;
float pivotShiftY = rect.Height * pivot.Y - rect.Height * 0.5f;
Vector2 pivotShift = new Vector2(pivotShiftX, pivotShiftY);
foreach (AssetList<Vector2f> outline in outlines)
{
for (int i = 0; i < outline.Count; i++)
{
Vector2 point = (Vector2)outline[i] * sprite.PixelsToUnits;
outline[i].CopyValues(point + pivotShift);
}
}
outlines.FixRotation(sprite, atlas);
}
private static void GenerateOutline(
ISpriteRenderData spriteRenderData,
UnityVersion version,
AssetList<AssetList<Vector2f>> outlines)
{
outlines.Clear();
if (spriteRenderData.Has_VertexData() && spriteRenderData.SubMeshes!.Count != 0)
{
for (int i = 0; i < spriteRenderData.SubMeshes.Count; i++)
{
Vector3[] vertices = spriteRenderData.VertexData.GenerateVertices(version, spriteRenderData.SubMeshes[i]);
List<Vector2[]> vectorArrayList = VertexDataToOutline(spriteRenderData.IndexBuffer, vertices, spriteRenderData.SubMeshes[i]);
outlines.AddRanges(vectorArrayList);
}
}
else if (spriteRenderData.Has_Vertices() && spriteRenderData.Vertices.Count != 0)
{
List<Vector2[]> vectorArrayList = VerticesToOutline(spriteRenderData.Vertices, spriteRenderData.Indices);
outlines.Capacity = vectorArrayList.Count;
outlines.AddRanges(vectorArrayList);
}
}
private static List<Vector2[]> VerticesToOutline(AccessListBase<ISpriteVertex> spriteVertexList, AssetList<ushort> spriteIndexArray)
{
Vector3[] vertices = new Vector3[spriteVertexList.Count];
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = spriteVertexList[i].Pos;
}
Vector3i[] triangles = new Vector3i[spriteIndexArray.Count / 3];
for (int i = 0, j = 0; i < triangles.Length; i++)
{
int x = spriteIndexArray[j++];
int y = spriteIndexArray[j++];
int z = spriteIndexArray[j++];
triangles[i] = new Vector3i(x, y, z);
}
return new MeshOutlineGenerator(vertices, triangles).GenerateOutlines();
}
private static List<Vector2[]> VertexDataToOutline(ReadOnlySpan<byte> indexBuffer, Vector3[] vertices, ISubMesh submesh)
{
Vector3i[] triangles = new Vector3i[submesh.IndexCount / 3];
for (int o = (int)submesh.FirstByte, ti = 0; ti < triangles.Length; o += 3 * sizeof(ushort), ti++)
{
ushort x = BitConverter.ToUInt16(indexBuffer[o..]);
ushort y = BitConverter.ToUInt16(indexBuffer[(o + sizeof(ushort))..]);
ushort z = BitConverter.ToUInt16(indexBuffer[(o + 2 * sizeof(ushort))..]);
triangles[ti] = new Vector3i(x, y, z);
}
return new MeshOutlineGenerator(vertices, triangles).GenerateOutlines();
}
private static void AddRanges(this AssetList<AssetList<Vector2f>> instance, List<Vector2[]> vectorArrayList)
{
foreach (Vector2[] vectorArray in vectorArrayList)
{
AssetList<Vector2f> assetList = instance.AddNew();
assetList.Capacity = vectorArray.Length;
foreach (Vector2 v in vectorArray)
{
assetList.AddNew().CopyValues(v);
}
}
}
}
}