348 lines
11 KiB
C#
348 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
namespace AibisDream.FrameAnimation.Editor
|
|
{
|
|
internal enum FrameAnimationImportIssueCode
|
|
{
|
|
JsonMissing,
|
|
TextureMissing,
|
|
JsonInvalid,
|
|
FramesMissing,
|
|
FrameNameInvalid,
|
|
FrameNameDuplicate,
|
|
FrameDurationInvalid,
|
|
FrameRectInvalid,
|
|
TextureSizeMismatch,
|
|
TrimmedUnsupported,
|
|
RotatedUnsupported,
|
|
TagNameInvalid,
|
|
TagNameDuplicate,
|
|
TagRangeInvalid,
|
|
TagDirectionInvalid,
|
|
TagConflict,
|
|
PlayableIdConflict,
|
|
ImportSourceIdInvalid,
|
|
ImportSourceIdDuplicate,
|
|
ImportSourceReferenceInvalid,
|
|
ImportedClipOwnershipInvalid,
|
|
TextureOwnershipConflict,
|
|
SpriteMatchFailed,
|
|
TextureImporterInvalid,
|
|
TextureWriteFailed,
|
|
SourceChanged,
|
|
ImageNameMismatch,
|
|
NoTags,
|
|
ApplyFailed
|
|
}
|
|
|
|
internal enum FrameAnimationImportChangeKind
|
|
{
|
|
Added,
|
|
Updated,
|
|
Missing,
|
|
Unchanged,
|
|
Error
|
|
}
|
|
|
|
internal enum FrameAnimationSpriteChangeKind
|
|
{
|
|
Added,
|
|
Updated,
|
|
Retained,
|
|
Unchanged,
|
|
Error
|
|
}
|
|
|
|
internal sealed class FrameAnimationImportIssue
|
|
{
|
|
public FrameAnimationValidationSeverity Severity { get; }
|
|
public FrameAnimationImportIssueCode Code { get; }
|
|
public string SourceId { get; }
|
|
public string TargetId { get; }
|
|
public string Message { get; }
|
|
|
|
public FrameAnimationImportIssue(
|
|
FrameAnimationValidationSeverity severity,
|
|
FrameAnimationImportIssueCode code,
|
|
string sourceId,
|
|
string targetId,
|
|
string message)
|
|
{
|
|
Severity = severity;
|
|
Code = code;
|
|
SourceId = sourceId ?? string.Empty;
|
|
TargetId = targetId ?? string.Empty;
|
|
Message = message ?? string.Empty;
|
|
}
|
|
}
|
|
|
|
internal sealed class AsepriteSourceFrame
|
|
{
|
|
public string FrameName { get; }
|
|
public RectInt Rect { get; }
|
|
public int DurationMs { get; }
|
|
public bool Rotated { get; }
|
|
public bool Trimmed { get; }
|
|
public RectInt SpriteSourceSize { get; }
|
|
public Vector2Int SourceSize { get; }
|
|
|
|
public AsepriteSourceFrame(
|
|
string frameName,
|
|
RectInt rect,
|
|
int durationMs,
|
|
bool rotated,
|
|
bool trimmed,
|
|
RectInt spriteSourceSize,
|
|
Vector2Int sourceSize)
|
|
{
|
|
FrameName = frameName ?? string.Empty;
|
|
Rect = rect;
|
|
DurationMs = durationMs;
|
|
Rotated = rotated;
|
|
Trimmed = trimmed;
|
|
SpriteSourceSize = spriteSourceSize;
|
|
SourceSize = sourceSize;
|
|
}
|
|
}
|
|
|
|
internal sealed class AsepriteSourceTag
|
|
{
|
|
public string Name { get; }
|
|
public int From { get; }
|
|
public int To { get; }
|
|
public string Direction { get; }
|
|
|
|
public AsepriteSourceTag(string name, int from, int to, string direction)
|
|
{
|
|
Name = name ?? string.Empty;
|
|
From = from;
|
|
To = to;
|
|
Direction = direction ?? string.Empty;
|
|
}
|
|
}
|
|
|
|
internal sealed class AsepriteSpriteSlot
|
|
{
|
|
public RectInt Rect { get; }
|
|
public string CanonicalFrameName { get; }
|
|
public IReadOnlyList<int> SourceIndices { get; }
|
|
public IReadOnlyList<string> FrameNames { get; }
|
|
|
|
public AsepriteSpriteSlot(
|
|
RectInt rect,
|
|
string canonicalFrameName,
|
|
IReadOnlyList<int> sourceIndices,
|
|
IReadOnlyList<string> frameNames)
|
|
{
|
|
Rect = rect;
|
|
CanonicalFrameName = canonicalFrameName ?? string.Empty;
|
|
SourceIndices = sourceIndices ?? Array.Empty<int>();
|
|
FrameNames = frameNames ?? Array.Empty<string>();
|
|
}
|
|
}
|
|
|
|
internal sealed class AsepriteSourceDocument
|
|
{
|
|
public IReadOnlyList<AsepriteSourceFrame> Frames { get; }
|
|
public IReadOnlyList<AsepriteSpriteSlot> SpriteSlots { get; }
|
|
public IReadOnlyDictionary<int, AsepriteSpriteSlot> SpriteSlotBySourceIndex { get; }
|
|
public IReadOnlyList<AsepriteSourceTag> Tags { get; }
|
|
public Vector2Int TextureSize { get; }
|
|
public string ImageName { get; }
|
|
|
|
public AsepriteSourceDocument(
|
|
IReadOnlyList<AsepriteSourceFrame> frames,
|
|
IReadOnlyList<AsepriteSourceTag> tags,
|
|
Vector2Int textureSize,
|
|
string imageName)
|
|
{
|
|
Frames = frames ?? Array.Empty<AsepriteSourceFrame>();
|
|
BuildSpriteSlots(Frames, out var spriteSlots, out var slotBySourceIndex);
|
|
SpriteSlots = spriteSlots;
|
|
SpriteSlotBySourceIndex = slotBySourceIndex;
|
|
Tags = tags ?? Array.Empty<AsepriteSourceTag>();
|
|
TextureSize = textureSize;
|
|
ImageName = imageName ?? string.Empty;
|
|
}
|
|
|
|
private static void BuildSpriteSlots(
|
|
IReadOnlyList<AsepriteSourceFrame> frames,
|
|
out IReadOnlyList<AsepriteSpriteSlot> spriteSlots,
|
|
out IReadOnlyDictionary<int, AsepriteSpriteSlot> slotBySourceIndex)
|
|
{
|
|
var builders = new List<SpriteSlotBuilder>();
|
|
var buildersByRect = new Dictionary<RectInt, SpriteSlotBuilder>();
|
|
for (var sourceIndex = 0; sourceIndex < frames.Count; sourceIndex++)
|
|
{
|
|
var frame = frames[sourceIndex];
|
|
if (!buildersByRect.TryGetValue(frame.Rect, out var builder))
|
|
{
|
|
builder = new SpriteSlotBuilder(frame.Rect, frame.FrameName);
|
|
buildersByRect.Add(frame.Rect, builder);
|
|
builders.Add(builder);
|
|
}
|
|
builder.SourceIndices.Add(sourceIndex);
|
|
builder.FrameNames.Add(frame.FrameName);
|
|
}
|
|
|
|
var slots = builders.Select(builder => builder.Build()).ToArray();
|
|
var index = new Dictionary<int, AsepriteSpriteSlot>();
|
|
foreach (var slot in slots)
|
|
{
|
|
foreach (var sourceIndex in slot.SourceIndices)
|
|
{
|
|
index[sourceIndex] = slot;
|
|
}
|
|
}
|
|
|
|
spriteSlots = slots;
|
|
slotBySourceIndex = index;
|
|
}
|
|
|
|
private sealed class SpriteSlotBuilder
|
|
{
|
|
public RectInt Rect { get; }
|
|
public string CanonicalFrameName { get; }
|
|
public List<int> SourceIndices { get; } = new List<int>();
|
|
public List<string> FrameNames { get; } = new List<string>();
|
|
|
|
public SpriteSlotBuilder(RectInt rect, string canonicalFrameName)
|
|
{
|
|
Rect = rect;
|
|
CanonicalFrameName = canonicalFrameName ?? string.Empty;
|
|
}
|
|
|
|
public AsepriteSpriteSlot Build()
|
|
{
|
|
return new AsepriteSpriteSlot(
|
|
Rect,
|
|
CanonicalFrameName,
|
|
SourceIndices.ToArray(),
|
|
FrameNames.ToArray());
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed class FrameAnimationClipDiff
|
|
{
|
|
public FrameAnimationImportChangeKind Kind { get; }
|
|
public string SourceTagName { get; }
|
|
public FrameClip Clip { get; }
|
|
public IReadOnlyList<int> SourceIndices { get; }
|
|
public string Summary { get; }
|
|
|
|
public FrameAnimationClipDiff(
|
|
FrameAnimationImportChangeKind kind,
|
|
string sourceTagName,
|
|
FrameClip clip,
|
|
IReadOnlyList<int> sourceIndices,
|
|
string summary)
|
|
{
|
|
Kind = kind;
|
|
SourceTagName = sourceTagName ?? string.Empty;
|
|
Clip = clip;
|
|
SourceIndices = sourceIndices ?? Array.Empty<int>();
|
|
Summary = summary ?? string.Empty;
|
|
}
|
|
}
|
|
|
|
internal sealed class FrameAnimationSpriteDiff
|
|
{
|
|
public FrameAnimationSpriteChangeKind Kind { get; }
|
|
public string FrameName { get; }
|
|
public IReadOnlyList<string> AliasFrameNames { get; }
|
|
public int SourceFrameCount => AliasFrameNames.Count;
|
|
public Rect Rect { get; }
|
|
public string Summary { get; }
|
|
|
|
public FrameAnimationSpriteDiff(
|
|
FrameAnimationSpriteChangeKind kind,
|
|
string frameName,
|
|
Rect rect,
|
|
string summary,
|
|
IReadOnlyList<string> aliasFrameNames = null)
|
|
{
|
|
Kind = kind;
|
|
FrameName = frameName ?? string.Empty;
|
|
AliasFrameNames = aliasFrameNames ?? Array.Empty<string>();
|
|
Rect = rect;
|
|
Summary = summary ?? string.Empty;
|
|
}
|
|
}
|
|
|
|
internal sealed class FrameAnimationImportSourcePreview
|
|
{
|
|
private readonly List<FrameAnimationImportIssue> issues = new List<FrameAnimationImportIssue>();
|
|
private readonly List<FrameAnimationClipDiff> clipDiffs = new List<FrameAnimationClipDiff>();
|
|
private readonly List<FrameAnimationSpriteDiff> spriteDiffs = new List<FrameAnimationSpriteDiff>();
|
|
|
|
public FrameAnimationImportSource Source { get; }
|
|
public AsepriteSourceDocument Document { get; internal set; }
|
|
public FrameAnimationSpritePlan SpritePlan { get; internal set; }
|
|
public string CurrentHash { get; internal set; } = string.Empty;
|
|
public IReadOnlyList<FrameAnimationImportIssue> Issues => issues;
|
|
public IReadOnlyList<FrameAnimationClipDiff> ClipDiffs => clipDiffs;
|
|
public IReadOnlyList<FrameAnimationSpriteDiff> SpriteDiffs => spriteDiffs;
|
|
public bool HasErrors => issues.Any(issue => issue.Severity == FrameAnimationValidationSeverity.Error);
|
|
public bool HasSpriteChanges => spriteDiffs.Any(diff =>
|
|
diff.Kind == FrameAnimationSpriteChangeKind.Added ||
|
|
diff.Kind == FrameAnimationSpriteChangeKind.Updated);
|
|
|
|
public FrameAnimationImportSourcePreview(FrameAnimationImportSource source)
|
|
{
|
|
Source = source;
|
|
}
|
|
|
|
public void AddIssue(FrameAnimationImportIssue issue)
|
|
{
|
|
if (issue != null)
|
|
{
|
|
issues.Add(issue);
|
|
}
|
|
}
|
|
|
|
public void AddClipDiff(FrameAnimationClipDiff diff)
|
|
{
|
|
if (diff != null)
|
|
{
|
|
clipDiffs.Add(diff);
|
|
}
|
|
}
|
|
|
|
public void AddSpriteDiff(FrameAnimationSpriteDiff diff)
|
|
{
|
|
if (diff != null)
|
|
{
|
|
spriteDiffs.Add(diff);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed class FrameAnimationImportPreview
|
|
{
|
|
private readonly List<FrameAnimationImportSourcePreview> sources =
|
|
new List<FrameAnimationImportSourcePreview>();
|
|
|
|
public FrameAnimationGraph Graph { get; }
|
|
public IReadOnlyList<FrameAnimationImportSourcePreview> Sources => sources;
|
|
public bool HasErrors => sources.Any(source => source.HasErrors);
|
|
public bool HasSpriteChanges => sources.Any(source => source.HasSpriteChanges);
|
|
|
|
public FrameAnimationImportPreview(FrameAnimationGraph graph)
|
|
{
|
|
Graph = graph;
|
|
}
|
|
|
|
public void AddSource(FrameAnimationImportSourcePreview preview)
|
|
{
|
|
if (preview != null)
|
|
{
|
|
sources.Add(preview);
|
|
}
|
|
}
|
|
}
|
|
}
|