736 lines
28 KiB
C#
736 lines
28 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using UnityEngine;
|
|||
|
using UnityEditor.U2D.Common;
|
|||
|
using UnityEditor.U2D.Layout;
|
|||
|
using UnityEngine.U2D;
|
|||
|
|
|||
|
namespace UnityEditor.U2D.Animation
|
|||
|
{
|
|||
|
internal interface ICopyToolStringStore
|
|||
|
{
|
|||
|
string stringStore
|
|||
|
{
|
|||
|
get;
|
|||
|
set;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
internal class SystemCopyBufferStringStore : ICopyToolStringStore
|
|||
|
{
|
|||
|
public string stringStore
|
|||
|
{
|
|||
|
get => EditorGUIUtility.systemCopyBuffer;
|
|||
|
set => EditorGUIUtility.systemCopyBuffer = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
internal class BoneStorage
|
|||
|
{
|
|||
|
public BoneCache[] bones;
|
|||
|
public Dictionary<string, string> boneMapping;
|
|||
|
|
|||
|
public BoneStorage(BoneCache[] bones, Dictionary<string, string> boneMapping = null)
|
|||
|
{
|
|||
|
this.bones = bones;
|
|||
|
this.boneMapping = boneMapping ?? new Dictionary<string, string>();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
internal class CopyTool : MeshToolWrapper
|
|||
|
{
|
|||
|
ICopyToolStringStore m_CopyToolStringStore;
|
|||
|
CopyToolView m_CopyToolView;
|
|||
|
bool m_HasValidCopyData = false;
|
|||
|
int m_LastCopyDataHash;
|
|||
|
|
|||
|
public float pixelsPerUnit
|
|||
|
{
|
|||
|
private get;
|
|||
|
set;
|
|||
|
}
|
|||
|
|
|||
|
public bool hasValidCopiedData
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
var hashCode = m_CopyToolStringStore.stringStore.GetHashCode();
|
|||
|
if (hashCode != m_LastCopyDataHash)
|
|||
|
{
|
|||
|
m_HasValidCopyData = IsValidCopyData(m_CopyToolStringStore.stringStore);
|
|||
|
m_LastCopyDataHash = hashCode;
|
|||
|
}
|
|||
|
return m_HasValidCopyData;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public ICopyToolStringStore copyToolStringStore
|
|||
|
{
|
|||
|
set => m_CopyToolStringStore = value;
|
|||
|
}
|
|||
|
|
|||
|
internal override void OnCreate()
|
|||
|
{
|
|||
|
m_CopyToolView = new CopyToolView();
|
|||
|
m_CopyToolView.onPasteActivated += OnPasteActivated;
|
|||
|
m_CopyToolStringStore = new SystemCopyBufferStringStore();
|
|||
|
disableMeshEditor = true;
|
|||
|
}
|
|||
|
|
|||
|
public override void Initialize(LayoutOverlay layout)
|
|||
|
{
|
|||
|
m_CopyToolView.Initialize(layout);
|
|||
|
}
|
|||
|
|
|||
|
protected override void OnActivate()
|
|||
|
{
|
|||
|
base.OnActivate();
|
|||
|
m_CopyToolView.Show(skinningCache.bonesReadOnly);
|
|||
|
}
|
|||
|
|
|||
|
protected override void OnDeactivate()
|
|||
|
{
|
|||
|
base.OnDeactivate();
|
|||
|
m_CopyToolView.Hide();
|
|||
|
}
|
|||
|
|
|||
|
void CopyMeshFromSpriteCache(SpriteCache sprite, SkinningCopySpriteData skinningSpriteData)
|
|||
|
{
|
|||
|
if (meshTool == null)
|
|||
|
return;
|
|||
|
|
|||
|
meshTool.SetupSprite(sprite);
|
|||
|
skinningSpriteData.vertices = meshTool.mesh.vertices;
|
|||
|
skinningSpriteData.vertexWeights = meshTool.mesh.vertexWeights;
|
|||
|
skinningSpriteData.indices = meshTool.mesh.indices;
|
|||
|
skinningSpriteData.edges = meshTool.mesh.edges;
|
|||
|
skinningSpriteData.boneWeightGuids = new List<string>(meshTool.mesh.bones.Length);
|
|||
|
skinningSpriteData.boneWeightNames = new List<string>(meshTool.mesh.bones.Length);
|
|||
|
foreach (var bone in meshTool.mesh.bones)
|
|||
|
{
|
|||
|
skinningSpriteData.boneWeightGuids.Add(bone.guid);
|
|||
|
skinningSpriteData.boneWeightNames.Add(bone.name);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void OnCopyActivated()
|
|||
|
{
|
|||
|
SkinningCopyData skinningCopyData = null;
|
|||
|
var selectedSprite = skinningCache.selectedSprite;
|
|||
|
if (selectedSprite == null)
|
|||
|
{
|
|||
|
var sprites = skinningCache.GetSprites();
|
|||
|
if(!skinningCache.character || sprites.Length > 1)
|
|||
|
skinningCopyData = CopyAll();
|
|||
|
else if(sprites.Length == 1)
|
|||
|
skinningCopyData = CopySingle(sprites[0]);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
skinningCopyData = CopySingle(selectedSprite);
|
|||
|
}
|
|||
|
|
|||
|
if (skinningCopyData != null)
|
|||
|
m_CopyToolStringStore.stringStore = SkinningCopyUtility.SerializeSkinningCopyDataToString(skinningCopyData);
|
|||
|
skinningCache.events.copy.Invoke();
|
|||
|
}
|
|||
|
|
|||
|
SkinningCopyData CopyAll()
|
|||
|
{
|
|||
|
var skinningCopyData = new SkinningCopyData
|
|||
|
{
|
|||
|
pixelsPerUnit = pixelsPerUnit,
|
|||
|
isCharacterData = skinningCache.hasCharacter,
|
|||
|
characterBones = skinningCache.hasCharacter
|
|||
|
? skinningCache.character.skeleton.bones.ToSpriteBone(Matrix4x4.identity)
|
|||
|
: null
|
|||
|
};
|
|||
|
|
|||
|
var sprites = skinningCache.GetSprites();
|
|||
|
foreach (var sprite in sprites)
|
|||
|
skinningCopyData.copyData.Add(GetDataForSprite(sprite));
|
|||
|
|
|||
|
if (meshTool != null)
|
|||
|
meshTool.SetupSprite(null);
|
|||
|
|
|||
|
return skinningCopyData;
|
|||
|
}
|
|||
|
|
|||
|
SkinningCopyData CopySingle(SpriteCache sprite)
|
|||
|
{
|
|||
|
var skinningCopyData = new SkinningCopyData
|
|||
|
{
|
|||
|
pixelsPerUnit = pixelsPerUnit,
|
|||
|
isCharacterData = false,
|
|||
|
characterBones = Array.Empty<SpriteBone>()
|
|||
|
};
|
|||
|
skinningCopyData.copyData.Add(GetDataForSprite(sprite));
|
|||
|
|
|||
|
return skinningCopyData;
|
|||
|
}
|
|||
|
|
|||
|
SkinningCopySpriteData GetDataForSprite(SpriteCache sprite)
|
|||
|
{
|
|||
|
var skinningSpriteData = new SkinningCopySpriteData();
|
|||
|
skinningSpriteData.spriteName = sprite.name;
|
|||
|
|
|||
|
CopyMeshFromSpriteCache(sprite, skinningSpriteData);
|
|||
|
|
|||
|
// Bones
|
|||
|
var rootBones = new List<BoneCache>();
|
|||
|
var boneCache = Array.Empty<BoneCache>();
|
|||
|
if (skinningCache.hasCharacter)
|
|||
|
{
|
|||
|
var characterPart = skinningCache.GetCharacterPart(sprite);
|
|||
|
if (characterPart != null && characterPart.bones != null)
|
|||
|
{
|
|||
|
boneCache = characterPart.bones;
|
|||
|
var bones = characterPart.bones.FindRoots();
|
|||
|
foreach (var bone in bones)
|
|||
|
rootBones.Add(bone);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
var skeleton = skinningCache.GetEffectiveSkeleton(sprite);
|
|||
|
if (skeleton != null && skeleton.boneCount > 0)
|
|||
|
{
|
|||
|
boneCache = skeleton.bones;
|
|||
|
var bones = boneCache.FindRoots();
|
|||
|
foreach (var bone in bones)
|
|||
|
rootBones.Add(bone);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (rootBones.Count > 0)
|
|||
|
{
|
|||
|
skinningSpriteData.spriteBones = new List<SpriteBoneCopyData>();
|
|||
|
foreach (var rootBone in rootBones)
|
|||
|
{
|
|||
|
var rootBoneIndex = skinningSpriteData.spriteBones.Count;
|
|||
|
|
|||
|
GetSpriteBoneDataRecursively(skinningSpriteData.spriteBones, rootBone, new List<BoneCache>(boneCache));
|
|||
|
if (skinningCache.hasCharacter)
|
|||
|
{
|
|||
|
// Offset the bones based on the currently selected Sprite in Character mode
|
|||
|
var characterPart = sprite.GetCharacterPart();
|
|||
|
if (characterPart != null)
|
|||
|
{
|
|||
|
var offset = characterPart.position;
|
|||
|
var rootSpriteBone = skinningSpriteData.spriteBones[rootBoneIndex];
|
|||
|
rootSpriteBone.spriteBone.position -= offset;
|
|||
|
skinningSpriteData.spriteBones[rootBoneIndex] = rootSpriteBone;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return skinningSpriteData;
|
|||
|
}
|
|||
|
|
|||
|
static void GetSpriteBoneDataRecursively(IList<SpriteBoneCopyData> bones, BoneCache rootBone, List<BoneCache> boneCache)
|
|||
|
{
|
|||
|
AppendSpriteBoneDataRecursively(bones, rootBone, -1, boneCache);
|
|||
|
}
|
|||
|
|
|||
|
static void AppendSpriteBoneDataRecursively(IList<SpriteBoneCopyData> bones, BoneCache currentBone, int parentIndex, List<BoneCache> boneCache)
|
|||
|
{
|
|||
|
var currentParentIndex = bones.Count;
|
|||
|
|
|||
|
var boneCopyData = new SpriteBoneCopyData()
|
|||
|
{
|
|||
|
spriteBone = new SpriteBone()
|
|||
|
{
|
|||
|
name = currentBone.name,
|
|||
|
guid = currentBone.guid,
|
|||
|
color = currentBone.bindPoseColor,
|
|||
|
parentId = parentIndex
|
|||
|
},
|
|||
|
order = boneCache.FindIndex(x => x == currentBone)
|
|||
|
};
|
|||
|
if (boneCopyData.order < 0)
|
|||
|
{
|
|||
|
boneCopyData.order = boneCache.Count;
|
|||
|
boneCache.Add(currentBone);
|
|||
|
}
|
|||
|
|
|||
|
if (parentIndex == -1 && currentBone.parentBone != null)
|
|||
|
{
|
|||
|
boneCopyData.spriteBone.position = currentBone.position;
|
|||
|
boneCopyData.spriteBone.rotation = currentBone.rotation;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
boneCopyData.spriteBone.position = currentBone.localPosition;
|
|||
|
boneCopyData.spriteBone.rotation = currentBone.localRotation;
|
|||
|
}
|
|||
|
boneCopyData.spriteBone.position = new Vector3(boneCopyData.spriteBone.position.x, boneCopyData.spriteBone.position.y, currentBone.depth);
|
|||
|
|
|||
|
boneCopyData.spriteBone.length = currentBone.localLength;
|
|||
|
bones.Add(boneCopyData);
|
|||
|
foreach (var child in currentBone)
|
|||
|
{
|
|||
|
var childBone = child as BoneCache;
|
|||
|
if (childBone != null)
|
|||
|
AppendSpriteBoneDataRecursively(bones, childBone, currentParentIndex, boneCache);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void OnPasteActivated(bool shouldPasteBones, bool shouldPasteMesh, bool shouldFlipX, bool shouldFlipY)
|
|||
|
{
|
|||
|
var copyBuffer = m_CopyToolStringStore.stringStore;
|
|||
|
if (!IsValidCopyData(copyBuffer))
|
|||
|
{
|
|||
|
Debug.LogError(TextContent.copyError1);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var skinningCopyData = SkinningCopyUtility.DeserializeStringToSkinningCopyData(copyBuffer);
|
|||
|
if (skinningCopyData == null || skinningCopyData.copyData.Count == 0)
|
|||
|
{
|
|||
|
Debug.LogError(TextContent.copyError2);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var doesCopyContainMultipleSprites = skinningCopyData.copyData.Count > 1;
|
|||
|
var sprites = skinningCache.GetSprites();
|
|||
|
|
|||
|
if (doesCopyContainMultipleSprites && skinningCopyData.copyData.Count != sprites.Length && shouldPasteMesh)
|
|||
|
{
|
|||
|
Debug.LogError(string.Format(TextContent.copyError3, sprites.Length, skinningCopyData.copyData.Count));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var selectedSprite = skinningCache.selectedSprite;
|
|||
|
using (skinningCache.UndoScope(TextContent.pasteData))
|
|||
|
{
|
|||
|
var scale = skinningCopyData.pixelsPerUnit > 0f ? pixelsPerUnit / skinningCopyData.pixelsPerUnit : 1f;
|
|||
|
var pastedBonesToSelect = new HashSet<BoneCache>();
|
|||
|
|
|||
|
var characterBones = Array.Empty<BoneCache>();
|
|||
|
var replaceCharacterSkeleton = shouldPasteBones && skinningCache.hasCharacter && skinningCopyData.isCharacterData;
|
|||
|
if (replaceCharacterSkeleton)
|
|||
|
{
|
|||
|
var spriteBones = skinningCopyData.characterBones;
|
|||
|
characterBones = PasteBonesInCharacter(skinningCache, spriteBones, shouldFlipX, shouldFlipY, scale);
|
|||
|
foreach (var newBone in characterBones)
|
|||
|
pastedBonesToSelect.Add(newBone);
|
|||
|
}
|
|||
|
|
|||
|
foreach (var copySpriteData in skinningCopyData.copyData)
|
|||
|
{
|
|||
|
SpriteCache sprite = null;
|
|||
|
if (selectedSprite != null && !doesCopyContainMultipleSprites)
|
|||
|
sprite = selectedSprite;
|
|||
|
if (sprite == null && !string.IsNullOrEmpty(copySpriteData.spriteName))
|
|||
|
sprite = FindSpriteWithName(sprites, copySpriteData.spriteName);
|
|||
|
|
|||
|
if (sprite == null)
|
|||
|
continue;
|
|||
|
|
|||
|
var boneMapping = new Dictionary<string, string>();
|
|||
|
if (shouldPasteBones && !replaceCharacterSkeleton)
|
|||
|
{
|
|||
|
var bonesToPaste = GetBonesInCorrectOrder(copySpriteData.spriteBones);
|
|||
|
var boneStorage = PasteBonesInSprite(skinningCache, sprite, bonesToPaste, characterBones, shouldFlipX, shouldFlipY, scale);
|
|||
|
if (boneStorage != null)
|
|||
|
{
|
|||
|
boneMapping = boneStorage.boneMapping;
|
|||
|
if (skinningCache.hasCharacter || sprite == selectedSprite)
|
|||
|
{
|
|||
|
foreach (var newBone in boneStorage.bones)
|
|||
|
pastedBonesToSelect.Add(newBone);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (shouldPasteMesh)
|
|||
|
PasteMeshInSprite(meshTool, sprite, copySpriteData, shouldFlipX, shouldFlipY, scale, boneMapping);
|
|||
|
}
|
|||
|
|
|||
|
var refreshSelection = skinningCache.hasCharacter || skinningCache.selectedSprite != null;
|
|||
|
if (refreshSelection)
|
|||
|
{
|
|||
|
var newBoneSelection = new BoneCache[pastedBonesToSelect.Count];
|
|||
|
pastedBonesToSelect.CopyTo(newBoneSelection);
|
|||
|
|
|||
|
meshTool.SetupSprite(selectedSprite); // This is to refresh the selected Sprite in meshTool.
|
|||
|
skinningCache.skeletonSelection.elements = newBoneSelection;
|
|||
|
skinningCache.events.boneSelectionChanged.Invoke();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
skinningCache.events.paste.Invoke(shouldPasteBones, shouldPasteMesh, shouldFlipX, shouldFlipY);
|
|||
|
}
|
|||
|
|
|||
|
static bool IsValidCopyData(string copyBuffer)
|
|||
|
{
|
|||
|
return SkinningCopyUtility.CanDeserializeStringToSkinningCopyData(copyBuffer);
|
|||
|
}
|
|||
|
|
|||
|
static Vector3 GetFlippedBonePosition(BoneCache bone, Vector2 startPosition, Rect spriteRect, bool flipX, bool flipY)
|
|||
|
{
|
|||
|
Vector3 position = startPosition;
|
|||
|
if (flipX)
|
|||
|
position.x += spriteRect.width - bone.position.x;
|
|||
|
else
|
|||
|
position.x += bone.position.x;
|
|||
|
|
|||
|
if (flipY)
|
|||
|
position.y += spriteRect.height - bone.position.y;
|
|||
|
else
|
|||
|
position.y += bone.position.y;
|
|||
|
|
|||
|
position.z = bone.position.z;
|
|||
|
return position;
|
|||
|
}
|
|||
|
|
|||
|
static Quaternion GetFlippedBoneRotation(BoneCache bone, bool flipX, bool flipY)
|
|||
|
{
|
|||
|
var euler = bone.rotation.eulerAngles;
|
|||
|
if (flipX)
|
|||
|
{
|
|||
|
if (euler.z <= 180)
|
|||
|
euler.z = 180 - euler.z;
|
|||
|
else
|
|||
|
euler.z = 540 - euler.z;
|
|||
|
}
|
|||
|
if (flipY)
|
|||
|
euler.z = 360 - euler.z;
|
|||
|
return Quaternion.Euler(euler);
|
|||
|
}
|
|||
|
|
|||
|
static void SetBonePositionAndRotation(BoneCache[] boneCache, TransformCache bone, Vector3[] position, Quaternion[] rotation)
|
|||
|
{
|
|||
|
var index = Array.FindIndex(boneCache, x => x == bone);
|
|||
|
if (index >= 0)
|
|||
|
{
|
|||
|
bone.position = position[index];
|
|||
|
bone.rotation = rotation[index];
|
|||
|
}
|
|||
|
foreach (var child in bone.children)
|
|||
|
{
|
|||
|
SetBonePositionAndRotation(boneCache, child, position, rotation);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static BoneCache[] PasteBonesInCharacter(SkinningCache skinningCache, SpriteBone[] spriteBones, bool shouldFlipX, bool shouldFlipY, float scale)
|
|||
|
{
|
|||
|
if (!skinningCache.hasCharacter)
|
|||
|
return null;
|
|||
|
|
|||
|
var boneCache = skinningCache.CreateBoneCacheFromSpriteBones(spriteBones, scale);
|
|||
|
if (shouldFlipX || shouldFlipY)
|
|||
|
{
|
|||
|
var characterRect = new Rect(Vector2.zero, skinningCache.character.dimension);
|
|||
|
var newPositions = new Vector3[boneCache.Length];
|
|||
|
var newRotations = new Quaternion[boneCache.Length];
|
|||
|
for (var i = 0; i < boneCache.Length; ++i)
|
|||
|
{
|
|||
|
newPositions[i] = GetFlippedBonePosition(boneCache[i], Vector2.zero, characterRect, shouldFlipX, shouldFlipY);
|
|||
|
newRotations[i] = GetFlippedBoneRotation(boneCache[i], shouldFlipX, shouldFlipY);
|
|||
|
}
|
|||
|
for (var i = 0; i < boneCache.Length; ++i)
|
|||
|
{
|
|||
|
boneCache[i].position = newPositions[i];
|
|||
|
boneCache[i].rotation = newRotations[i];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var skeleton = skinningCache.character.skeleton;
|
|||
|
skeleton.SetBones(boneCache);
|
|||
|
skinningCache.events.skeletonTopologyChanged.Invoke(skeleton);
|
|||
|
|
|||
|
return boneCache;
|
|||
|
}
|
|||
|
|
|||
|
static BoneStorage PasteBonesInSprite(SkinningCache skinningCache, SpriteCache sprite, SpriteBone[] newBones, BoneCache[] characterBones, bool shouldFlipX, bool shouldFlipY, float scale)
|
|||
|
{
|
|||
|
if (sprite == null || skinningCache.mode == SkinningMode.SpriteSheet && skinningCache.hasCharacter)
|
|||
|
return null;
|
|||
|
|
|||
|
var spriteRect = sprite.textureRect;
|
|||
|
var skeleton = skinningCache.GetEffectiveSkeleton(sprite);
|
|||
|
|
|||
|
var newBonesCache = skinningCache.CreateBoneCacheFromSpriteBones(newBones, scale);
|
|||
|
if (newBonesCache.Length == 0)
|
|||
|
return null;
|
|||
|
|
|||
|
Vector2 rectPosition;
|
|||
|
if (skinningCache.mode == SkinningMode.Character)
|
|||
|
{
|
|||
|
var characterPart = sprite.GetCharacterPart();
|
|||
|
if (characterPart == null)
|
|||
|
return null;
|
|||
|
rectPosition = characterPart.position;
|
|||
|
}
|
|||
|
else
|
|||
|
rectPosition = spriteRect.position;
|
|||
|
|
|||
|
var newPositions = new Vector3[newBonesCache.Length];
|
|||
|
var newRotations = new Quaternion[newBonesCache.Length];
|
|||
|
for (var i = 0; i < newBonesCache.Length; ++i)
|
|||
|
{
|
|||
|
newPositions[i] = GetFlippedBonePosition(newBonesCache[i], rectPosition, spriteRect, shouldFlipX, shouldFlipY);
|
|||
|
newRotations[i] = GetFlippedBoneRotation(newBonesCache[i], shouldFlipX, shouldFlipY);
|
|||
|
}
|
|||
|
|
|||
|
foreach (var bone in newBonesCache)
|
|||
|
{
|
|||
|
if (bone.parent == null)
|
|||
|
{
|
|||
|
SetBonePositionAndRotation(newBonesCache, bone, newPositions, newRotations);
|
|||
|
if (skinningCache.mode == SkinningMode.Character)
|
|||
|
bone.SetParent(skeleton);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var boneNameMapping = new Dictionary<string, string>();
|
|||
|
if (skinningCache.mode == SkinningMode.SpriteSheet)
|
|||
|
{
|
|||
|
skeleton.SetBones(newBonesCache);
|
|||
|
skeleton.SetDefaultPose();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
boneNameMapping = AddBonesToSkeletonWithUniqueNames(characterBones, newBonesCache, skeleton);
|
|||
|
skeleton.SetDefaultPose();
|
|||
|
}
|
|||
|
|
|||
|
skinningCache.events.skeletonTopologyChanged.Invoke(skeleton);
|
|||
|
return new BoneStorage(newBonesCache, boneNameMapping);
|
|||
|
}
|
|||
|
|
|||
|
static Dictionary<string, string> AddBonesToSkeletonWithUniqueNames(IList<BoneCache> characterBones, IList<BoneCache> newBones, SkeletonCache skeleton)
|
|||
|
{
|
|||
|
var nameMapping = new Dictionary<string, string>();
|
|||
|
|
|||
|
var existingBoneGuids = new HashSet<string>();
|
|||
|
var existingBoneNames = new HashSet<string>(skeleton.boneCount);
|
|||
|
for (var i = 0; i < characterBones.Count; i++)
|
|||
|
{
|
|||
|
if(!string.IsNullOrEmpty(characterBones[i].guid))
|
|||
|
existingBoneGuids.Add(characterBones[i].guid);
|
|||
|
}
|
|||
|
|
|||
|
for (var i = 0; i < skeleton.boneCount; i++)
|
|||
|
existingBoneNames.Add(skeleton.bones[i].name);
|
|||
|
|
|||
|
foreach (var newBone in newBones)
|
|||
|
{
|
|||
|
var guid = newBone.guid;
|
|||
|
if (string.IsNullOrEmpty(guid) || existingBoneGuids.Contains(guid))
|
|||
|
continue;
|
|||
|
|
|||
|
var boneName = newBone.name;
|
|||
|
if (existingBoneNames.Contains(boneName))
|
|||
|
newBone.name = SkeletonController.AutoNameBoneCopy(boneName, skeleton.bones);
|
|||
|
|
|||
|
existingBoneGuids.Add(newBone.guid);
|
|||
|
existingBoneNames.Add(newBone.name);
|
|||
|
nameMapping[boneName] = newBone.name;
|
|||
|
skeleton.AddBone(newBone);
|
|||
|
}
|
|||
|
|
|||
|
return nameMapping;
|
|||
|
}
|
|||
|
|
|||
|
static void PasteMeshInSprite(MeshTool meshTool, SpriteCache sprite, SkinningCopySpriteData copySpriteData, bool shouldFlipX, bool shouldFlipY, float scale, Dictionary<string, string> boneMapping)
|
|||
|
{
|
|||
|
if (meshTool == null || sprite == null)
|
|||
|
return;
|
|||
|
|
|||
|
var vertices = copySpriteData.vertices ?? Array.Empty<Vector2>();
|
|||
|
var vertexWeights = copySpriteData.vertexWeights ?? Array.Empty<EditableBoneWeight>();
|
|||
|
|
|||
|
meshTool.SetupSprite(sprite);
|
|||
|
meshTool.mesh.SetVertices(vertices, vertexWeights);
|
|||
|
if (!Mathf.Approximately(scale, 1f) || shouldFlipX || shouldFlipY)
|
|||
|
{
|
|||
|
var spriteRect = sprite.textureRect;
|
|||
|
for (var i = 0; i < meshTool.mesh.vertexCount; ++i)
|
|||
|
{
|
|||
|
var position = meshTool.mesh.vertices[i];
|
|||
|
if (!Mathf.Approximately(scale, 1f))
|
|||
|
position *= scale;
|
|||
|
if (shouldFlipX)
|
|||
|
position.x = spriteRect.width - meshTool.mesh.vertices[i].x;
|
|||
|
if (shouldFlipY)
|
|||
|
position.y = spriteRect.height - meshTool.mesh.vertices[i].y;
|
|||
|
meshTool.mesh.vertices[i] = position;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
meshTool.mesh.SetIndices(copySpriteData.indices);
|
|||
|
meshTool.mesh.SetEdges(copySpriteData.edges);
|
|||
|
|
|||
|
var skinningCache = meshTool.skinningCache;
|
|||
|
var skeleton = skinningCache.GetEffectiveSkeleton(sprite);
|
|||
|
var hasGuids = copySpriteData.boneWeightGuids.Count > 0;
|
|||
|
for (var i = 0; i < copySpriteData.boneWeightGuids.Count; i++)
|
|||
|
{
|
|||
|
if (string.IsNullOrEmpty(copySpriteData.boneWeightGuids[i]))
|
|||
|
{
|
|||
|
hasGuids = false;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var skeletonBones = skeleton.bones;
|
|||
|
var influenceBones = hasGuids ? GetBonesFromGuids(copySpriteData, skeletonBones, boneMapping) : GetBonesFromNames(copySpriteData, skeletonBones, boneMapping);
|
|||
|
|
|||
|
// Update associated bones for mesh
|
|||
|
meshTool.mesh.SetCompatibleBoneSet(influenceBones);
|
|||
|
meshTool.mesh.bones = influenceBones; // Fixes weights for bones that do not exist
|
|||
|
|
|||
|
// Update associated bones for character
|
|||
|
if (skinningCache.hasCharacter)
|
|||
|
{
|
|||
|
var characterPart = sprite.GetCharacterPart();
|
|||
|
if (characterPart != null)
|
|||
|
{
|
|||
|
characterPart.bones = influenceBones;
|
|||
|
skinningCache.events.characterPartChanged.Invoke(characterPart);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
meshTool.UpdateMesh();
|
|||
|
}
|
|||
|
|
|||
|
static BoneCache[] GetBonesFromGuids(SkinningCopySpriteData copySpriteData, IList<BoneCache> skeletonBones, Dictionary<string, string> boneMapping)
|
|||
|
{
|
|||
|
var spriteBones = new List<BoneCache>();
|
|||
|
for (var i = 0; i < copySpriteData.boneWeightGuids.Count; i++)
|
|||
|
{
|
|||
|
var bone = FindBoneWithGuid(skeletonBones, copySpriteData.boneWeightGuids[i]);
|
|||
|
if (bone == null)
|
|||
|
continue;
|
|||
|
|
|||
|
if (boneMapping != null && boneMapping.ContainsKey(bone.name))
|
|||
|
{
|
|||
|
bone = FindBoneWithName(skeletonBones, boneMapping[bone.name]);
|
|||
|
if (bone == null)
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
spriteBones.Add(bone);
|
|||
|
}
|
|||
|
|
|||
|
return spriteBones.ToArray();
|
|||
|
}
|
|||
|
|
|||
|
static BoneCache[] GetBonesFromNames(SkinningCopySpriteData copySpriteData, IList<BoneCache> skeletonBones, Dictionary<string, string> boneMapping)
|
|||
|
{
|
|||
|
var spriteBones = new List<BoneCache>();
|
|||
|
for (var i = 0; i < copySpriteData.boneWeightNames.Count; ++i)
|
|||
|
{
|
|||
|
var boneName = copySpriteData.boneWeightNames[i];
|
|||
|
if (boneMapping != null && boneMapping.ContainsKey(boneName))
|
|||
|
boneName = boneMapping[boneName];
|
|||
|
|
|||
|
var bone = FindBoneWithName(skeletonBones, boneName);
|
|||
|
if (bone == null)
|
|||
|
continue;
|
|||
|
|
|||
|
spriteBones.Add(bone);
|
|||
|
}
|
|||
|
|
|||
|
return spriteBones.ToArray();
|
|||
|
}
|
|||
|
|
|||
|
static SpriteBone[] GetBonesInCorrectOrder(IList<SpriteBoneCopyData> spriteBones)
|
|||
|
{
|
|||
|
var orderedBones = new SpriteBone[spriteBones.Count];
|
|||
|
for (var i = 0; i < spriteBones.Count; ++i)
|
|||
|
{
|
|||
|
var order = spriteBones[i].order;
|
|||
|
if (order >= 0)
|
|||
|
{
|
|||
|
orderedBones[order] = spriteBones[i].spriteBone;
|
|||
|
var parentId = orderedBones[order].parentId;
|
|||
|
if (parentId >= 0)
|
|||
|
orderedBones[order].parentId = spriteBones[parentId].order;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
orderedBones[i] = spriteBones[i].spriteBone;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return orderedBones;
|
|||
|
}
|
|||
|
|
|||
|
static SpriteCache FindSpriteWithName(IList<SpriteCache> sprites, string spriteName)
|
|||
|
{
|
|||
|
for (var i = 0; i < sprites.Count; i++)
|
|||
|
{
|
|||
|
var sprite = sprites[i];
|
|||
|
if (sprite.name == spriteName)
|
|||
|
return sprite;
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
static BoneCache FindBoneWithName(IList<BoneCache> bones, string boneName)
|
|||
|
{
|
|||
|
for (var i = 0; i < bones.Count; i++)
|
|||
|
{
|
|||
|
var bone = bones[i];
|
|||
|
if (bone.name == boneName)
|
|||
|
return bone;
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
static BoneCache FindBoneWithGuid(IList<BoneCache> bones, string guid)
|
|||
|
{
|
|||
|
for (var i = 0; i < bones.Count; i++)
|
|||
|
{
|
|||
|
var bone = bones[i];
|
|||
|
if (bone.guid == guid)
|
|||
|
return bone;
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
internal class CopyToolView
|
|||
|
{
|
|||
|
PastePanel m_PastePanel;
|
|||
|
|
|||
|
public event Action<bool, bool, bool, bool> onPasteActivated = (bone, mesh, flipX, flipY) => {};
|
|||
|
|
|||
|
public void Show(bool readonlyBone)
|
|||
|
{
|
|||
|
m_PastePanel.SetHiddenFromLayout(false);
|
|||
|
m_PastePanel.BonePasteEnable(!readonlyBone);
|
|||
|
}
|
|||
|
|
|||
|
public void Hide()
|
|||
|
{
|
|||
|
m_PastePanel.SetHiddenFromLayout(true);
|
|||
|
}
|
|||
|
|
|||
|
public void Initialize(LayoutOverlay layoutOverlay)
|
|||
|
{
|
|||
|
m_PastePanel = PastePanel.GenerateFromUXML();
|
|||
|
BindElements();
|
|||
|
layoutOverlay.rightOverlay.Add(m_PastePanel);
|
|||
|
m_PastePanel.SetHiddenFromLayout(true);
|
|||
|
}
|
|||
|
|
|||
|
void BindElements()
|
|||
|
{
|
|||
|
m_PastePanel.onPasteActivated += OnPasteActivated;
|
|||
|
}
|
|||
|
|
|||
|
void OnPasteActivated(bool bone, bool mesh, bool flipX, bool flipY)
|
|||
|
{
|
|||
|
onPasteActivated(bone, mesh, flipX, flipY);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|