Initial bread2unity commit
This commit is contained in:
parent
b68e9cd967
commit
5021c83afa
205
Assets/Editor/bread2unity/AnimationCreator.cs
Normal file
205
Assets/Editor/bread2unity/AnimationCreator.cs
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Bread2Unity;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Animations;
|
||||||
|
using UnityEngine;
|
||||||
|
using Animation = Bread2Unity.Animation;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public static class AnimationCreator
|
||||||
|
{
|
||||||
|
private class BccadCurve : AnimationCurve
|
||||||
|
{
|
||||||
|
private float _prev;
|
||||||
|
|
||||||
|
public new void AddKey(float time, float value)
|
||||||
|
{
|
||||||
|
if (keys.Length != 0 && !(Math.Abs(value - _prev) > 0.000001)) return;
|
||||||
|
AddKey(new Keyframe(time, value, float.PositiveInfinity, float.PositiveInfinity));
|
||||||
|
_prev = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyLastKey(float time)
|
||||||
|
{
|
||||||
|
Keyframe lastKey = keys.LastOrDefault();
|
||||||
|
|
||||||
|
base.AddKey(time, keys.Length > 0 ? lastKey.value : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateAnimation(BccadPrefab bccadPrefab, BCCAD bccad, PrefabData prefabData,
|
||||||
|
List<Sprite> sprites)
|
||||||
|
{
|
||||||
|
var gameObject = bccadPrefab.ParentObject;
|
||||||
|
var rootPrefabName = gameObject.transform.parent.gameObject.name;
|
||||||
|
var spritesFolderPath =
|
||||||
|
$"Assets\\Resources\\Sprites\\Games\\{char.ToUpperInvariant(rootPrefabName[0]) + rootPrefabName.Substring(1)}";
|
||||||
|
|
||||||
|
var animationsFolderPath = spritesFolderPath + $"/anim/{prefabData.Name}";
|
||||||
|
if (!Directory.Exists(animationsFolderPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(animationsFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var controller =
|
||||||
|
AnimatorController.CreateAnimatorControllerAtPath(
|
||||||
|
AssetDatabase.GenerateUniqueAssetPath(
|
||||||
|
$"{animationsFolderPath}/{prefabData.Name}Controller.controller"));
|
||||||
|
var bccadSprites = bccad.sprites;
|
||||||
|
|
||||||
|
//get all of the parts associated with the game object
|
||||||
|
var steps = prefabData.Animations.SelectMany(animation => animation.Steps);
|
||||||
|
var bccadSpritesOfPrefab = steps.Select(step => step.BccadSprite).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var animation in prefabData.Animations)
|
||||||
|
{
|
||||||
|
var clip = CreateAnimationClip(bccadPrefab, animation, sprites, bccadSpritesOfPrefab);
|
||||||
|
|
||||||
|
AssetDatabase.CreateAsset(clip,
|
||||||
|
AssetDatabase.GenerateUniqueAssetPath(
|
||||||
|
$"{animationsFolderPath}/{animation.Name}.anim"));
|
||||||
|
controller.AddMotion(clip);
|
||||||
|
}
|
||||||
|
|
||||||
|
var animator = gameObject.AddComponent<Animator>();
|
||||||
|
animator.runtimeAnimatorController = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AnimationClip CreateAnimationClip(BccadPrefab bccadPrefab, Animation animation,
|
||||||
|
List<Sprite> sprites,
|
||||||
|
IReadOnlyCollection<BccadSprite> spritesAssociatedWithPrefab)
|
||||||
|
{
|
||||||
|
var animationClip = new AnimationClip();
|
||||||
|
var prefab = bccadPrefab.ParentObject;
|
||||||
|
for (int childIndex = 0; childIndex < prefab.transform.childCount; childIndex++)
|
||||||
|
{
|
||||||
|
var child = prefab.transform.GetChild(childIndex).gameObject;
|
||||||
|
|
||||||
|
var partsOfGameObject = spritesAssociatedWithPrefab.SelectMany(sprite => sprite.parts)
|
||||||
|
.Where(part => bccadPrefab.RegionToChild[part.RegionIndex] == child).ToList();
|
||||||
|
|
||||||
|
var enabledCurve = new BccadCurve();
|
||||||
|
|
||||||
|
var xTransformCurve = new BccadCurve();
|
||||||
|
var yTransformCurve = new BccadCurve();
|
||||||
|
var zTransformCurve = new BccadCurve();
|
||||||
|
|
||||||
|
var rotationCurve = new BccadCurve();
|
||||||
|
|
||||||
|
var flipXCurve = new BccadCurve();
|
||||||
|
var flipYCurve = new BccadCurve();
|
||||||
|
|
||||||
|
var scaleXCurve = new BccadCurve();
|
||||||
|
var scaleYCurve = new BccadCurve();
|
||||||
|
|
||||||
|
var spriteFrames = new List<ObjectReferenceKeyframe>();
|
||||||
|
|
||||||
|
var currentTime = 0f;
|
||||||
|
|
||||||
|
for (int stepIndex = 0; stepIndex < animation.Steps.Count; stepIndex++)
|
||||||
|
{
|
||||||
|
var currentStep = animation.Steps[stepIndex];
|
||||||
|
var bccadSprite = currentStep.BccadSprite;
|
||||||
|
// Find the index of part of the game object
|
||||||
|
var partIndex = bccadSprite.parts.Select((value, index) => new { value, index })
|
||||||
|
.Where(pair => bccadPrefab.RegionToChild[pair.value.RegionIndex] == child)
|
||||||
|
.Select(pair => pair.index).DefaultIfEmpty(-1)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
enabledCurve.AddKey(currentTime, partIndex == -1 ? 0 : 1);
|
||||||
|
|
||||||
|
if (partIndex != -1)
|
||||||
|
{
|
||||||
|
var bccadSpritePart = bccadSprite.parts[partIndex];
|
||||||
|
|
||||||
|
var sprite = sprites[bccadSpritePart.RegionIndex.Index];
|
||||||
|
var width = bccadSpritePart.StretchX / bccadPrefab.WidthRatio;
|
||||||
|
var height = bccadSpritePart.StretchY / bccadPrefab.HeightRatio;
|
||||||
|
var x = (bccadSpritePart.PosX - 512f) /
|
||||||
|
SpriteCreator.PixelsPerUnit + sprite.bounds.size.x * 0.5f * width;
|
||||||
|
var y = -(bccadSpritePart.PosY - 512f) / SpriteCreator.PixelsPerUnit -
|
||||||
|
sprite.bounds.size.y * 0.5f * height;
|
||||||
|
var z = -0.00001f * partIndex;
|
||||||
|
|
||||||
|
xTransformCurve.AddKey(currentTime, x);
|
||||||
|
yTransformCurve.AddKey(currentTime, y);
|
||||||
|
zTransformCurve.AddKey(currentTime, z);
|
||||||
|
|
||||||
|
scaleXCurve.AddKey(currentTime, width);
|
||||||
|
scaleYCurve.AddKey(currentTime, height);
|
||||||
|
|
||||||
|
if (spriteFrames.Count == 0 || spriteFrames.Last().value != sprite)
|
||||||
|
{
|
||||||
|
var spriteKeyframe = new ObjectReferenceKeyframe
|
||||||
|
{
|
||||||
|
time = currentTime,
|
||||||
|
value = sprite
|
||||||
|
};
|
||||||
|
spriteFrames.Add(spriteKeyframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
flipXCurve.AddKey(currentTime, bccadSpritePart.FlipX ? 1 : 0);
|
||||||
|
flipYCurve.AddKey(currentTime, bccadSpritePart.FlipY ? 1 : 0);
|
||||||
|
|
||||||
|
rotationCurve.AddKey(currentTime, -bccadSpritePart.Rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase the time for the next frame
|
||||||
|
currentTime += currentStep.Delay / 30f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childIndex == 0)
|
||||||
|
{
|
||||||
|
enabledCurve.CopyLastKey(currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
var spriteBinding = new EditorCurveBinding
|
||||||
|
{
|
||||||
|
type = typeof(SpriteRenderer),
|
||||||
|
propertyName = "m_Sprite",
|
||||||
|
path = $"{prefab.name} {childIndex}"
|
||||||
|
};
|
||||||
|
|
||||||
|
var animateActive = childIndex == 0 || spritesAssociatedWithPrefab.Any(sprite =>
|
||||||
|
sprite.parts.All(part => bccadPrefab.RegionToChild[part.RegionIndex] != child));
|
||||||
|
if (animateActive)
|
||||||
|
animationClip.SetCurve(child.name, typeof(GameObject), "m_IsActive", enabledCurve);
|
||||||
|
if ((from part in partsOfGameObject select part.FlipX).Distinct().Count() > 1)
|
||||||
|
animationClip.SetCurve(child.name, typeof(SpriteRenderer), "m_FlipX", flipXCurve);
|
||||||
|
if ((from part in partsOfGameObject select part.FlipY).Distinct().Count() > 1)
|
||||||
|
animationClip.SetCurve(child.name, typeof(SpriteRenderer), "m_FlipY", flipYCurve);
|
||||||
|
if ((from part in partsOfGameObject select part.RegionIndex.Index).Distinct().Count() > 1)
|
||||||
|
AnimationUtility.SetObjectReferenceCurve(animationClip, spriteBinding, spriteFrames.ToArray());
|
||||||
|
if ((from part in partsOfGameObject select part.PosX).Distinct().Count() > 1 ||
|
||||||
|
(from part in partsOfGameObject select part.PosY).Distinct().Count() > 1)
|
||||||
|
{
|
||||||
|
animationClip.SetCurve(child.name, typeof(Transform), "localPosition.x", xTransformCurve);
|
||||||
|
animationClip.SetCurve(child.name, typeof(Transform), "localPosition.y", yTransformCurve);
|
||||||
|
animationClip.SetCurve(child.name, typeof(Transform), "localPosition.z", zTransformCurve);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((from part in partsOfGameObject select part.Rotation).Distinct().Count() > 1)
|
||||||
|
{
|
||||||
|
animationClip.SetCurve(child.name, typeof(Transform), "localEulerAngles.z", rotationCurve);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((from part in partsOfGameObject select part.StretchX).Distinct().Count() > 1 ||
|
||||||
|
(from part in partsOfGameObject select part.StretchY).Distinct().Count() > 1)
|
||||||
|
{
|
||||||
|
animationClip.SetCurve(child.name, typeof(Transform), "localScale.x", scaleXCurve);
|
||||||
|
animationClip.SetCurve(child.name, typeof(Transform), "localScale.y", scaleYCurve);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animationClip.frameRate = 30; //fps
|
||||||
|
animationClip.name = animation.Name;
|
||||||
|
|
||||||
|
return animationClip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d4aae79bea7b7234f9ce059ade5fce08
|
guid: c8ef7864bd34c1f46b262e15cfff32c9
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
381
Assets/Editor/bread2unity/AnimationFixer.cs
Normal file
381
Assets/Editor/bread2unity/AnimationFixer.cs
Normal file
|
@ -0,0 +1,381 @@
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using System.IO;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public static class AnimationFixer
|
||||||
|
{
|
||||||
|
private static int CurveCounter = 0;
|
||||||
|
static System.DateTime StartTime;
|
||||||
|
private const float TangentDegreeDifferent = 10;
|
||||||
|
|
||||||
|
[MenuItem("AnimationTool/ProcessForConstant %e")]
|
||||||
|
static void Execute()
|
||||||
|
{
|
||||||
|
CurveCounter = 0;
|
||||||
|
StartTime = System.DateTime.Now;
|
||||||
|
|
||||||
|
UnityEngine.Object[] selectedObjects = Selection.GetFiltered<UnityEngine.Object>(SelectionMode.DeepAssets);
|
||||||
|
|
||||||
|
foreach (UnityEngine.Object selectedObj in selectedObjects)
|
||||||
|
{
|
||||||
|
string path = AssetDatabase.GetAssetPath(selectedObj);
|
||||||
|
if (selectedObj is AnimationClip)
|
||||||
|
{
|
||||||
|
Debug.Log("is AnimationClip");
|
||||||
|
ProcessCurveForConstant(path);
|
||||||
|
CurveCounter++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (path.ToLower().EndsWith(".fbx", System.StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
Object[] fbxObjects = AssetDatabase.LoadAllAssetsAtPath(path);
|
||||||
|
foreach (var subObj in fbxObjects)
|
||||||
|
{
|
||||||
|
if (subObj is AnimationClip
|
||||||
|
&& !subObj.name.StartsWith("__preview__", System.StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
Debug.Log("Copy animation clip : " + path + " clip name: " +
|
||||||
|
(subObj as AnimationClip).name);
|
||||||
|
string newClipPath = CopyAnimation(subObj as AnimationClip);
|
||||||
|
ProcessCurveForConstant(newClipPath);
|
||||||
|
CurveCounter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
Debug.Log("Cruve sum: " + CurveCounter + " Time: " +
|
||||||
|
((System.DateTime.Now - StartTime).TotalMilliseconds / 1000) + "s.");
|
||||||
|
}
|
||||||
|
|
||||||
|
static string CopyAnimation(AnimationClip sourceClip)
|
||||||
|
{
|
||||||
|
string path = AssetDatabase.GetAssetPath(sourceClip);
|
||||||
|
path = Path.Combine(Path.GetDirectoryName(path), sourceClip.name) + ".anim";
|
||||||
|
string newPath = AssetDatabase.GenerateUniqueAssetPath(path);
|
||||||
|
|
||||||
|
AnimationClip newClip = new AnimationClip();
|
||||||
|
EditorUtility.CopySerialized(sourceClip, newClip);
|
||||||
|
AssetDatabase.CreateAsset(newClip, newPath);
|
||||||
|
//AssetDatabase.Refresh();
|
||||||
|
Debug.Log("CopyAnimation: " + newPath);
|
||||||
|
return newPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ProcessCurveForConstant(string clipPath)
|
||||||
|
{
|
||||||
|
Debug.Log("Processing : " + clipPath);
|
||||||
|
ProcessCurveForConstant(AssetDatabase.LoadAssetAtPath<AnimationClip>(clipPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ProcessCurveForConstant(AnimationClip animationClip)
|
||||||
|
{
|
||||||
|
AnimationUtility.GetCurveBindings(animationClip);
|
||||||
|
EditorUtility.SetDirty(animationClip);
|
||||||
|
|
||||||
|
var soClip = new SerializedObject(animationClip);
|
||||||
|
float sampleRate = soClip.FindProperty("m_SampleRate").floatValue;
|
||||||
|
float oneKeyframeTime = (float)((int)((1.0f / sampleRate) * 1000)) / 1000 + 0.001f;
|
||||||
|
|
||||||
|
string[] editorCurveSetNames = new string[] { "m_EditorCurves", "m_EulerEditorCurves" };
|
||||||
|
//string[] editorCurveSetNames = new string[] { "m_EditorCurves" };
|
||||||
|
|
||||||
|
foreach (var editorCurveSetName in editorCurveSetNames)
|
||||||
|
{
|
||||||
|
var curCurveSet = soClip.FindProperty(editorCurveSetName);
|
||||||
|
int curCurveSetLenght = curCurveSet.arraySize;
|
||||||
|
if (curCurveSetLenght == 0)
|
||||||
|
{
|
||||||
|
Debug.Log("Can not fine editor curves in " + editorCurveSetName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int curveSetIndex = 0; curveSetIndex < curCurveSetLenght; curveSetIndex++)
|
||||||
|
{
|
||||||
|
var curCurveInfo = curCurveSet.GetArrayElementAtIndex(curveSetIndex);
|
||||||
|
Debug.Log(editorCurveSetName + " index : " + curveSetIndex + " attribute: " +
|
||||||
|
curCurveInfo.FindPropertyRelative("attribute").stringValue);
|
||||||
|
|
||||||
|
var curCurve = curCurveInfo.FindPropertyRelative("curve");
|
||||||
|
var curCurveData = curCurve.FindPropertyRelative("m_Curve");
|
||||||
|
int curCurveDatalength = curCurveData.arraySize;
|
||||||
|
Debug.Log("curve lenght:" + curCurveDatalength);
|
||||||
|
|
||||||
|
for (int curveDataIndex = 3; curveDataIndex < curCurveDatalength; curveDataIndex++)
|
||||||
|
{
|
||||||
|
ProcessOneKeyframeOfEditorCurve(curveDataIndex, curCurveData, oneKeyframeTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] curveSetNames = new string[]
|
||||||
|
{ "m_PositionCurves", "m_RotationCurves", "m_ScaleCurves", "m_FloatCurves" };
|
||||||
|
|
||||||
|
foreach (var curveSetName in curveSetNames)
|
||||||
|
{
|
||||||
|
var curCurveSet = soClip.FindProperty(curveSetName);
|
||||||
|
int curCurveSetLenght = curCurveSet.arraySize;
|
||||||
|
if (curCurveSetLenght == 0)
|
||||||
|
{
|
||||||
|
Debug.Log("Can not fine curves in " + curveSetName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isHaveAttribute = curveSetName == "m_FloatCurves";
|
||||||
|
|
||||||
|
for (int curveSetIndex = 0; curveSetIndex < curCurveSetLenght; curveSetIndex++)
|
||||||
|
{
|
||||||
|
var curCurveInfo = curCurveSet.GetArrayElementAtIndex(curveSetIndex);
|
||||||
|
if (isHaveAttribute)
|
||||||
|
{
|
||||||
|
Debug.Log(curveSetName + " index : " + curveSetIndex + " attribute: " +
|
||||||
|
curCurveInfo.FindPropertyRelative("attribute").stringValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log(curveSetName + " index : " + curveSetIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
var curCurve = curCurveInfo.FindPropertyRelative("curve");
|
||||||
|
var curCurveData = curCurve.FindPropertyRelative("m_Curve");
|
||||||
|
int curCurveDatalength = curCurveData.arraySize;
|
||||||
|
Debug.Log("curve lenght:" + curCurveDatalength);
|
||||||
|
|
||||||
|
for (int curveDataIndex = 3; curveDataIndex < curCurveDatalength; curveDataIndex++)
|
||||||
|
{
|
||||||
|
ProcessOneKeyframeCurve(curveDataIndex, curCurveData, oneKeyframeTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
soClip.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
const int kBrokenMask = 1 << 0;
|
||||||
|
const int kLeftTangentMask = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4;
|
||||||
|
const int kRightTangentMask = 1 << 5 | 1 << 6 | 1 << 7 | 1 << 8;
|
||||||
|
|
||||||
|
static void SetKeyBroken(SerializedProperty keyframeInfo, bool broken)
|
||||||
|
{
|
||||||
|
var tangentModeProp = keyframeInfo.FindPropertyRelative("tangentMode");
|
||||||
|
if (broken)
|
||||||
|
tangentModeProp.intValue |= kBrokenMask;
|
||||||
|
else
|
||||||
|
tangentModeProp.intValue &= ~kBrokenMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetKeyLeftTangentMode(SerializedProperty keyframeInfo, AnimationUtility.TangentMode tangentMode)
|
||||||
|
{
|
||||||
|
var tangentModeProp = keyframeInfo.FindPropertyRelative("tangentMode");
|
||||||
|
tangentModeProp.intValue &= ~kLeftTangentMask;
|
||||||
|
tangentModeProp.intValue |= (int)tangentMode << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetKeyRightTangentMode(SerializedProperty keyframeInfo, AnimationUtility.TangentMode tangentMode)
|
||||||
|
{
|
||||||
|
var tangentModeProp = keyframeInfo.FindPropertyRelative("tangentMode");
|
||||||
|
tangentModeProp.intValue &= ~kRightTangentMask;
|
||||||
|
tangentModeProp.intValue |= (int)tangentMode << 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float CalculateLinearTangent(SerializedProperty keyframeInfo, SerializedProperty toKeyframeInfo)
|
||||||
|
{
|
||||||
|
float curTime = keyframeInfo.FindPropertyRelative("time").floatValue;
|
||||||
|
float toTime = toKeyframeInfo.FindPropertyRelative("time").floatValue;
|
||||||
|
float curValue = keyframeInfo.FindPropertyRelative("value").floatValue;
|
||||||
|
float toValue = toKeyframeInfo.FindPropertyRelative("value").floatValue;
|
||||||
|
|
||||||
|
float dt = toTime - curTime;
|
||||||
|
if (Mathf.Abs(dt) < float.Epsilon)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return (toValue - curValue) / dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ProcessOneKeyframeOfEditorCurve(int curveDataIndex, SerializedProperty curCurveData,
|
||||||
|
float oneKeyframeTime)
|
||||||
|
{
|
||||||
|
var keyframe1 = curCurveData.GetArrayElementAtIndex(curveDataIndex - 3);
|
||||||
|
var keyframe2 = curCurveData.GetArrayElementAtIndex(curveDataIndex - 2);
|
||||||
|
var keyframe3 = curCurveData.GetArrayElementAtIndex(curveDataIndex - 1);
|
||||||
|
var keyframe4 = curCurveData.GetArrayElementAtIndex(curveDataIndex);
|
||||||
|
|
||||||
|
float time1 = keyframe1.FindPropertyRelative("time").floatValue;
|
||||||
|
float time2 = keyframe2.FindPropertyRelative("time").floatValue;
|
||||||
|
float time3 = keyframe3.FindPropertyRelative("time").floatValue;
|
||||||
|
float time4 = keyframe4.FindPropertyRelative("time").floatValue;
|
||||||
|
|
||||||
|
SerializedProperty outSlope1 = keyframe1.FindPropertyRelative("outSlope");
|
||||||
|
SerializedProperty inSlope2 = keyframe2.FindPropertyRelative("inSlope");
|
||||||
|
SerializedProperty outSlope2 = keyframe2.FindPropertyRelative("outSlope");
|
||||||
|
SerializedProperty inSlope3 = keyframe3.FindPropertyRelative("inSlope");
|
||||||
|
SerializedProperty outSlope3 = keyframe3.FindPropertyRelative("outSlope");
|
||||||
|
SerializedProperty inSlope4 = keyframe4.FindPropertyRelative("inSlope");
|
||||||
|
|
||||||
|
float outTangetDegree1 = Mathf.Rad2Deg * Mathf.Atan(outSlope1.floatValue * oneKeyframeTime);
|
||||||
|
float inTangetDegree2 = Mathf.Rad2Deg * Mathf.Atan(inSlope2.floatValue * oneKeyframeTime);
|
||||||
|
float outTangetDegree3 = Mathf.Rad2Deg * Mathf.Atan(outSlope3.floatValue * oneKeyframeTime);
|
||||||
|
float inTangetDegree4 = Mathf.Rad2Deg * Mathf.Atan(inSlope4.floatValue * oneKeyframeTime);
|
||||||
|
|
||||||
|
float AngleDiff1 = Mathf.Abs(outTangetDegree1 - inTangetDegree2);
|
||||||
|
float AngleDiff2 = Mathf.Abs(outTangetDegree3 - inTangetDegree4);
|
||||||
|
//check constant keyframe
|
||||||
|
if ((time2 - time1 <= oneKeyframeTime)
|
||||||
|
&& (time3 - time2 <= oneKeyframeTime)
|
||||||
|
&& (time4 - time3 <= oneKeyframeTime)
|
||||||
|
&& AngleDiff1 > TangentDegreeDifferent
|
||||||
|
&& AngleDiff2 > TangentDegreeDifferent)
|
||||||
|
{
|
||||||
|
Debug.Log(string.Format("index:{0},{1},{2},{3}", curveDataIndex - 3, curveDataIndex - 2,
|
||||||
|
curveDataIndex - 1, curveDataIndex));
|
||||||
|
|
||||||
|
var keyframeValue = keyframe1.FindPropertyRelative("value");
|
||||||
|
switch (keyframeValue.propertyType)
|
||||||
|
{
|
||||||
|
case SerializedPropertyType.Float:
|
||||||
|
{
|
||||||
|
SetKeyBroken(keyframe1, true);
|
||||||
|
SetKeyRightTangentMode(keyframe1, AnimationUtility.TangentMode.Linear);
|
||||||
|
|
||||||
|
SetKeyBroken(keyframe2, true);
|
||||||
|
SetKeyLeftTangentMode(keyframe2, AnimationUtility.TangentMode.Linear);
|
||||||
|
SetKeyRightTangentMode(keyframe2, AnimationUtility.TangentMode.Constant);
|
||||||
|
inSlope2.floatValue = CalculateLinearTangent(keyframe2, keyframe1);
|
||||||
|
outSlope2.floatValue = float.PositiveInfinity;
|
||||||
|
|
||||||
|
SetKeyBroken(keyframe3, true);
|
||||||
|
SetKeyLeftTangentMode(keyframe3, AnimationUtility.TangentMode.Constant);
|
||||||
|
SetKeyRightTangentMode(keyframe3, AnimationUtility.TangentMode.Linear);
|
||||||
|
inSlope3.floatValue = float.PositiveInfinity;
|
||||||
|
outSlope3.floatValue = CalculateLinearTangent(keyframe3, keyframe4);
|
||||||
|
|
||||||
|
SetKeyBroken(keyframe4, true);
|
||||||
|
SetKeyLeftTangentMode(keyframe4, AnimationUtility.TangentMode.Linear);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vector3 ConstantVector3 =
|
||||||
|
new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
|
||||||
|
|
||||||
|
static Vector4 ConstantVector4 = new Vector4(float.PositiveInfinity, float.PositiveInfinity,
|
||||||
|
float.PositiveInfinity, float.PositiveInfinity);
|
||||||
|
|
||||||
|
static Quaternion ConstantQuaternion =
|
||||||
|
new Quaternion(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, 1);
|
||||||
|
|
||||||
|
static void ProcessOneKeyframeCurve(int curveDataIndex, SerializedProperty curCurveData, float oneKeyframeTime)
|
||||||
|
{
|
||||||
|
var keyframe1 = curCurveData.GetArrayElementAtIndex(curveDataIndex - 3);
|
||||||
|
var keyframe2 = curCurveData.GetArrayElementAtIndex(curveDataIndex - 2);
|
||||||
|
var keyframe3 = curCurveData.GetArrayElementAtIndex(curveDataIndex - 1);
|
||||||
|
var keyframe4 = curCurveData.GetArrayElementAtIndex(curveDataIndex);
|
||||||
|
|
||||||
|
float time1 = keyframe1.FindPropertyRelative("time").floatValue;
|
||||||
|
float time2 = keyframe2.FindPropertyRelative("time").floatValue;
|
||||||
|
float time3 = keyframe3.FindPropertyRelative("time").floatValue;
|
||||||
|
float time4 = keyframe4.FindPropertyRelative("time").floatValue;
|
||||||
|
|
||||||
|
SerializedProperty outSlope1 = keyframe1.FindPropertyRelative("outSlope");
|
||||||
|
SerializedProperty inSlope2 = keyframe2.FindPropertyRelative("inSlope");
|
||||||
|
SerializedProperty outSlope2 = keyframe2.FindPropertyRelative("outSlope");
|
||||||
|
SerializedProperty inSlope3 = keyframe3.FindPropertyRelative("inSlope");
|
||||||
|
SerializedProperty outSlope3 = keyframe3.FindPropertyRelative("outSlope");
|
||||||
|
SerializedProperty inSlope4 = keyframe4.FindPropertyRelative("inSlope");
|
||||||
|
|
||||||
|
//check constant keyframe
|
||||||
|
if ((time2 - time1 <= oneKeyframeTime)
|
||||||
|
&& (time3 - time2 <= oneKeyframeTime)
|
||||||
|
&& (time4 - time3 <= oneKeyframeTime)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var keyframeValue = keyframe1.FindPropertyRelative("value");
|
||||||
|
|
||||||
|
Debug.Log(string.Format("index:{0},{1},{2},{3}", curveDataIndex - 3, curveDataIndex - 2,
|
||||||
|
curveDataIndex - 1, curveDataIndex));
|
||||||
|
|
||||||
|
switch (keyframeValue.propertyType)
|
||||||
|
{
|
||||||
|
case SerializedPropertyType.Float:
|
||||||
|
inSlope2.floatValue = float.PositiveInfinity;
|
||||||
|
outSlope2.floatValue = float.PositiveInfinity;
|
||||||
|
inSlope3.floatValue = float.PositiveInfinity;
|
||||||
|
outSlope3.floatValue = float.PositiveInfinity;
|
||||||
|
break;
|
||||||
|
case SerializedPropertyType.Vector3:
|
||||||
|
inSlope2.vector3Value = ConstantVector3;
|
||||||
|
outSlope2.vector3Value = ConstantVector3;
|
||||||
|
inSlope3.vector3Value = ConstantVector3;
|
||||||
|
outSlope3.vector3Value = ConstantVector3;
|
||||||
|
break;
|
||||||
|
case SerializedPropertyType.Vector4:
|
||||||
|
inSlope2.vector4Value = ConstantVector4;
|
||||||
|
outSlope2.vector4Value = ConstantVector4;
|
||||||
|
inSlope3.vector4Value = ConstantVector4;
|
||||||
|
outSlope3.vector4Value = ConstantVector4;
|
||||||
|
break;
|
||||||
|
case SerializedPropertyType.Quaternion:
|
||||||
|
inSlope2.quaternionValue = ConstantQuaternion;
|
||||||
|
outSlope2.quaternionValue = ConstantQuaternion;
|
||||||
|
inSlope3.quaternionValue = ConstantQuaternion;
|
||||||
|
outSlope3.quaternionValue = ConstantQuaternion;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MyFix(AnimationClip clip)
|
||||||
|
{
|
||||||
|
var soClip = new SerializedObject(clip);
|
||||||
|
|
||||||
|
string[] editorCurveSetNames = new string[] { "m_EditorCurves", "m_EulerEditorCurves", "m_RotationCurves" };
|
||||||
|
//string[] editorCurveSetNames = new string[] { "m_EditorCurves" };
|
||||||
|
|
||||||
|
foreach (var editorCurveSetName in editorCurveSetNames)
|
||||||
|
{
|
||||||
|
var curCurveSet = soClip.FindProperty(editorCurveSetName);
|
||||||
|
|
||||||
|
for (int curveSetIndex = 0; curveSetIndex < curCurveSet.arraySize; curveSetIndex++)
|
||||||
|
{
|
||||||
|
var curCurveInfo = curCurveSet.GetArrayElementAtIndex(curveSetIndex);
|
||||||
|
var curCurve = curCurveInfo.FindPropertyRelative("curve");
|
||||||
|
var curCurveData = curCurve.FindPropertyRelative("m_Curve");
|
||||||
|
int curCurveDataLength = curCurveData.arraySize;
|
||||||
|
Debug.Log("curve lenght:" + curCurveDataLength);
|
||||||
|
for (int index = 0; index < curCurveDataLength; index++)
|
||||||
|
{
|
||||||
|
var keyframe = curCurveData.GetArrayElementAtIndex(index);
|
||||||
|
if (editorCurveSetName.Equals("m_RotationCurves"))
|
||||||
|
{
|
||||||
|
var infiniteQuaternion = new Quaternion(
|
||||||
|
float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity,
|
||||||
|
float.PositiveInfinity);
|
||||||
|
keyframe.FindPropertyRelative("inSlope").quaternionValue = infiniteQuaternion;
|
||||||
|
keyframe.FindPropertyRelative("outSlope").quaternionValue = infiniteQuaternion;
|
||||||
|
}
|
||||||
|
else if (keyframe.FindPropertyRelative("attribute")
|
||||||
|
.stringValue.Contains("m_LocalRotation"))
|
||||||
|
{
|
||||||
|
keyframe.FindPropertyRelative("inSlope").floatValue = float.PositiveInfinity;
|
||||||
|
keyframe.FindPropertyRelative("outSlope").floatValue = float.PositiveInfinity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
soClip.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: c33bcfd692627dd4a97ac1cd1d930420
|
guid: c5f9faab3428c1a479eecff16161bafa
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
|
@ -1,65 +1,131 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using Bread2Unity;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Animation = Bread2Unity.Animation;
|
||||||
|
|
||||||
namespace Bread2Unity
|
namespace Bread2Unity
|
||||||
{
|
{
|
||||||
public class BCCAD : IDataModel
|
public class BCCAD : DataModel
|
||||||
{
|
{
|
||||||
|
public static BCCAD Read(byte[] bytes)
|
||||||
public BCCAD Read(byte[] bytes)
|
|
||||||
{
|
{
|
||||||
sheetW = BitConverter.ToUInt16(bytes, 4);
|
BCCAD bccad = new BCCAD();
|
||||||
sheetH = BitConverter.ToUInt16(bytes, 6);
|
var byteBuffer = new ByteBuffer(bytes);
|
||||||
|
byteBuffer.ReadInt(); //timestamp
|
||||||
|
bccad.sheetW = byteBuffer.ReadUShort();
|
||||||
// int max = (bytes[8] * 2) + 12;
|
bccad.sheetH = byteBuffer.ReadUShort();
|
||||||
int max = 64 * bytes[8] + 12;
|
// Sprites
|
||||||
|
var spritesNum = byteBuffer.ReadInt();
|
||||||
// note this doesn't account for empty sprites, but I'll get there when i get there
|
for (int i = 0; i < spritesNum; i++)
|
||||||
for (int i = 12; i < max; i += 2) // 16 bit bytes, skip every 2nd byte
|
|
||||||
{
|
{
|
||||||
ISprite spriteParts_ = new ISprite();
|
BccadSprite bccadSprite = new BccadSprite();
|
||||||
int compare = 0;
|
var partsNum = byteBuffer.ReadInt();
|
||||||
for (int j = 0; j < bytes[i]; j++)
|
for (int j = 0; j < partsNum; j++)
|
||||||
{
|
{
|
||||||
int ind = i + 4 + (64 * j);
|
SpritePart part = new SpritePart();
|
||||||
|
var region = new Region
|
||||||
|
{
|
||||||
|
regionX = byteBuffer.ReadUShort(),
|
||||||
|
regionY = byteBuffer.ReadUShort(),
|
||||||
|
regionW = byteBuffer.ReadUShort(),
|
||||||
|
regionH = byteBuffer.ReadUShort()
|
||||||
|
};
|
||||||
|
var result = bccad.regions.FindIndex(x => Equals(x, region));
|
||||||
|
if (result == -1)
|
||||||
|
{
|
||||||
|
bccad.regions.Add(region);
|
||||||
|
part.RegionIndex = new RegionIndex(bccad.regions.Count - 1, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var repeatedNumber = bccadSprite.parts.Count(p => p.RegionIndex.Index == result);
|
||||||
|
part.RegionIndex = new RegionIndex(result, repeatedNumber);
|
||||||
|
|
||||||
|
}
|
||||||
|
part.PosX = byteBuffer.ReadShort();
|
||||||
|
part.PosY = byteBuffer.ReadShort();
|
||||||
|
part.StretchX = byteBuffer.ReadFloat();
|
||||||
|
part.StretchY = byteBuffer.ReadFloat();
|
||||||
|
part.Rotation = byteBuffer.ReadFloat();
|
||||||
|
part.FlipX = byteBuffer.ReadByte() != 0;
|
||||||
|
part.FlipY = byteBuffer.ReadByte() != 0;
|
||||||
|
//Multicolor and screen color
|
||||||
|
part.Multicolor = new Color(Convert.ToInt32(byteBuffer.ReadByte() & 0xff) / 255f,
|
||||||
|
Convert.ToInt32(byteBuffer.ReadByte() & 0xff) / 255f,
|
||||||
|
Convert.ToInt32(byteBuffer.ReadByte() & 0xff) / 255f);
|
||||||
|
part.ScreenColor = new Color(Convert.ToInt32(byteBuffer.ReadByte() & 0xff) / 255f,
|
||||||
|
Convert.ToInt32(byteBuffer.ReadByte() & 0xff) / 255f,
|
||||||
|
Convert.ToInt32(byteBuffer.ReadByte() & 0xff) / 255f);
|
||||||
|
|
||||||
ISpritePart part = new ISpritePart();
|
part.Opacity = byteBuffer.ReadByte();
|
||||||
part.regionX = BitConverter.ToUInt16(bytes, ind + 0);
|
for (int k = 0; k < 12; k++)
|
||||||
part.regionY = BitConverter.ToUInt16(bytes, ind + 2);
|
{
|
||||||
part.regionW = BitConverter.ToUInt16(bytes, ind + 4);
|
byteBuffer.ReadByte();
|
||||||
part.regionH = BitConverter.ToUInt16(bytes, ind + 6);
|
}
|
||||||
part.posX = BitConverter.ToInt16(bytes, ind + 8);
|
|
||||||
part.posY = BitConverter.ToInt16(bytes, ind + 10);
|
|
||||||
part.stretchX = BitConverter.ToSingle(bytes, ind + 12);
|
|
||||||
part.stretchY = BitConverter.ToSingle(bytes, ind + 14);
|
|
||||||
part.rotation = BitConverter.ToSingle(bytes, ind + 16);
|
|
||||||
part.flipX = bytes[ind + 18] != (byte)0;
|
|
||||||
part.flipY = bytes[ind + 20] != (byte)0;
|
|
||||||
// im sure the values between 20 and 28 are important so remind me to come back to these
|
|
||||||
part.opacity = bytes[ind + 28];
|
|
||||||
|
|
||||||
Debug.Log("offset: " + ind + ", val: " + part.regionX);
|
part.designation = byteBuffer.ReadByte();
|
||||||
|
part.unknown = byteBuffer.ReadShort();
|
||||||
spriteParts_.parts.Add(part);
|
part.tlDepth = byteBuffer.ReadFloat();
|
||||||
|
part.blDepth = byteBuffer.ReadFloat();
|
||||||
compare += 64;
|
part.trDepth = byteBuffer.ReadFloat();
|
||||||
|
part.brDepth = byteBuffer.ReadFloat();
|
||||||
|
bccadSprite.parts.Add(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
sprites.Add(spriteParts_);
|
bccad.sprites.Add(bccadSprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animations
|
||||||
|
var animationsNum = byteBuffer.ReadInt();
|
||||||
|
for (int i = 0; i < animationsNum; i++)
|
||||||
|
{
|
||||||
|
var anim = new Animation();
|
||||||
|
var nameBuilder = new StringBuilder();
|
||||||
|
var length = Convert.ToInt32(byteBuffer.ReadByte());
|
||||||
|
for (int j = 0; j < length; j++)
|
||||||
|
{
|
||||||
|
nameBuilder.Append(Convert.ToChar(byteBuffer.ReadByte()));
|
||||||
|
}
|
||||||
|
|
||||||
i += compare;
|
for (int j = 0; j < 4 - ((length + 1) % 4); j++)
|
||||||
|
{
|
||||||
|
byteBuffer.ReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
anim.Name = nameBuilder.ToString();
|
||||||
|
anim.InterpolationInt = byteBuffer.ReadInt();
|
||||||
|
var stepsNum = byteBuffer.ReadInt();
|
||||||
|
for (int j = 0; j < stepsNum; j++)
|
||||||
|
{
|
||||||
|
var spriteIndex = byteBuffer.ReadUShort();
|
||||||
|
var step = new AnimationStep
|
||||||
|
{
|
||||||
|
Delay = byteBuffer.ReadUShort(),
|
||||||
|
TranslateX = byteBuffer.ReadShort(),
|
||||||
|
TranslateY = byteBuffer.ReadShort(),
|
||||||
|
Depth = byteBuffer.ReadFloat(),
|
||||||
|
StretchX = byteBuffer.ReadFloat(),
|
||||||
|
StretchY = byteBuffer.ReadFloat(),
|
||||||
|
Rotation = byteBuffer.ReadFloat(),
|
||||||
|
Color = new Color(Convert.ToInt32(byteBuffer.ReadByte() & 0xff) / 255f,
|
||||||
|
Convert.ToInt32(byteBuffer.ReadByte() & 0xff) / 255f,
|
||||||
|
Convert.ToInt32(byteBuffer.ReadByte() & 0xff) / 255f),
|
||||||
|
BccadSprite = bccad.sprites[spriteIndex]
|
||||||
|
};
|
||||||
|
byteBuffer.ReadByte();
|
||||||
|
byteBuffer.ReadByte();
|
||||||
|
byteBuffer.ReadByte();
|
||||||
|
step.Opacity = Convert.ToByte(Convert.ToInt32(byteBuffer.ReadShort()) & 0xFF);
|
||||||
|
anim.Steps.Add(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
bccad.animations.Add(anim);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BCCAD()
|
return bccad;
|
||||||
{
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// sprites length bytes start = 12
|
|
||||||
}
|
}
|
||||||
}
|
}
|
134
Assets/Editor/bread2unity/BccadPrefab.cs
Normal file
134
Assets/Editor/bread2unity/BccadPrefab.cs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Bread2Unity;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public class BccadPrefab
|
||||||
|
{
|
||||||
|
public GameObject ParentObject { get; }
|
||||||
|
public readonly Dictionary<RegionIndex, GameObject> RegionToChild = new Dictionary<RegionIndex, GameObject>();
|
||||||
|
private readonly List<GameObject> _children = new List<GameObject>();
|
||||||
|
private readonly PrefabData _data;
|
||||||
|
private readonly BCCAD _bccad;
|
||||||
|
public float HeightRatio { get; }
|
||||||
|
public float WidthRatio { get; }
|
||||||
|
|
||||||
|
public BccadPrefab(PrefabData data, BCCAD bccad, Texture2D texture)
|
||||||
|
{
|
||||||
|
ParentObject = new GameObject(data.Name);
|
||||||
|
_data = data;
|
||||||
|
HeightRatio = (float)texture.height / bccad.sheetH;
|
||||||
|
WidthRatio = (float)texture.width / bccad.sheetW;
|
||||||
|
_bccad = bccad;
|
||||||
|
CalculateParts();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CalculateParts()
|
||||||
|
{
|
||||||
|
var defaultSprite = _bccad.sprites[_data.SpriteIndex];
|
||||||
|
if (_data.Animations.Count == 0)
|
||||||
|
{
|
||||||
|
for (var index = 0; index < defaultSprite.parts.Count; index++)
|
||||||
|
{
|
||||||
|
var part = defaultSprite.parts[index];
|
||||||
|
var child = new GameObject($"{_data.Name} {index}");
|
||||||
|
child.transform.SetParent(ParentObject.transform);
|
||||||
|
_children.Add(child);
|
||||||
|
RegionToChild.Add(part.RegionIndex, child);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get all regions
|
||||||
|
var anim = _data.Animations;
|
||||||
|
var bccadSprites = anim.SelectMany(a => a.Steps).Select(step => step.BccadSprite).Distinct().ToList();
|
||||||
|
var availableRegions = bccadSprites.SelectMany(sprite => sprite.parts).Select(part => part.RegionIndex)
|
||||||
|
.Distinct().ToList();
|
||||||
|
|
||||||
|
var childIndex = 0;
|
||||||
|
while (availableRegions.Count > 0)
|
||||||
|
{
|
||||||
|
var child = new GameObject($"{_data.Name} {childIndex}");
|
||||||
|
child.transform.SetParent(ParentObject.transform);
|
||||||
|
_children.Add(child);
|
||||||
|
var nextRegion = availableRegions[0];
|
||||||
|
availableRegions.RemoveAt(0);
|
||||||
|
var regionsList = new List<RegionIndex> { nextRegion };
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var notAdjacentRegions = FindNotAdjacentRegions(regionsList, bccadSprites, availableRegions);
|
||||||
|
var maybeNotAdjacentRegion = availableRegions.Select(reg => (RegionIndex?)reg)
|
||||||
|
.FirstOrDefault(region => notAdjacentRegions.Contains((RegionIndex)region));
|
||||||
|
if (maybeNotAdjacentRegion is { } notAdjacentRegion)
|
||||||
|
{
|
||||||
|
availableRegions.Remove(notAdjacentRegion);
|
||||||
|
regionsList.Add(notAdjacentRegion);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var r in regionsList)
|
||||||
|
{
|
||||||
|
RegionToChild.Add(r, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
childIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<RegionIndex> FindNotAdjacentRegions(List<RegionIndex> regions, List<BccadSprite> sprites, List<RegionIndex> availableRegions)
|
||||||
|
{
|
||||||
|
var notAdjacentRegions = new List<RegionIndex>(availableRegions);
|
||||||
|
foreach (var sprite in sprites)
|
||||||
|
{
|
||||||
|
var regionsOfSprite = sprite.parts.Select(part => part.RegionIndex).ToArray();
|
||||||
|
if (regionsOfSprite.Intersect(regions).Any())
|
||||||
|
{
|
||||||
|
foreach (var r in regionsOfSprite)
|
||||||
|
{
|
||||||
|
notAdjacentRegions.Remove(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return notAdjacentRegions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Tuple<int, SpritePart, GameObject>> GetHiddenParts()
|
||||||
|
{
|
||||||
|
var sprite = _bccad.sprites[_data.SpriteIndex];
|
||||||
|
var pairs = new List<Tuple<int, SpritePart, GameObject>>();
|
||||||
|
var gameObjects = new List<GameObject>(_children);
|
||||||
|
foreach (var part in sprite.parts)
|
||||||
|
{
|
||||||
|
var child = RegionToChild[part.RegionIndex];
|
||||||
|
gameObjects.Remove(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var gameObject in gameObjects)
|
||||||
|
{
|
||||||
|
//find a random part associated with the game object
|
||||||
|
var region = RegionToChild.FirstOrDefault(keyValuePair => keyValuePair.Value == gameObject)
|
||||||
|
.Key;
|
||||||
|
var parts = _data.Animations.SelectMany(anim => anim.Steps).Select(s => s.BccadSprite)
|
||||||
|
.SelectMany(bccadSprite => bccadSprite.parts);
|
||||||
|
|
||||||
|
var partIndexPair = parts
|
||||||
|
.Select((value, index) => new { value, index })
|
||||||
|
.First(pair => pair.value.RegionIndex.Equals(region));
|
||||||
|
var index = partIndexPair.index;
|
||||||
|
var part = partIndexPair.value;
|
||||||
|
pairs.Add(new Tuple<int, SpritePart, GameObject>(index, part, gameObject));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return pairs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: ddf1fd563dabc6040a863537a081843a
|
guid: 7be9a40b9a8419b43aa454fa09e9ce78
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
61
Assets/Editor/bread2unity/BccadTest.cs
Normal file
61
Assets/Editor/bread2unity/BccadTest.cs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public static class BccadTest
|
||||||
|
{
|
||||||
|
public static void TestBccad()
|
||||||
|
{
|
||||||
|
const string folderPath =
|
||||||
|
"C:\\Users\\Eliya\\games\\3DS\\PackEnglishV12\\PackEnglishV12\\PackHack\\ExtractedRomFS\\cellanim";
|
||||||
|
var allFiles = Directory.GetFiles(folderPath, "*.bccad", SearchOption.AllDirectories);
|
||||||
|
var problematicFiles = new HashSet<string>();
|
||||||
|
foreach (var file in allFiles)
|
||||||
|
{
|
||||||
|
var bccad = BCCAD.Read(File.ReadAllBytes(file));
|
||||||
|
var name = Path.GetFileName(file);
|
||||||
|
for (var spriteIndex = 0; spriteIndex < bccad.sprites.Count; spriteIndex++)
|
||||||
|
{
|
||||||
|
var sprite = bccad.sprites[spriteIndex];
|
||||||
|
/*for (var partIndex = 0; partIndex < sprite.parts.Count; partIndex++)
|
||||||
|
{
|
||||||
|
var part = sprite.parts[partIndex];
|
||||||
|
if (part.Multicolor != Color.white)
|
||||||
|
Debug.Log($"multycolor not white at {name} sprite: {spriteIndex} part: {partIndex}");
|
||||||
|
|
||||||
|
if (part.ScreenColor != Color.black)
|
||||||
|
Debug.Log($"screen color not black at {name} sprite: {spriteIndex} part: {partIndex}");
|
||||||
|
}*/
|
||||||
|
var v = sprite.parts.GroupBy(p => p.FlipX)
|
||||||
|
.Any(a => a.Select(part => part.RegionIndex.Index).Distinct().Count() < a.Count());
|
||||||
|
// if (sprite.parts.Select(part => part.Region).Distinct().Count() < sprite.parts.Count)
|
||||||
|
if(v)
|
||||||
|
// Debug.Log($"duplicate regions {name} sprite: {spriteIndex}");
|
||||||
|
problematicFiles.Add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*for (var animIndex = 0; animIndex < bccad.animations.Count; animIndex++)
|
||||||
|
{
|
||||||
|
var anim = bccad.animations[animIndex];
|
||||||
|
if (anim.Interpolated) Debug.Log($"interpolated {name} anim: {animIndex}");
|
||||||
|
for (var stepIndex = 0; stepIndex < anim.Steps.Count; stepIndex++)
|
||||||
|
{
|
||||||
|
var step = anim.Steps[stepIndex];
|
||||||
|
if(step.Color != Color.white)
|
||||||
|
Debug.Log($"step color not white at {name} anim: {animIndex} step: {stepIndex}");
|
||||||
|
if (step.Opacity != 255)
|
||||||
|
Debug.Log($"step opacity not 255 at {name} anim: {animIndex} step: {stepIndex}");
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var filename in problematicFiles)
|
||||||
|
{
|
||||||
|
Debug.Log(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Editor/bread2unity/BccadTest.cs.meta
Normal file
11
Assets/Editor/bread2unity/BccadTest.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af00b07ba074d9c44ab931b3b98e5605
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,47 +1,131 @@
|
||||||
using System.Collections;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
|
|
||||||
using Starpelly;
|
using Bread2Unity;
|
||||||
|
|
||||||
namespace Bread2Unity
|
namespace Bread2Unity
|
||||||
{
|
{
|
||||||
public class Bread2Unity : EditorWindow
|
public class Bread2UnityGUI : EditorWindow
|
||||||
{
|
{
|
||||||
public const string editorFolderName = "bread2unity";
|
public const string EditorFolderName = "bread2unity";
|
||||||
|
private GameObject _prefab;
|
||||||
|
private DataModel animation;
|
||||||
|
private List<PrefabData> _prefabDataList = new List<PrefabData>();
|
||||||
|
private List<string> _animationsIndexes;
|
||||||
|
private Vector2 _scrollPosition;
|
||||||
|
|
||||||
|
|
||||||
[MenuItem("Tools/bread2unity")]
|
[MenuItem("Tools/bread2unity")]
|
||||||
public static void ShowWindow()
|
public static void ShowWindow()
|
||||||
{
|
{
|
||||||
EditorWindow.GetWindow<Bread2Unity>("bread2unity");
|
GetWindow<Bread2UnityGUI>("bread2unity");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateGUI()
|
||||||
|
{
|
||||||
|
_animationsIndexes = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnGUI()
|
public void OnGUI()
|
||||||
{
|
{
|
||||||
Texture logo = (Texture)AssetDatabase.LoadAssetAtPath($"Assets/Editor/{editorFolderName}/logo.png", typeof(Texture));
|
Texture logo =
|
||||||
GUILayout.Box(logo, new GUILayoutOption[] { GUILayout.ExpandWidth(true), GUILayout.Height(60) });
|
(Texture)AssetDatabase.LoadAssetAtPath($"Assets/Editor/{EditorFolderName}/logo.png", typeof(Texture));
|
||||||
|
GUILayout.Box(logo, GUILayout.ExpandWidth(true), GUILayout.Height(60));
|
||||||
GUILayout.Space(30);
|
GUILayout.Space(30);
|
||||||
|
|
||||||
GUIStyle desc = EditorStyles.label;
|
GUIStyle desc = EditorStyles.label;
|
||||||
desc.wordWrap = true;
|
desc.wordWrap = true;
|
||||||
desc.fontStyle = FontStyle.BoldAndItalic;
|
desc.fontStyle = FontStyle.BoldAndItalic;
|
||||||
|
|
||||||
GUILayout.Box("bread2unity is a tool built with the purpose of converting RH Megamix and Fever animations to unity. And to generally speed up development by a lot." +
|
GUILayout.Box(
|
||||||
"\nCreated by Starpelly.", desc);
|
"bread2unity is a tool built with the purpose of converting RH Megamix animations to unity. And to generally speed up development by a lot.",
|
||||||
|
desc);
|
||||||
|
|
||||||
GUILayout.Space(120);
|
GUILayout.Space(60);
|
||||||
|
EditorGUIUtility.labelWidth = 100;
|
||||||
|
|
||||||
if (GUILayout.Button("Test"))
|
GUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.PrefixLabel("Prefab");
|
||||||
|
_prefab = (GameObject)EditorGUILayout.ObjectField(_prefab, typeof(GameObject), false);
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Separator();
|
||||||
|
|
||||||
|
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, GUILayout.MaxHeight(60));
|
||||||
|
var plusGuiStyle = new GUIStyle(GUI.skin.button)
|
||||||
{
|
{
|
||||||
string path = EditorUtility.OpenFilePanel("Open BCCAD File", null, "bccad");
|
fontSize = 35,
|
||||||
if (path.Length != 0)
|
alignment = TextAnchor.MiddleCenter
|
||||||
|
};
|
||||||
|
var minusGuiStyle = new GUIStyle(GUI.skin.button)
|
||||||
|
{
|
||||||
|
fontSize = 35,
|
||||||
|
alignment = TextAnchor.MiddleCenter
|
||||||
|
};
|
||||||
|
for (var i = 0; i < _prefabDataList.Count; i++)
|
||||||
|
{
|
||||||
|
var prefabData = _prefabDataList[i];
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
prefabData.Name = EditorGUILayout.TextField("Name", prefabData.Name);
|
||||||
|
_animationsIndexes[i] = EditorGUILayout.TextField("Animations", _animationsIndexes[i]);
|
||||||
|
prefabData.SpriteIndex =
|
||||||
|
EditorGUILayout.IntField("Sprite Index", prefabData.SpriteIndex);
|
||||||
|
if (GUILayout.Button("-", minusGuiStyle, GUILayout.Height(15), GUILayout.Width(15)))
|
||||||
{
|
{
|
||||||
var fileContent = File.ReadAllBytes(path);
|
_prefabDataList.RemoveAt(i);
|
||||||
new BCCAD().Read(fileContent);
|
_animationsIndexes.RemoveAt(i);
|
||||||
|
Repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
|
||||||
|
EditorGUILayout.Separator();
|
||||||
|
|
||||||
|
if (GUILayout.Button("+", plusGuiStyle, GUILayout.Height(40), GUILayout.Width(40)))
|
||||||
|
{
|
||||||
|
_prefabDataList.Add(new PrefabData("", 0));
|
||||||
|
_animationsIndexes.Add("");
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Separator();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Create Prefabs (WIP)") && _prefab != null)
|
||||||
|
{
|
||||||
|
//Choose png and bccad files
|
||||||
|
var bccadFilePath = EditorUtility.OpenFilePanel("Open BCCAD File", null, "bccad");
|
||||||
|
var bccadFileFolder = Path.GetDirectoryName(bccadFilePath);
|
||||||
|
var pngFilePath = EditorUtility.OpenFilePanel("Open Texture File", bccadFileFolder, "png");
|
||||||
|
if (bccadFilePath != null && pngFilePath != null)
|
||||||
|
{
|
||||||
|
var bccad = BCCAD.Read(File.ReadAllBytes(bccadFilePath));
|
||||||
|
var spriteTexture = SpriteCreator.ComputeSprites(bccad, pngFilePath, _prefab.name);
|
||||||
|
//Create prefab from prefab data
|
||||||
|
for (int i = 0; i < _prefabDataList.Count; i++)
|
||||||
|
{
|
||||||
|
List<int> animationIndexes;
|
||||||
|
var prefabData = _prefabDataList[i];
|
||||||
|
if (_animationsIndexes[i].Equals(""))
|
||||||
|
animationIndexes = new List<int>();
|
||||||
|
else
|
||||||
|
animationIndexes = _animationsIndexes[i].Split(',').Select(int.Parse).ToList();
|
||||||
|
prefabData.Animations =
|
||||||
|
animationIndexes.Select(index => bccad.animations[index]).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
PrefabCreator.CreatePrefab(_prefab, bccad, _prefabDataList, spriteTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("bccad test"))
|
||||||
|
{
|
||||||
|
BccadTest.TestBccad();
|
||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.BeginHorizontal();
|
GUILayout.BeginHorizontal();
|
||||||
|
@ -49,6 +133,7 @@ namespace Bread2Unity
|
||||||
{
|
{
|
||||||
Application.OpenURL("https://github.com/rhmodding/bread");
|
Application.OpenURL("https://github.com/rhmodding/bread");
|
||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
52
Assets/Editor/bread2unity/ByteBuffer.cs
Normal file
52
Assets/Editor/bread2unity/ByteBuffer.cs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public class ByteBuffer
|
||||||
|
{
|
||||||
|
private readonly byte[] _bytes;
|
||||||
|
|
||||||
|
public int ReadPoint{ get; private set; }
|
||||||
|
|
||||||
|
public ByteBuffer(byte[] bytes)
|
||||||
|
{
|
||||||
|
_bytes = bytes;
|
||||||
|
ReadPoint = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ushort ReadUShort()
|
||||||
|
{
|
||||||
|
var result = BitConverter.ToUInt16(_bytes, ReadPoint);
|
||||||
|
ReadPoint += sizeof(ushort);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short ReadShort()
|
||||||
|
{
|
||||||
|
var result = BitConverter.ToInt16(_bytes, ReadPoint);
|
||||||
|
ReadPoint += sizeof(short);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float ReadFloat()
|
||||||
|
{
|
||||||
|
float result = BitConverter.ToSingle(_bytes, ReadPoint);
|
||||||
|
ReadPoint += sizeof(float);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte ReadByte()
|
||||||
|
{
|
||||||
|
byte result = _bytes[ReadPoint];
|
||||||
|
ReadPoint += sizeof(byte);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ReadInt()
|
||||||
|
{
|
||||||
|
int result = BitConverter.ToInt32(_bytes, ReadPoint);
|
||||||
|
ReadPoint += sizeof(int);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Editor/bread2unity/ByteBuffer.cs.meta
Normal file
11
Assets/Editor/bread2unity/ByteBuffer.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1883cd0d74b590740936a6ffc3580eb6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
35
Assets/Editor/bread2unity/Model/Animation.cs
Normal file
35
Assets/Editor/bread2unity/Model/Animation.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public class Animation
|
||||||
|
{
|
||||||
|
public List<AnimationStep> Steps = new List<AnimationStep>();
|
||||||
|
public string Name;
|
||||||
|
public int InterpolationInt = 0;
|
||||||
|
|
||||||
|
public bool Interpolated => (InterpolationInt & 0b1) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AnimationStep
|
||||||
|
{
|
||||||
|
public ushort Delay;
|
||||||
|
|
||||||
|
public BccadSprite BccadSprite;
|
||||||
|
|
||||||
|
public short TranslateX;
|
||||||
|
public short TranslateY;
|
||||||
|
|
||||||
|
public float Depth;
|
||||||
|
|
||||||
|
public float StretchX;
|
||||||
|
public float StretchY;
|
||||||
|
|
||||||
|
public float Rotation;
|
||||||
|
|
||||||
|
public byte Opacity;
|
||||||
|
public Color Color;
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Editor/bread2unity/Model/Animation.cs.meta
Normal file
11
Assets/Editor/bread2unity/Model/Animation.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 994f68ab1a3b68b49a0ae01b0bf37f0c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
70
Assets/Editor/bread2unity/Model/BCCADSprite.cs
Normal file
70
Assets/Editor/bread2unity/Model/BCCADSprite.cs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public class BccadSprite
|
||||||
|
{
|
||||||
|
public List<SpritePart> parts = new List<SpritePart>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SpritePart
|
||||||
|
{
|
||||||
|
public RegionIndex RegionIndex;
|
||||||
|
|
||||||
|
public short PosX;
|
||||||
|
public short PosY;
|
||||||
|
|
||||||
|
public float StretchX;
|
||||||
|
public float StretchY;
|
||||||
|
|
||||||
|
public float Rotation;
|
||||||
|
|
||||||
|
public bool FlipX;
|
||||||
|
public bool FlipY;
|
||||||
|
|
||||||
|
public byte Opacity;
|
||||||
|
|
||||||
|
public byte designation;
|
||||||
|
public short unknown;
|
||||||
|
public float tlDepth;
|
||||||
|
public float blDepth;
|
||||||
|
public float trDepth;
|
||||||
|
public float brDepth;
|
||||||
|
public Color Multicolor;
|
||||||
|
public Color ScreenColor;
|
||||||
|
public Color GetColor() => new Color(Multicolor.r, Multicolor.g, Multicolor.b, Opacity / 255f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RegionIndex
|
||||||
|
{
|
||||||
|
public int Index { get; }
|
||||||
|
public int RepeatedNumber { get; }
|
||||||
|
|
||||||
|
public RegionIndex(int index, int repeatedNumber)
|
||||||
|
{
|
||||||
|
RepeatedNumber = repeatedNumber;
|
||||||
|
Index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(RegionIndex other)
|
||||||
|
{
|
||||||
|
return Index == other.Index && RepeatedNumber == other.RepeatedNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is RegionIndex other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hash = 23;
|
||||||
|
hash = hash * 31 + Index;
|
||||||
|
hash = hash * 31 + RepeatedNumber;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Editor/bread2unity/Model/BCCADSprite.cs.meta
Normal file
11
Assets/Editor/bread2unity/Model/BCCADSprite.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ea35991e78a2918439cc249d1e4e293e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
52
Assets/Editor/bread2unity/Model/DataModel.cs
Normal file
52
Assets/Editor/bread2unity/Model/DataModel.cs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public class DataModel
|
||||||
|
{
|
||||||
|
public List<Region> regions = new List<Region>();
|
||||||
|
public List<BccadSprite> sprites = new List<BccadSprite>();
|
||||||
|
public List<Animation> animations = new List<Animation>();
|
||||||
|
public int sheetW;
|
||||||
|
public int sheetH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Region
|
||||||
|
{
|
||||||
|
public ushort regionX;
|
||||||
|
public ushort regionY;
|
||||||
|
public ushort regionW;
|
||||||
|
public ushort regionH;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"regionX: {regionX} regionY: {regionY} regionW: {regionW} regionH: {regionH}";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool Equals(Region other)
|
||||||
|
{
|
||||||
|
return regionX == other.regionX && regionY == other.regionY && regionW == other.regionW &&
|
||||||
|
regionH == other.regionH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, obj)) return false;
|
||||||
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
|
if (obj.GetType() != this.GetType()) return false;
|
||||||
|
return Equals((Region)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hash = 23;
|
||||||
|
hash = hash * 31 + regionX;
|
||||||
|
hash = hash * 31 + regionY;
|
||||||
|
hash = hash * 31 + regionW;
|
||||||
|
hash = hash * 31 + regionH;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Editor/bread2unity/Model/DataModel.cs.meta
Normal file
11
Assets/Editor/bread2unity/Model/DataModel.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c90d75428898ac74e819c39b7249f133
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,24 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Bread2Unity
|
|
||||||
{
|
|
||||||
public class IAnimation
|
|
||||||
{
|
|
||||||
public List<IAnimationStep> steps;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class IAnimationStep
|
|
||||||
{
|
|
||||||
public ushort spriteIndex;
|
|
||||||
public ushort delay;
|
|
||||||
|
|
||||||
public float stretchX;
|
|
||||||
public float stretchY;
|
|
||||||
|
|
||||||
public float rotation;
|
|
||||||
|
|
||||||
public byte opacity;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Bread2Unity
|
|
||||||
{
|
|
||||||
public class IDataModel
|
|
||||||
{
|
|
||||||
public List<ISprite> sprites = new List<ISprite>();
|
|
||||||
public List<IAnimation> animations = new List<IAnimation>();
|
|
||||||
public int sheetW;
|
|
||||||
public int sheetH;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Bread2Unity
|
|
||||||
{
|
|
||||||
public class ISprite
|
|
||||||
{
|
|
||||||
public List<ISpritePart> parts = new List<ISpritePart>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ISpritePart
|
|
||||||
{
|
|
||||||
public ushort regionX;
|
|
||||||
public ushort regionY;
|
|
||||||
public ushort regionW;
|
|
||||||
public ushort regionH;
|
|
||||||
|
|
||||||
public short posX;
|
|
||||||
public short posY;
|
|
||||||
|
|
||||||
public float stretchX;
|
|
||||||
public float stretchY;
|
|
||||||
|
|
||||||
public float rotation;
|
|
||||||
|
|
||||||
public bool flipX;
|
|
||||||
public bool flipY;
|
|
||||||
|
|
||||||
public byte opacity;
|
|
||||||
}
|
|
||||||
}
|
|
83
Assets/Editor/bread2unity/PrefabCreator.cs
Normal file
83
Assets/Editor/bread2unity/PrefabCreator.cs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Bread2Unity;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using Animation = Bread2Unity.Animation;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public static class PrefabCreator
|
||||||
|
{
|
||||||
|
public static void CreatePrefab(GameObject prefab, BCCAD bccad,
|
||||||
|
List<PrefabData> prefabDataList, Texture2D texture)
|
||||||
|
{
|
||||||
|
var prefabName = prefab.name;
|
||||||
|
var spritesFolderPath =
|
||||||
|
$"Sprites\\Games\\{char.ToUpperInvariant(prefabName[0]) + prefabName.Substring(1)}";
|
||||||
|
var prefabAssetPath = AssetDatabase.GetAssetPath(prefab);
|
||||||
|
var root = PrefabUtility.LoadPrefabContents(prefabAssetPath);
|
||||||
|
foreach (var prefabData in prefabDataList)
|
||||||
|
{
|
||||||
|
var defaultSprite = bccad.sprites[prefabData.SpriteIndex];
|
||||||
|
var bccadPrefab = new BccadPrefab(prefabData, bccad, texture);
|
||||||
|
var newPrefab = bccadPrefab.ParentObject;
|
||||||
|
newPrefab.transform.SetParent(root.transform);
|
||||||
|
var sprites = Resources.LoadAll<Sprite>(spritesFolderPath).ToList()
|
||||||
|
.FindAll(s => s.name.Contains(texture.name));
|
||||||
|
for (var index = 0; index < defaultSprite.parts.Count; index++)
|
||||||
|
{
|
||||||
|
var spritePart = defaultSprite.parts[index];
|
||||||
|
var gameObjectPart = bccadPrefab.RegionToChild[spritePart.RegionIndex];
|
||||||
|
ApplySpriteSettingsFromBccad(gameObjectPart, spritePart, bccadPrefab,
|
||||||
|
sprites[spritePart.RegionIndex.Index],
|
||||||
|
index);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (partIndex, spritePart, hiddenGameObject) in bccadPrefab.GetHiddenParts())
|
||||||
|
{
|
||||||
|
hiddenGameObject.SetActive(false);
|
||||||
|
ApplySpriteSettingsFromBccad(hiddenGameObject, spritePart, bccadPrefab,
|
||||||
|
sprites[spritePart.RegionIndex.Index], partIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefabData.Animations.Count > 0)
|
||||||
|
AnimationCreator.CreateAnimation(bccadPrefab, bccad, prefabData, sprites);
|
||||||
|
|
||||||
|
PrefabUtility.SaveAsPrefabAsset(root, AssetDatabase.GetAssetPath(prefab));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplySpriteSettingsFromBccad(GameObject gameObjectPart, SpritePart spritePart,
|
||||||
|
BccadPrefab prefab, Sprite sprite, int index)
|
||||||
|
{
|
||||||
|
var spriteRenderer = (SpriteRenderer)gameObjectPart.AddComponent(typeof(SpriteRenderer));
|
||||||
|
|
||||||
|
spriteRenderer.sprite = sprite;
|
||||||
|
spriteRenderer.color = spritePart.GetColor();
|
||||||
|
spriteRenderer.flipX = spritePart.FlipX;
|
||||||
|
spriteRenderer.flipY = spritePart.FlipY;
|
||||||
|
spriteRenderer.enabled = true;
|
||||||
|
gameObjectPart.transform.SetParent(prefab.ParentObject.transform);
|
||||||
|
|
||||||
|
// Bread draws sprites from the edge, and unity from the middle.
|
||||||
|
var width = spritePart.StretchX / prefab.WidthRatio;
|
||||||
|
var height = spritePart.StretchY / prefab.HeightRatio;
|
||||||
|
// var pixelsPerUnit = 73;
|
||||||
|
|
||||||
|
var position =
|
||||||
|
new Vector3(
|
||||||
|
(spritePart.PosX - 512f) /
|
||||||
|
SpriteCreator.PixelsPerUnit + sprite.bounds.size.x * 0.5f * width,
|
||||||
|
-(spritePart.PosY - 512f) / SpriteCreator.PixelsPerUnit -
|
||||||
|
sprite.bounds.size.y * 0.5f * height,
|
||||||
|
-0.00001f * index);
|
||||||
|
var rotation = Quaternion.AngleAxis(spritePart.Rotation, new Vector3(0, 0, -1));
|
||||||
|
gameObjectPart.transform.localPosition = position;
|
||||||
|
gameObjectPart.transform.localRotation = rotation;
|
||||||
|
gameObjectPart.transform.localScale = new Vector3(width, height, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Editor/bread2unity/PrefabCreator.cs.meta
Normal file
11
Assets/Editor/bread2unity/PrefabCreator.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 59809bd6fc62dce4aab4eb333ffbdc64
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
18
Assets/Editor/bread2unity/PrefabData.cs
Normal file
18
Assets/Editor/bread2unity/PrefabData.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Bread2Unity;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public class PrefabData
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public List<Animation> Animations;
|
||||||
|
public int SpriteIndex;
|
||||||
|
|
||||||
|
public PrefabData(string name, int spriteIndex)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
SpriteIndex = spriteIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Editor/bread2unity/PrefabData.cs.meta
Normal file
11
Assets/Editor/bread2unity/PrefabData.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6807e41eb8dc0ae46b2e5b728af55e2d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
103
Assets/Editor/bread2unity/SpriteCreator.cs
Normal file
103
Assets/Editor/bread2unity/SpriteCreator.cs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
using Bread2Unity;
|
||||||
|
|
||||||
|
namespace Bread2Unity
|
||||||
|
{
|
||||||
|
public class SpriteCreator : MonoBehaviour
|
||||||
|
{
|
||||||
|
public const int PixelsPerUnit = 100;
|
||||||
|
public static Texture2D ComputeSprites(BCCAD bccad, string texturePath, string prefabName)
|
||||||
|
{
|
||||||
|
var textureName = Path.GetFileName(texturePath);
|
||||||
|
var spritesFolder =
|
||||||
|
$"Assets\\Resources\\Sprites\\Games\\{char.ToUpperInvariant(prefabName[0]) + prefabName.Substring(1)}\\";
|
||||||
|
if (!Directory.Exists(spritesFolder))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(spritesFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
var destTexturePath =
|
||||||
|
spritesFolder +
|
||||||
|
$"{textureName}";
|
||||||
|
var newTexture = new Texture2D(bccad.sheetW, bccad.sheetH);
|
||||||
|
newTexture.LoadImage(File.ReadAllBytes(texturePath));
|
||||||
|
var rotatedTexture = RotateTexture(newTexture);
|
||||||
|
rotatedTexture.name = textureName.Substring(0, textureName.Length - ".png".Length);
|
||||||
|
File.WriteAllBytes(destTexturePath, rotatedTexture.EncodeToPNG());
|
||||||
|
AssetDatabase.ImportAsset(destTexturePath);
|
||||||
|
var ti = AssetImporter.GetAtPath(destTexturePath) as TextureImporter;
|
||||||
|
|
||||||
|
if (ti != null)
|
||||||
|
{
|
||||||
|
ti.isReadable = true;
|
||||||
|
// constants
|
||||||
|
ti.textureType = TextureImporterType.Sprite;
|
||||||
|
ti.spriteImportMode = SpriteImportMode.Multiple;
|
||||||
|
ti.spritePixelsPerUnit = PixelsPerUnit;
|
||||||
|
ti.filterMode = FilterMode.Point;
|
||||||
|
ti.textureCompression = TextureImporterCompression.Uncompressed;
|
||||||
|
var newData = new List<SpriteMetaData>();
|
||||||
|
var rectCtr = 0;
|
||||||
|
var heightRatio = (float)rotatedTexture.height / bccad.sheetH;
|
||||||
|
var widthRatio = (float)rotatedTexture.width / bccad.sheetW;
|
||||||
|
foreach (var r in bccad.regions)
|
||||||
|
{
|
||||||
|
var smd = new SpriteMetaData
|
||||||
|
{
|
||||||
|
pivot = new Vector2(0.5f, 0.5f),
|
||||||
|
alignment = 0,
|
||||||
|
name = rotatedTexture.name + "_" + rectCtr,
|
||||||
|
rect = new Rect(r.regionX * widthRatio,
|
||||||
|
rotatedTexture.height - (r.regionH + r.regionY) * heightRatio, r.regionW * widthRatio,
|
||||||
|
r.regionH * heightRatio)
|
||||||
|
};
|
||||||
|
|
||||||
|
newData.Add(smd);
|
||||||
|
rectCtr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ti.spritesheet = newData.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetDatabase.ImportAsset(destTexturePath, ImportAssetOptions.ForceUpdate);
|
||||||
|
return rotatedTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Texture2D RotateTexture(Texture2D image)
|
||||||
|
{
|
||||||
|
Texture2D
|
||||||
|
target = new Texture2D(image.height, image.width, image.format,
|
||||||
|
false); //flip image width<>height, as we rotated the image, it might be a rect. not a square image
|
||||||
|
|
||||||
|
Color32[] pixels = image.GetPixels32(0);
|
||||||
|
pixels = RotateTextureGrid(pixels, image.width, image.height);
|
||||||
|
target.SetPixels32(pixels);
|
||||||
|
target.Apply();
|
||||||
|
|
||||||
|
//flip image width<>height, as we rotated the image, it might be a rect. not a square image
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Color32[] RotateTextureGrid(Color32[] tex, int wid, int hi)
|
||||||
|
{
|
||||||
|
Color32[] ret = new Color32[wid * hi]; //reminder we are flipping these in the target
|
||||||
|
|
||||||
|
for (int y = 0; y < hi; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < wid; x++)
|
||||||
|
{
|
||||||
|
ret[(hi - 1) - y + x * hi] = tex[x + y * wid]; //juggle the pixels around
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Editor/bread2unity/SpriteCreator.cs.meta
Normal file
11
Assets/Editor/bread2unity/SpriteCreator.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a4430af4dfb91f4f9720962a6eef059
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Loading…
Reference in a new issue