chore: update to spine 3.8.99
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import threading
|
||||
import shutil
|
||||
from lib.skeleton_binary import SkeletonBinary
|
||||
from lib.alpha_composite import AlphaComposite
|
||||
from lib.atlas_reader import AtlasReader
|
||||
from lib.base64_util import *
|
||||
@@ -63,7 +62,6 @@ class Builder:
|
||||
return
|
||||
|
||||
def __build_assets(self, operator_name):
|
||||
use_skel = self.config["operator"]["use_skel"]
|
||||
file_paths = dict(
|
||||
source=self.config["operator"]["source_folder"].format(name=operator_name),
|
||||
target=self.config["operator"]["target_folder"].format(name=operator_name),
|
||||
@@ -92,8 +90,7 @@ class Builder:
|
||||
target=self.__skeleton_binary_thread,
|
||||
args=(
|
||||
file_paths,
|
||||
data,
|
||||
use_skel
|
||||
data
|
||||
),
|
||||
daemon=True,
|
||||
),
|
||||
@@ -176,24 +173,23 @@ class Builder:
|
||||
thread.join()
|
||||
atlas_to_base64_thread.join()
|
||||
|
||||
def __skeleton_binary_thread(self, file_paths, data, use_skel):
|
||||
def __skeleton_binary_thread(self, file_paths, data):
|
||||
source_path = file_paths["source"]
|
||||
target_path = file_paths["target"]
|
||||
common_name = file_paths["common_name"]
|
||||
|
||||
SkeletonBinary(source_path + common_name, target_path + common_name, use_skel)
|
||||
if use_skel is True:
|
||||
self.__skel_to_base64(
|
||||
target_path + common_name + ".skel",
|
||||
data,
|
||||
self.config["server"]["operator_folder"] + common_name + ".skel",
|
||||
)
|
||||
else:
|
||||
self.__json_to_base64(
|
||||
target_path + common_name + ".json",
|
||||
data,
|
||||
self.config["server"]["operator_folder"] + common_name + ".json",
|
||||
)
|
||||
if common_name.strip().endswith(".skel") is False:
|
||||
common_name += ".skel"
|
||||
file_path = pathlib.Path.cwd().joinpath(source_path + common_name)
|
||||
save_path = pathlib.Path.cwd().joinpath(target_path + common_name)
|
||||
shutil.copyfile(
|
||||
file_path,
|
||||
save_path
|
||||
)
|
||||
self.__skel_to_base64(
|
||||
save_path,
|
||||
data,
|
||||
self.config["server"]["operator_folder"] + common_name,
|
||||
)
|
||||
|
||||
def __fallback_thread(self, file_paths, data):
|
||||
source_path = file_paths["source"]
|
||||
|
||||
@@ -21,7 +21,6 @@ class Config:
|
||||
"project.json": dict
|
||||
},
|
||||
operator=dict(
|
||||
use_skel=bool,
|
||||
preview=str,
|
||||
project_json=str,
|
||||
source_folder=str,
|
||||
|
||||
@@ -1,859 +0,0 @@
|
||||
import pathlib
|
||||
import json
|
||||
import shutil
|
||||
|
||||
TransformMode = [
|
||||
"normal",
|
||||
"onlyTranslation",
|
||||
"noRotationOrReflection",
|
||||
"noScale",
|
||||
"noScaleOrReflection"
|
||||
]
|
||||
|
||||
BlendMode = [
|
||||
"normal",
|
||||
"additive",
|
||||
"multiply",
|
||||
"screen"
|
||||
]
|
||||
|
||||
PositionMode = [
|
||||
"fixed",
|
||||
"percent"
|
||||
]
|
||||
|
||||
SpacingMode = [
|
||||
"length",
|
||||
"fixed",
|
||||
"percent"
|
||||
]
|
||||
|
||||
RotateMode = [
|
||||
"tangent",
|
||||
"chain",
|
||||
"chainScale"
|
||||
]
|
||||
|
||||
AttachmentType = [
|
||||
"region",
|
||||
"boundingbox",
|
||||
"mesh",
|
||||
"linkedmesh",
|
||||
"path",
|
||||
"point",
|
||||
"clipping"
|
||||
]
|
||||
|
||||
BONE_ROTATE = 0
|
||||
BONE_TRANSLATE = 1
|
||||
BONE_SCALE = 2
|
||||
BONE_SHEAR = 3
|
||||
|
||||
SLOT_ATTACHMENT = 0
|
||||
SLOT_COLOR = 1
|
||||
SLOT_TWO_COLOR = 2
|
||||
|
||||
PATH_POSITION = 0
|
||||
PATH_SPACING = 1
|
||||
PATH_MIX = 2
|
||||
|
||||
CURVE_LINEAR = 0
|
||||
CURVE_STEPPED = 1
|
||||
CURVE_BEZIER = 2
|
||||
|
||||
class SkeletonBinary:
|
||||
|
||||
def __init__(self, file_path, save_path, use_skel, scale=1):
|
||||
if file_path.strip().endswith(".skel") is False:
|
||||
file_path += ".skel"
|
||||
file_path = pathlib.Path.cwd().joinpath(file_path)
|
||||
if save_path.strip().endswith(".json") is False or save_path.strip().endswith(".skel") is False:
|
||||
if use_skel is True:
|
||||
save_path += ".skel"
|
||||
else:
|
||||
save_path += ".json"
|
||||
save_path = pathlib.Path.cwd().joinpath(save_path)
|
||||
|
||||
if use_skel is True:
|
||||
shutil.copyfile(
|
||||
file_path,
|
||||
save_path
|
||||
)
|
||||
return
|
||||
|
||||
self.index = 0
|
||||
self.scale = scale
|
||||
self.dict = dict()
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
self.binaryData = f.read()
|
||||
self.readSkeletonData()
|
||||
|
||||
with open(save_path, "w") as f:
|
||||
json.dump(self.dict, f)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def readSkeletonData(self):
|
||||
self.dict["skeleton"] = dict()
|
||||
self.dict["skeleton"]["hash"] = self.readString()
|
||||
if len(self.dict["skeleton"]["hash"]) == 0:
|
||||
self.dict["skeleton"]["hash"] = None
|
||||
self.dict["skeleton"]["spine"] = self.readString()
|
||||
if len(self.dict["skeleton"]["spine"]) == 0:
|
||||
self.dict["skeleton"]["spine"] = None
|
||||
self.dict["skeleton"]["width"] = self.readFloat()
|
||||
self.dict["skeleton"]["height"] = self.readFloat()
|
||||
|
||||
nonessential = self.readBoolean()
|
||||
|
||||
if nonessential is True:
|
||||
self.dict["skeleton"]["fps"] = self.readFloat()
|
||||
self.dict["skeleton"]["images"] = self.readString()
|
||||
if len(self.dict["skeleton"]["images"]) == 0:
|
||||
self.dict["skeleton"]["images"] = None
|
||||
|
||||
# Bones.
|
||||
self.dict["bones"] = list()
|
||||
for i in range(self.readInt(True)):
|
||||
name = self.readString()
|
||||
if (i == 0):
|
||||
parent = None
|
||||
else:
|
||||
parent = self.dict["bones"][self.readInt(True)]["name"]
|
||||
data = dict(
|
||||
name=name,
|
||||
parent=parent,
|
||||
rotation=self.readFloat(),
|
||||
x=self.readFloat() * self.scale,
|
||||
y=self.readFloat() * self.scale,
|
||||
scaleX=self.readFloat(),
|
||||
scaleY=self.readFloat(),
|
||||
shearX=self.readFloat(),
|
||||
shearY=self.readFloat(),
|
||||
length=self.readFloat() * self.scale,
|
||||
transform=TransformMode[self.readInt(True)]
|
||||
)
|
||||
if nonessential is True:
|
||||
data["color"] = self.rgba8888ToColor(self.readInt())
|
||||
self.dict["bones"].append(data)
|
||||
|
||||
# Slots.
|
||||
self.dict["slots"] = list()
|
||||
for i in range(self.readInt(True)):
|
||||
slotName = self.readString()
|
||||
boneData = self.dict["bones"][self.readInt(True)]["name"]
|
||||
data = dict(
|
||||
name=slotName,
|
||||
bone=boneData,
|
||||
color=self.rgba8888ToColor(self.readInt())[2:],
|
||||
)
|
||||
# not working for dyn_illust_char_1012_skadi2.skel
|
||||
# darkColor = self.readInt()
|
||||
# if (darkColor != -1):
|
||||
# data["dark"] = self.rgba888ToColor(darkColor)[2:]
|
||||
|
||||
data["attachment"] = self.readString()
|
||||
|
||||
data["blend"] = BlendMode[self.readInt(True)]
|
||||
self.dict["slots"].append(data)
|
||||
|
||||
# IK constraints.
|
||||
self.dict["ik"] = list()
|
||||
for i in range(self.readInt(True)):
|
||||
data = dict(
|
||||
name=self.readString(),
|
||||
order=self.readInt(True)
|
||||
)
|
||||
data["bones"] = list()
|
||||
for j in range(self.readInt(True)):
|
||||
data["bones"].append(self.dict["bones"][self.readInt(True)]["name"])
|
||||
data["target"] = self.dict["bones"][self.readInt(True)]["name"]
|
||||
data["mix"] = self.readFloat()
|
||||
data["bendPositive"] = self.readBoolean()
|
||||
self.dict["ik"].append(data)
|
||||
|
||||
# Transform constraints.
|
||||
self.dict["transform"] = list()
|
||||
for i in range(self.readInt(True)):
|
||||
data = dict(
|
||||
name=self.readString(),
|
||||
order=self.readInt(True)
|
||||
)
|
||||
data["bones"] = list()
|
||||
for j in range(self.readInt(True)):
|
||||
data["bones"].append(self.dict["bones"][self.readInt(True)]["name"])
|
||||
data["target"] = self.dict["bones"][self.readInt(True)]["name"]
|
||||
# not working for dyn_illust_char_1012_skadi2.skel
|
||||
# data["local"] = self.readBoolean()
|
||||
# data["relative"] = self.readBoolean()
|
||||
data["rotation"] = self.readFloat()
|
||||
data["x"] = self.readFloat() * self.scale
|
||||
data["y"] = self.readFloat() * self.scale
|
||||
data["scaleX"] = self.readFloat()
|
||||
data["scaleY"] = self.readFloat()
|
||||
data["shearY"] = self.readFloat()
|
||||
data["rotateMix"] = self.readFloat()
|
||||
data["translateMix"] = self.readFloat()
|
||||
data["scaleMix"] = self.readFloat()
|
||||
data["shearMix"] = self.readFloat()
|
||||
self.dict["transform"].append(data)
|
||||
|
||||
# Path constraints.
|
||||
self.dict["path"] = list()
|
||||
for i in range(self.readInt(True)):
|
||||
data = dict(
|
||||
name=self.readString(),
|
||||
order=self.readInt(True),
|
||||
bones=list()
|
||||
)
|
||||
for j in range(self.readInt(True)):
|
||||
data["bones"].append(self.dict["bones"][self.readInt(True)]["name"])
|
||||
data["target"] = self.dict["slots"][self.readInt(True)]["name"]
|
||||
data["positionMode"] = PositionMode[self.readInt(True)]
|
||||
data["spacingMode"] = SpacingMode[self.readInt(True)]
|
||||
data["rotateMode"] = RotateMode[self.readInt(True)]
|
||||
data["rotation"] = self.readFloat()
|
||||
data["position"] = self.readFloat()
|
||||
if data["positionMode"] == "fixed":
|
||||
data["position"] *= self.scale
|
||||
data["spacing"] = self.readFloat()
|
||||
if data["spacingMode"] == "length" or data["spacingMode"] == "fixed":
|
||||
data["spacing"] *= self.scale
|
||||
data["rotateMix"] = self.readFloat()
|
||||
data["translateMix"] = self.readFloat()
|
||||
self.dict["path"].append(data)
|
||||
|
||||
# Default skin.
|
||||
self.dict["skins"] = dict()
|
||||
self.dict["skinNames"] = list()
|
||||
defaultSkin = self.readSkin("default", nonessential)
|
||||
if defaultSkin is not None:
|
||||
self.dict["skins"]["default"] = defaultSkin
|
||||
self.dict["skinNames"].append("default")
|
||||
|
||||
# Skins.
|
||||
for i in range(self.readInt(True)):
|
||||
skinName = self.readString()
|
||||
self.dict["skins"][skinName] = self.readSkin(skinName, nonessential)
|
||||
self.dict["skinNames"].append(skinName)
|
||||
|
||||
# Events.
|
||||
self.dict["events"] = list()
|
||||
for i in range(self.readInt(True)):
|
||||
self.dict["events"].append(
|
||||
dict(
|
||||
name=self.readString(),
|
||||
int=self.readInt(False),
|
||||
float=self.readFloat(),
|
||||
string=self.readString()
|
||||
)
|
||||
)
|
||||
|
||||
# Animations.
|
||||
self.dict["animations"] = dict()
|
||||
for i in range(self.readInt(True)):
|
||||
animationName = self.readString()
|
||||
animation = self.readAnimation(animationName)
|
||||
self.dict["animations"][animationName] = animation
|
||||
|
||||
def readSkin(self, skinName, nonessential):
|
||||
skin = dict()
|
||||
slotCount = self.readInt(True)
|
||||
if slotCount == 0:
|
||||
return None
|
||||
for i in range(slotCount):
|
||||
slotIndex = self.readInt(True)
|
||||
slot = dict()
|
||||
for j in range(self.readInt(True)):
|
||||
name = self.readString()
|
||||
attachment = self.readAttachment(slotIndex, name, nonessential)
|
||||
if attachment is not None:
|
||||
slot[name] = attachment
|
||||
skin[self.dict["slots"][slotIndex]["name"]] = slot
|
||||
return skin
|
||||
|
||||
def readAttachment(self, slotIndex, attachmentName, nonessential):
|
||||
name = self.readString()
|
||||
if (name == None):
|
||||
name = attachmentName
|
||||
type = AttachmentType[self.read()]
|
||||
if type == "region":
|
||||
path = self.readString()
|
||||
rotation = self.readFloat()
|
||||
x = self.readFloat()
|
||||
y = self.readFloat()
|
||||
scaleX = self.readFloat()
|
||||
scaleY = self.readFloat()
|
||||
width = self.readFloat()
|
||||
height = self.readFloat()
|
||||
color = self.rgba8888ToColor(self.readInt())[2:]
|
||||
if path == None:
|
||||
path = name
|
||||
return dict(
|
||||
path=path,
|
||||
x=x,
|
||||
y=y,
|
||||
scaleX=scaleX,
|
||||
scaleY=scaleY,
|
||||
rotation=rotation,
|
||||
width=width,
|
||||
height=height,
|
||||
color=color,
|
||||
name=name,
|
||||
type=type
|
||||
)
|
||||
elif type == "boundingbox":
|
||||
vertexCount = self.readInt(True)
|
||||
vertices = self.readVertices(vertexCount)
|
||||
if nonessential is True:
|
||||
color = self.rgba8888ToColor(self.readInt())[2:]
|
||||
else:
|
||||
color = "00000000"
|
||||
return dict(
|
||||
vertexCount=vertexCount,
|
||||
vertices=vertices,
|
||||
color=color,
|
||||
name=name,
|
||||
type=type
|
||||
)
|
||||
elif type == "mesh":
|
||||
path = self.readString()
|
||||
color = self.rgba8888ToColor(self.readInt())[2:]
|
||||
vertexCount = self.readInt(True)
|
||||
uvs = self.readFloatArray(vertexCount << 1, 1)
|
||||
triangles = self.readShortArray()
|
||||
vertices = self.readVertices(vertexCount)
|
||||
hull = self.readInt(True)
|
||||
edges = None
|
||||
width = 0
|
||||
height = 0
|
||||
if nonessential is True:
|
||||
edges = self.readShortArray()
|
||||
width = self.readFloat()
|
||||
height = self.readFloat()
|
||||
if path == None:
|
||||
path = name
|
||||
return dict(
|
||||
path=path,
|
||||
color=color,
|
||||
width=width,
|
||||
height=height,
|
||||
uvs=uvs,
|
||||
triangles=triangles,
|
||||
hull=hull,
|
||||
edges=edges,
|
||||
vertices=vertices,
|
||||
name=name,
|
||||
type=type
|
||||
)
|
||||
elif type == "linkedmesh":
|
||||
path = self.readString()
|
||||
color = self.rgba8888ToColor(self.readInt())[2:]
|
||||
skin = self.readString()
|
||||
parent = self.readString()
|
||||
deform = self.readBoolean()
|
||||
width = 0
|
||||
height = 0
|
||||
if nonessential is True:
|
||||
width = self.readFloat()
|
||||
height = self.readFloat()
|
||||
if path == None:
|
||||
path = name
|
||||
return dict(
|
||||
path=path,
|
||||
color=color,
|
||||
skin=skin,
|
||||
parent=parent,
|
||||
deform=deform,
|
||||
width=width,
|
||||
height=height,
|
||||
name=name,
|
||||
type=type
|
||||
)
|
||||
elif type == "path":
|
||||
closed = self.readBoolean()
|
||||
constantSpeed = self.readBoolean()
|
||||
vertexCount = self.readInt(True)
|
||||
vertices = self.readVertices(vertexCount)
|
||||
lengths = [0] * int(vertexCount / 3)
|
||||
for i in range(len(lengths)):
|
||||
lengths[i] = self.readFloat() * self.scale
|
||||
if nonessential is True:
|
||||
color = self.rgba8888ToColor(self.readInt())[2:]
|
||||
else:
|
||||
color = "00000000"
|
||||
return dict(
|
||||
closed=closed,
|
||||
constantSpeed=constantSpeed,
|
||||
vertexCount=vertexCount,
|
||||
vertices=vertices,
|
||||
lengths=lengths,
|
||||
color=color,
|
||||
name=name,
|
||||
type=type
|
||||
)
|
||||
elif type == "point":
|
||||
rotation = self.readFloat()
|
||||
x = self.readFloat()
|
||||
y = self.readFloat()
|
||||
if nonessential is True:
|
||||
color = self.rgba8888ToColor(self.readInt())[2:]
|
||||
else:
|
||||
color = "00000000"
|
||||
return dict(
|
||||
rotation=rotation,
|
||||
x=x,
|
||||
y=y,
|
||||
color=color,
|
||||
name=name,
|
||||
type=type
|
||||
)
|
||||
elif type == "clipping":
|
||||
end = self.readInt(True)
|
||||
vertexCount = self.readInt(True)
|
||||
vertices = self.readVertices(vertexCount)
|
||||
if nonessential is True:
|
||||
color = self.rgba8888ToColor(self.readInt())[2:]
|
||||
else:
|
||||
color = "00000000"
|
||||
return dict(
|
||||
end=end,
|
||||
vertexCount=vertexCount,
|
||||
vertices=vertices,
|
||||
color=color,
|
||||
name=name,
|
||||
type=type
|
||||
)
|
||||
return None
|
||||
|
||||
def readVertices(self, vertexCount):
|
||||
verticesLength = vertexCount << 1
|
||||
if self.readBoolean() is False:
|
||||
return self.readFloatArray(verticesLength, self.scale)
|
||||
bonesArray = []
|
||||
for i in range(vertexCount):
|
||||
boneCount = self.readInt(True)
|
||||
bonesArray.append(boneCount)
|
||||
for j in range(boneCount):
|
||||
bonesArray.append(self.readInt(True))
|
||||
bonesArray.append(self.readFloat() * self.scale)
|
||||
bonesArray.append(self.readFloat() * self.scale)
|
||||
bonesArray.append(self.readFloat())
|
||||
return bonesArray
|
||||
|
||||
def readAnimation(self, name):
|
||||
animation = dict()
|
||||
duration = 0
|
||||
# Slot timelines.
|
||||
slots = dict()
|
||||
for i in range(self.readInt(True)):
|
||||
slotIndex = self.readInt(True)
|
||||
slotMap = dict()
|
||||
for j in range(self.readInt(True)):
|
||||
timelineType = self.read()
|
||||
frameCount = self.readInt(True)
|
||||
timeline = [None] * frameCount
|
||||
if timelineType == SLOT_ATTACHMENT:
|
||||
for frameIndex in range(frameCount):
|
||||
e = dict()
|
||||
e["time"] = self.readFloat()
|
||||
e["name"] = self.readString()
|
||||
timeline[frameIndex] = e
|
||||
slotMap["attachment"] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
elif timelineType == SLOT_COLOR:
|
||||
for frameIndex in range(frameCount):
|
||||
e = dict()
|
||||
e["time"] = self.readFloat()
|
||||
e["color"] = self.rgba8888ToColor(self.readInt())[2:]
|
||||
timeline[frameIndex] = e
|
||||
if (frameIndex < frameCount - 1):
|
||||
self.readCurve(frameIndex, timeline)
|
||||
slotMap["color"] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
elif timelineType == SLOT_TWO_COLOR:
|
||||
for frameIndex in range(frameCount):
|
||||
e = dict()
|
||||
e["time"] = self.readFloat()
|
||||
e["light"] = self.rgba8888ToColor(self.readInt())[2:]
|
||||
e["dark"] = self.rgba888ToColor(self.readInt)[2:]
|
||||
timeline[frameIndex] = e
|
||||
if (frameIndex < frameCount - 1):
|
||||
self.readCurve(frameIndex, timeline)
|
||||
slotMap["twoColor"] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
slots[self.dict["slots"][slotIndex]["name"]] = slotMap
|
||||
animation["slots"] = slots
|
||||
|
||||
# Bone timelines.
|
||||
bones = dict()
|
||||
for i in range(self.readInt(True)):
|
||||
boneIndex = self.readInt(True)
|
||||
boneMap = dict()
|
||||
for j in range(self.readInt(True)):
|
||||
timelineType = self.read()
|
||||
frameCount = self.readInt(True)
|
||||
timeline = [None] * frameCount
|
||||
if timelineType == BONE_ROTATE:
|
||||
for frameIndex in range(frameCount):
|
||||
e = dict()
|
||||
e["time"] = self.readFloat()
|
||||
e["angle"] = self.readFloat()
|
||||
timeline[frameIndex] = e
|
||||
if (frameIndex < frameCount - 1):
|
||||
self.readCurve(frameIndex, timeline)
|
||||
boneMap["rotate"] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
elif timelineType == BONE_TRANSLATE or timelineType == BONE_SCALE or timelineType == BONE_SHEAR:
|
||||
timelineScale = 1
|
||||
if timelineType == BONE_TRANSLATE:
|
||||
timelineScale = self.scale
|
||||
for frameIndex in range(frameCount):
|
||||
e = dict()
|
||||
e["time"] = self.readFloat()
|
||||
e["x"] = self.readFloat() * timelineScale
|
||||
e["y"] = self.readFloat() * timelineScale
|
||||
timeline[frameIndex] = e
|
||||
if (frameIndex < frameCount - 1):
|
||||
self.readCurve(frameIndex, timeline)
|
||||
if timelineType == BONE_TRANSLATE:
|
||||
boneMap["translate"] = timeline
|
||||
elif timelineType == BONE_SCALE:
|
||||
boneMap["scale"] = timeline
|
||||
else:
|
||||
boneMap["shear"] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
bones[self.dict["bones"][boneIndex]["name"]] = boneMap
|
||||
animation["bones"] = bones
|
||||
|
||||
# IK constraint timelines.
|
||||
iks = dict()
|
||||
a = self.readInt(True)
|
||||
for i in range(a):
|
||||
ikIndex = self.readInt(True)
|
||||
frameCount = self.readInt(True)
|
||||
timeline = [None] * frameCount
|
||||
for frameIndex in range(frameCount):
|
||||
e = dict()
|
||||
e["time"] = self.readFloat()
|
||||
e["mix"] = self.readFloat()
|
||||
e["bendPositive"] = self.readBoolean()
|
||||
timeline[frameIndex] = e
|
||||
if (frameIndex < frameCount - 1):
|
||||
self.readCurve(frameIndex, timeline)
|
||||
iks[self.dict["ik"][ikIndex]["name"]] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
animation["ik"] = iks
|
||||
|
||||
# Transform constraint timelines.
|
||||
transforms = dict()
|
||||
for i in range(self.readInt(True)):
|
||||
transformIndex = self.readInt(True)
|
||||
frameCount = self.readInt(True)
|
||||
timeline = [None] * frameCount
|
||||
for frameIndex in range(frameCount):
|
||||
e = dict()
|
||||
e["time"] = self.readFloat()
|
||||
e["rotateMix"] = self.readFloat()
|
||||
e["translateMix"] = self.readFloat()
|
||||
e["scaleMix"] = self.readFloat()
|
||||
e["shearMix"] = self.readFloat()
|
||||
timeline[frameIndex] = e
|
||||
if (frameIndex < frameCount - 1):
|
||||
self.readCurve(frameIndex, timeline)
|
||||
transforms[self.dict["transform"][transformIndex]["name"]] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
animation["transform"] = transforms
|
||||
|
||||
# Path constraint timelines.
|
||||
paths = dict()
|
||||
for i in range(self.readInt(True)):
|
||||
pathIndex = self.readInt(True)
|
||||
data = self.dict["path"][pathIndex]
|
||||
pathMap = dict()
|
||||
for j in range(self.readInt(True)):
|
||||
timelineType = self.read()
|
||||
frameCount = self.readInt(True)
|
||||
timeline = [None] * frameCount
|
||||
if timelineType == PATH_POSITION or timelineType == PATH_SPACING:
|
||||
timelineScale = 1
|
||||
if timelineType == PATH_SPACING:
|
||||
if data["spacingMode"] == "length" or data["spacingMode"] == "fixed":
|
||||
timelineScale = self.scale
|
||||
else:
|
||||
if data["positionMode"] == "fixed":
|
||||
timelineScale = self.scale
|
||||
for frameIndex in range(frameCount):
|
||||
e = dict()
|
||||
e["time"] = self.readFloat()
|
||||
e["position"] = self.readFloat() * timelineScale
|
||||
timeline[frameIndex] = e
|
||||
if (frameIndex < frameCount - 1):
|
||||
self.readCurve(frameIndex, timeline)
|
||||
if timelineType == PATH_POSITION:
|
||||
pathMap["position"] = timeline
|
||||
else:
|
||||
pathMap["spacing"] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
elif timelineType == PATH_MIX:
|
||||
for frameIndex in range(frameCount):
|
||||
timeline[frameIndex]["time"] = self.readFloat()
|
||||
timeline[frameIndex]["rotateMix"] = self.readFloat()
|
||||
timeline[frameIndex]["translateMix"] = self.readFloat()
|
||||
self.readCurve(frameIndex, timeline)
|
||||
pathMap["mix"] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
paths[self.dict["path"][pathIndex]["name"]] = pathMap
|
||||
animation["paths"] = paths
|
||||
|
||||
# Deform timelines.
|
||||
deformDict = dict()
|
||||
for i in range(self.readInt(True)):
|
||||
skinName = self.dict["skinNames"][self.readInt(True)]
|
||||
skin = self.dict["skins"][skinName]
|
||||
if skin == None:
|
||||
raise LookupError("Skin not found")
|
||||
deformMap = dict()
|
||||
for j in range(self.readInt(True)):
|
||||
slotIndex = self.readInt(True)
|
||||
slot = self.dict["slots"][slotIndex]
|
||||
for k in range(self.readInt(True)):
|
||||
attachmentName = self.readString()
|
||||
attachment = dict()
|
||||
|
||||
frameCount = self.readInt(True)
|
||||
timeline = [None] * frameCount
|
||||
for frameIndex in range(frameCount):
|
||||
time = self.readFloat()
|
||||
end = self.readInt(True)
|
||||
e = dict()
|
||||
e["time"] = time
|
||||
if end != 0:
|
||||
deform = []
|
||||
start = self.readInt(True)
|
||||
end += start
|
||||
if (self.scale == 1):
|
||||
for v in range(start, end):
|
||||
deform.append(self.readFloat())
|
||||
else:
|
||||
for v in range(start, end):
|
||||
deform.append(self.readFloat() * self.scale)
|
||||
e["vertices"] = deform
|
||||
e["offset"] = start
|
||||
timeline[frameIndex] = e
|
||||
if frameIndex < frameCount - 1:
|
||||
self.readCurve(frameIndex, timeline)
|
||||
attachment[attachmentName] = timeline
|
||||
duration = max(duration, timeline[frameCount - 1]["time"])
|
||||
deformMap[slot["name"]] = attachment
|
||||
deformDict[skinName] = deformMap
|
||||
animation["deform"] = deformDict
|
||||
|
||||
# Draw order timeline.
|
||||
drawOrderCount = self.readInt(True)
|
||||
if (drawOrderCount > 0):
|
||||
slotCount = len(self.dict["slots"])
|
||||
drawOrders = [None] * drawOrderCount
|
||||
for i in range(drawOrderCount):
|
||||
drawOrderMap = dict()
|
||||
time = self.readFloat()
|
||||
offsetCount = self.readInt(True)
|
||||
offsets = [None] * offsetCount
|
||||
for j in range(offsetCount):
|
||||
slotIndex = self.readInt(True)
|
||||
e = dict()
|
||||
e["slot"] = self.dict["slots"][slotIndex]["name"]
|
||||
e["offset"] = self.readInt(True)
|
||||
offsets[j] = e
|
||||
drawOrderMap["time"] = time
|
||||
drawOrderMap["offsets"] = offsets
|
||||
drawOrders[i] = drawOrderMap
|
||||
animation["drawOrder"] = drawOrders
|
||||
duration = max(duration, drawOrders[drawOrderCount - 1]["time"])
|
||||
|
||||
# Event timeline.
|
||||
eventCount = self.readInt(True)
|
||||
if (eventCount > 0):
|
||||
timeline = [None] * eventCount
|
||||
for i in range(eventCount):
|
||||
time = self.readFloat()
|
||||
eventData = self.dict["events"][self.readInt(True)]
|
||||
event = dict(
|
||||
int=self.readInt(False),
|
||||
name=eventData["name"],
|
||||
float=self.readFloat(),
|
||||
time=time
|
||||
)
|
||||
if self.readBoolean() is True:
|
||||
event["string"] = self.readString()
|
||||
else:
|
||||
event["string"] = eventData["string"]
|
||||
timeline[i] = event
|
||||
animation["events"] = timeline
|
||||
duration = max(duration, timeline[eventCount - 1]["time"])
|
||||
return animation
|
||||
|
||||
def readCurve(self, frameIndex, timeline):
|
||||
case = self.read()
|
||||
if case == CURVE_LINEAR:
|
||||
timeline[frameIndex]["curve"] = "linear"
|
||||
elif case == CURVE_STEPPED:
|
||||
timeline[frameIndex]["curve"] = "stepped"
|
||||
elif case == CURVE_BEZIER:
|
||||
cx1 = self.readFloat()
|
||||
cy1 = self.readFloat()
|
||||
cx2 = self.readFloat()
|
||||
cy2 = self.readFloat()
|
||||
timeline[frameIndex]["curve"] = [cx1, cy1, cx2, cy2]
|
||||
|
||||
def readInt(self, optimizePositive=None) -> int:
|
||||
# java native input.readInt()
|
||||
if optimizePositive is None:
|
||||
ch1 = self.read()
|
||||
ch2 = self.read()
|
||||
ch3 = self.read()
|
||||
ch4 = self.read()
|
||||
if ((ch1 | ch2 | ch3 | ch4) < 0):
|
||||
raise ValueError("((ch1 | ch2 | ch3 | ch4) is < 0")
|
||||
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0))
|
||||
b = self.read()
|
||||
result = b & 0x7F;
|
||||
if ((b & 0x80) != 0):
|
||||
b = self.read()
|
||||
result |= (b & 0x7F) << 7
|
||||
if ((b & 0x80) != 0):
|
||||
b = self.read()
|
||||
result |= (b & 0x7F) << 14
|
||||
if ((b & 0x80) != 0):
|
||||
b = self.read()
|
||||
result |= (b & 0x7F) << 21
|
||||
if ((b & 0x80) != 0):
|
||||
b = self.read()
|
||||
result |= (b & 0x7F) << 28
|
||||
if result > 0xFFFFFFFF:
|
||||
raise OverflowError("not an int32")
|
||||
if result > 0x7FFFFFFF:
|
||||
result = int(0x100000000 - result)
|
||||
if result < 2147483648:
|
||||
result = -result
|
||||
else:
|
||||
result = -2147483648
|
||||
if optimizePositive is True:
|
||||
return result
|
||||
else:
|
||||
return ((result >> 1) ^ -(result & 1))
|
||||
|
||||
def readString(self):
|
||||
chars = [None] * 32
|
||||
byteCount = self.readInt(True)
|
||||
if byteCount == 0:
|
||||
return None
|
||||
elif byteCount == 1:
|
||||
return ""
|
||||
byteCount -= 1
|
||||
if len(chars) < byteCount:
|
||||
chars = [None] * byteCount
|
||||
charCount = 0
|
||||
i = 0
|
||||
while (i < byteCount):
|
||||
b = self.read()
|
||||
shiftedB = b >> 4
|
||||
if shiftedB == -1:
|
||||
# 0b11110000 -> 0b11111111 ?
|
||||
raise ValueError("shiftedB is -1")
|
||||
elif shiftedB == 12 or shiftedB == 13:
|
||||
chars[charCount] = chr((b & 0x1F) << 6 | self.read() & 0x3F)
|
||||
charCount += 1
|
||||
i += 2
|
||||
elif shiftedB == 14:
|
||||
chars[charCount] = chr((b & 0x0F) << 12 | (self.read() & 0x3F) << 6 | self.read() & 0x3F)
|
||||
charCount += 1
|
||||
i += 3
|
||||
else:
|
||||
chars[charCount] = chr(b)
|
||||
charCount += 1
|
||||
i += 1
|
||||
string = ""
|
||||
for c in chars:
|
||||
if c is not None:
|
||||
string += c
|
||||
return string
|
||||
|
||||
def readFloat(self):
|
||||
"""
|
||||
IEEE 754
|
||||
"""
|
||||
exponent_len = 8
|
||||
mantissa_len = 23
|
||||
bits = (self.read() << 24) | (self.read() << 16) | (self.read() << 8) | self.read()
|
||||
sign_raw = (bits & 0x80000000) >> (exponent_len + mantissa_len)
|
||||
exponent_raw = (bits & 0x7f800000) >> mantissa_len
|
||||
mantissa_raw = bits & 0x007fffff
|
||||
|
||||
if sign_raw == 1:
|
||||
sign = -1
|
||||
else:
|
||||
sign = 1
|
||||
|
||||
if exponent_raw == 2 ** exponent_len - 1:
|
||||
if mantissa_raw == 2 ** mantissa_len - 1:
|
||||
return float('nan')
|
||||
|
||||
return sign * float('inf') # Inf
|
||||
|
||||
exponent = exponent_raw - (2 ** (exponent_len - 1) - 1)
|
||||
|
||||
if exponent_raw == 0:
|
||||
mantissa = 0
|
||||
else:
|
||||
mantissa = 1
|
||||
|
||||
for b in range(mantissa_len - 1, -1, -1):
|
||||
if mantissa_raw & (2 ** b):
|
||||
mantissa += 1 / (2 ** (mantissa_len - b))
|
||||
|
||||
return sign * (2 ** exponent) * mantissa
|
||||
|
||||
def readBoolean(self):
|
||||
b = self.read()
|
||||
if b < 0:
|
||||
raise ValueError("b is < 0")
|
||||
return b != 0
|
||||
|
||||
def rgba8888ToColor(self, value):
|
||||
return hex(value)
|
||||
|
||||
def rgba888ToColor(self, value):
|
||||
return hex(value & 0xffffff)
|
||||
|
||||
def readShort(self):
|
||||
ch1 = self.read()
|
||||
ch2 = self.read()
|
||||
if ((ch1 | ch2) < 0):
|
||||
raise ValueError("(ch1 | ch2) < 0")
|
||||
return ((ch1 << 8) + (ch2 << 0))
|
||||
|
||||
def readFloatArray(self, n, scale):
|
||||
array = [0.0] * n
|
||||
# ????
|
||||
if scale == 1:
|
||||
for i in range(n):
|
||||
array[i] = self.readFloat()
|
||||
else:
|
||||
for i in range(n):
|
||||
array[i] = self.readFloat() * scale
|
||||
return array
|
||||
|
||||
def readShortArray(self):
|
||||
n = self.readInt(True)
|
||||
array = [0] * n
|
||||
for i in range(n):
|
||||
array[i] = self.readShort()
|
||||
return array
|
||||
|
||||
def read(self) -> int:
|
||||
result = self.binaryData[self.index]
|
||||
self.index += 1
|
||||
return result
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import os
|
||||
import pathlib
|
||||
import json
|
||||
with open(os.path.join(pathlib.Path(__file__).parent.absolute(), "operator", "skadi", "dyn_illust_char_1012_skadi2.json"), "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
with open(os.path.join(pathlib.Path(__file__).parent.absolute(), "dyn_illust_char_1012_skadi2[sorted].json"), "w") as f:
|
||||
json.dump(data, f, indent=4, sort_keys=True)
|
||||
Reference in New Issue
Block a user