feat: init repo
This commit is contained in:
32
lib/alpha_composite.py
Normal file
32
lib/alpha_composite.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import pathlib
|
||||
from PIL import Image
|
||||
|
||||
class AlphaComposite:
|
||||
|
||||
def __init__(self, image_path: str, save_path) -> None:
|
||||
if image_path.strip().endswith(".png") is True:
|
||||
image_path = image_path.replace(".png", "")
|
||||
if save_path.strip().endswith(".png") is False:
|
||||
save_path += ".png"
|
||||
self.image_path = pathlib.Path.cwd().joinpath(image_path + ".png")
|
||||
self.mask_path = pathlib.Path.cwd().joinpath(image_path + "[alpha].png")
|
||||
self.save_path = save_path
|
||||
self.image = None
|
||||
self.mask = None
|
||||
self.loaded_image = None
|
||||
self.loaded_mask = None
|
||||
self.__open_image()
|
||||
self.__alpha_composite()
|
||||
pass
|
||||
|
||||
def __open_image(self):
|
||||
self.image = Image.open(self.image_path)
|
||||
self.mask = Image.open(self.mask_path)
|
||||
self.loaded_image = self.image.load()
|
||||
self.loaded_mask = self.mask.load()
|
||||
|
||||
def __alpha_composite(self):
|
||||
for y in range(self.mask.size[1]):
|
||||
for x in range(self.mask.size[0]):
|
||||
self.loaded_image[x, y] = (self.loaded_image[x, y][0], self.loaded_image[x, y][1], self.loaded_image[x, y][2], self.loaded_mask[x, y][2])
|
||||
self.image.save(pathlib.Path.cwd().joinpath(self.save_path))
|
||||
30
lib/atlas_reader.py
Normal file
30
lib/atlas_reader.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import pathlib
|
||||
import shutil
|
||||
|
||||
class AtlasReader:
|
||||
|
||||
def __init__(self, file_path: str, save_path: str) -> None:
|
||||
if file_path.strip().endswith(".atlas") is False:
|
||||
file_path += ".atlas"
|
||||
if save_path.strip().endswith(".atlas") is False:
|
||||
save_path += ".atlas"
|
||||
|
||||
self.file_path = pathlib.Path.cwd().joinpath(file_path)
|
||||
self.save_path = pathlib.Path.cwd().joinpath(save_path)
|
||||
self.images = list()
|
||||
|
||||
def get_images(self):
|
||||
with open(self.file_path, "r") as f:
|
||||
line = f.readline()
|
||||
while line:
|
||||
line = line.strip()
|
||||
if line.endswith(".png") is True:
|
||||
self.images.append(line)
|
||||
line = f.readline()
|
||||
self.copy()
|
||||
|
||||
def copy(self):
|
||||
shutil.copyfile(
|
||||
self.file_path,
|
||||
self.save_path
|
||||
)
|
||||
46
lib/builder.py
Normal file
46
lib/builder.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import threading
|
||||
|
||||
from lib.skeleton_binary import SkeletonBinary
|
||||
from lib.alpha_composite import AlphaComposite
|
||||
from lib.atlas_reader import AtlasReader
|
||||
|
||||
class Builder:
|
||||
|
||||
def __init__(self, operator_name, config) -> None:
|
||||
self.operator_name = operator_name
|
||||
self.config = config
|
||||
|
||||
def start(self):
|
||||
self.__set_version()
|
||||
return
|
||||
|
||||
def stop(self):
|
||||
return
|
||||
|
||||
def __build_assets(self):
|
||||
source = "./operator/skadi/extracted/"
|
||||
target = "./operator/skadi/"
|
||||
name = "dyn_illust_char_1012_skadi2"
|
||||
ar = AtlasReader(source + name, target + name)
|
||||
skeleton_binary = threading.Thread(
|
||||
target=SkeletonBinary,
|
||||
args=(source + name, target + name)
|
||||
)
|
||||
ar_thread = threading.Thread(
|
||||
target=ar.get_images,
|
||||
args=()
|
||||
)
|
||||
ar_thread.start()
|
||||
skeleton_binary.start()
|
||||
ar_thread.join()
|
||||
for item in ar.images:
|
||||
threading.Thread(
|
||||
target=AlphaComposite,
|
||||
args=(source + item, target + item)
|
||||
).start()
|
||||
return
|
||||
|
||||
def __set_version(self):
|
||||
version = input("Enter build version: ")
|
||||
print(version)
|
||||
return
|
||||
65
lib/config.py
Normal file
65
lib/config.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import pathlib, yaml
|
||||
|
||||
class Config:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.config_path = pathlib.Path.cwd().joinpath("config.yaml")
|
||||
self.__read_config()
|
||||
|
||||
def __read_config(self):
|
||||
try:
|
||||
self.config = yaml.safe_load(open(self.config_path, "r"))
|
||||
except Exception as e:
|
||||
raise
|
||||
else:
|
||||
self.__validate_config()
|
||||
|
||||
def __validate_config(self):
|
||||
config_keys = dict(
|
||||
server=dict,
|
||||
operators=dict
|
||||
)
|
||||
self.__config_check("config", self.config, config_keys)
|
||||
|
||||
server_keys = dict(
|
||||
template_folder=str,
|
||||
release_folder=str,
|
||||
js_access_folder=str,
|
||||
css_access_folder=str,
|
||||
asset_access_folder=str,
|
||||
version_file=str
|
||||
)
|
||||
self.__config_check("server", self.config["server"], server_keys)
|
||||
|
||||
operators_keys = dict(
|
||||
source_folder=str,
|
||||
target_folder=str,
|
||||
common_name=str
|
||||
)
|
||||
for operator_name, operator_content in self.config["operators"].items():
|
||||
self.__config_check(operator_name, operator_content, operators_keys)
|
||||
|
||||
with open(self.config_path, 'w') as f:
|
||||
yaml.safe_dump(self.config, f, allow_unicode=True)
|
||||
|
||||
def __config_check(self, block_name: str, contents: dict, required_keys: dict):
|
||||
checklist = [key for key in required_keys]
|
||||
if contents is not None:
|
||||
for key, value in contents.items():
|
||||
value_type = type(value)
|
||||
if key in checklist:
|
||||
required_type = required_keys[key]
|
||||
else:
|
||||
break
|
||||
if value_type != required_type:
|
||||
raise TypeError("Item {key} in config.yaml is not set up correctly. Type {value_type} is detected, but type {required_type} is required.".format(key=key, value_type=value_type.__name__, required_type=required_type.__name__))
|
||||
else:
|
||||
checklist.remove(key)
|
||||
if key.endswith("_file") is True:
|
||||
if value.endswith("/") is True:
|
||||
contents[key] = value[:len(value) - 1]
|
||||
elif key.endswith("_folder"):
|
||||
if value.endswith("/") is False:
|
||||
contents[key] = value + "/"
|
||||
if len(checklist) != 0:
|
||||
raise LookupError("config.yaml has missing item(s) for Block {block_name}: {items}".format(block_name=block_name, items=', '.join([key for key in checklist])))
|
||||
12
lib/server.py
Normal file
12
lib/server.py
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
class Server:
|
||||
|
||||
def __init__(self, port, config) -> None:
|
||||
self.port = port
|
||||
self.config = config
|
||||
|
||||
def start(self):
|
||||
return
|
||||
|
||||
def stop(self):
|
||||
return
|
||||
845
lib/skeleton_binary.py
Normal file
845
lib/skeleton_binary.py
Normal file
@@ -0,0 +1,845 @@
|
||||
import pathlib
|
||||
import json
|
||||
|
||||
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, scale=1) -> None:
|
||||
if file_path.strip().endswith(".skel") is False:
|
||||
file_path += ".skel"
|
||||
if save_path.strip().endswith(".json") is False:
|
||||
save_path += ".json"
|
||||
|
||||
self.index = 0
|
||||
self.scale = scale
|
||||
self.dict = dict()
|
||||
try:
|
||||
with open(pathlib.Path.cwd().joinpath(file_path), "rb") as f:
|
||||
self.binaryData = f.read()
|
||||
|
||||
self.readSkeletonData()
|
||||
|
||||
with open(pathlib.Path.cwd().joinpath(save_path), "w") as f:
|
||||
json.dump(self.dict, f, indent=2, sort_keys=True)
|
||||
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)
|
||||
)
|
||||
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["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.0] * 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
|
||||
# needs a if statement here to decode dyn_illust_char_1012_skadi2.skel
|
||||
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.readByte()
|
||||
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
|
||||
for i in range(byteCount):
|
||||
b = self.read()
|
||||
shiftedB = b >> 4
|
||||
if shiftedB == -1:
|
||||
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
|
||||
|
||||
8
lib/sort_json.py
Normal file
8
lib/sort_json.py
Normal file
@@ -0,0 +1,8 @@
|
||||
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