AnimX importer done
Finished the importer for now, it may contain errors but those would be part of the library
This commit is contained in:
@@ -502,56 +502,7 @@ def transfer_vertex_weights(context: Context, obj: bpy.types.Object, source_grou
|
|||||||
|
|
||||||
#Binary tools
|
#Binary tools
|
||||||
|
|
||||||
import ctypes
|
|
||||||
def ReadCSharp_str(data: BytesIO) -> str:
|
|
||||||
return data.read(read7bitEncoded_int(data)).decode('utf-16-le')
|
|
||||||
|
|
||||||
def WriteCSharp_str(data: BytesIO, string: str) -> str:
|
|
||||||
write7bitEncoded_int(len(string)*2)
|
|
||||||
return data.write(string.encode("utf-16-le"))
|
|
||||||
|
|
||||||
def read7bitEncoded_ulong(data: BytesIO) -> np.int64:
|
|
||||||
num: ctypes.c_uint = ctypes.c_uint(0)
|
|
||||||
num2: int = 0
|
|
||||||
flag: bool = True
|
|
||||||
|
|
||||||
while (flag):
|
|
||||||
b: ctypes.c_ubyte = ctypes.c_ubyte(struct.unpack('<B', data.read(1))[0])
|
|
||||||
flag = ((b & 128) > 0)
|
|
||||||
num |= ((b & 127) << num2)
|
|
||||||
num2 += 7
|
|
||||||
if not flag:
|
|
||||||
break
|
|
||||||
|
|
||||||
return num
|
|
||||||
|
|
||||||
def read7bitEncoded_int(data: BytesIO) -> ctypes.c_int:
|
|
||||||
num: ctypes.c_int = ctypes.c_int(0)
|
|
||||||
num2:ctypes.c_int = ctypes.c_int(0)
|
|
||||||
while (num2 != 35):
|
|
||||||
b: ctypes.c_ubyte = ctypes.c_ubyte(struct.unpack('<B', data.read(1))[0])
|
|
||||||
num |= int(b & 127) << num2
|
|
||||||
num2 += 7
|
|
||||||
if ((b & 128) == 0):
|
|
||||||
return num
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def write7bitEncoded_ulong(data: BytesIO, integer: ctypes.c_ulong) -> None:
|
|
||||||
while integer > ctypes.c_ulong(0):
|
|
||||||
b: ctypes.c_ubyte = ctypes.c_ubyte(integer & ctypes.c_ulong(127))
|
|
||||||
integer >>= 7
|
|
||||||
if integer > ctypes.c_ulong(0):
|
|
||||||
b |= 128
|
|
||||||
data.write(b)
|
|
||||||
if integer <= ctypes.c_ulong(0):
|
|
||||||
return
|
|
||||||
|
|
||||||
def write7bitEncoded_int(data: BytesIO, value: ctypes.c_int) -> None:
|
|
||||||
num: ctypes.c_uint = ctypes.c_uint(value)
|
|
||||||
while(num >= ctypes.c_ubyte(128)):
|
|
||||||
data.write(ctypes.c_ubyte(num | ctypes.c_ubyte(128)))
|
|
||||||
num >>= 7
|
|
||||||
data.Write(ctypes.c_ubyte(num))
|
|
||||||
|
|
||||||
|
|
||||||
#encoding FrooxEngine/C# types in binary:
|
#encoding FrooxEngine/C# types in binary:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"language": 0,
|
"language": 0,
|
||||||
"last_update_check": 1734208835.8049936
|
"last_update_check": 1734295375.2681296
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,19 @@ import typing
|
|||||||
import struct
|
import struct
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
def writeNullable(data: BytesIO, value: = None):
|
||||||
|
|
||||||
|
data.write(struct.pack("?", value == None))
|
||||||
|
if(value == None):
|
||||||
|
return
|
||||||
|
data.write()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def ReadCSharp_str(data: BytesIO) -> str:
|
def ReadCSharp_str(data: BytesIO) -> str:
|
||||||
charamount = read7bitEncoded_int(data)
|
charamount = read7bitEncoded_int(data)
|
||||||
string: str = data.read(charamount).decode('utf-8', errors="replace")
|
string: str = data.read(charamount).decode('utf-8', errors="replace")
|
||||||
print("read string: "+string)
|
#print("read string: "+string)
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def WriteCSharp_str(data: BytesIO, string: str) -> str:
|
def WriteCSharp_str(data: BytesIO, string: str) -> str:
|
||||||
@@ -41,7 +50,7 @@ def read7bitEncoded_int(data: BytesIO) -> int:
|
|||||||
|
|
||||||
def write7bitEncoded_ulong(data: BytesIO, integer: int) -> None:
|
def write7bitEncoded_ulong(data: BytesIO, integer: int) -> None:
|
||||||
while integer > int(0):
|
while integer > int(0):
|
||||||
b: int = ctypes.c_ubyte(integer & int(127))
|
b: int = int(integer & int(127))
|
||||||
integer >>= 7
|
integer >>= 7
|
||||||
if integer > int(0):
|
if integer > int(0):
|
||||||
b |= 128
|
b |= 128
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from os import replace
|
from os import replace
|
||||||
|
from re import S
|
||||||
from types import FrameType
|
from types import FrameType
|
||||||
|
|
||||||
import lz4.block
|
import lz4.block
|
||||||
@@ -10,6 +11,7 @@ import typing
|
|||||||
import struct
|
import struct
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
|
||||||
KeyframeInterpolation: dict[str, int] = {
|
KeyframeInterpolation: dict[str, int] = {
|
||||||
"Hold": 1,
|
"Hold": 1,
|
||||||
"Linear": 2,
|
"Linear": 2,
|
||||||
@@ -18,28 +20,17 @@ KeyframeInterpolation: dict[str, int] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class KeyFrame():
|
class KeyFrame():
|
||||||
time: resonite_types.float = resonite_types.float(0)
|
time: resonite_types.float
|
||||||
interpolation: resonite_types.int = resonite_types.int(0)
|
interpolation: resonite_types.byte
|
||||||
value: resonite_types.ResoType
|
value: resonite_types.ResoType
|
||||||
left_tan: resonite_types.ResoType
|
left_tan: resonite_types.ResoType
|
||||||
right_tan: resonite_types.ResoType
|
right_tan: resonite_types.ResoType
|
||||||
|
|
||||||
|
|
||||||
def __getattr__(self, name: str):
|
|
||||||
if name == "interpolation":
|
|
||||||
interp: int = 0
|
|
||||||
if (self["left_tan"] != None and self["right_tan"] != None):
|
|
||||||
interp = 3
|
|
||||||
|
|
||||||
|
|
||||||
return resonite_types.int(interp)
|
|
||||||
return super().__getattribute__(name)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
self.time = resonite_types.float(0)
|
||||||
|
self.interpolation = resonite_types.byte(0)
|
||||||
|
|
||||||
|
|
||||||
def RequiresTangents(self) -> bool:
|
def RequiresTangents(self) -> bool:
|
||||||
@@ -48,14 +39,17 @@ class KeyFrame():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
class ResoTrack(resonite_types.ResoType):
|
class ResoTrack(resonite_types.ResoType):
|
||||||
node: resonite_types.string = resonite_types.string("")
|
node: resonite_types.string
|
||||||
property: resonite_types.string = resonite_types.string("")
|
property: resonite_types.string
|
||||||
Owner: AnimX
|
Owner: AnimX
|
||||||
FrameType: type[resonite_types.ResoType]
|
FrameType: str
|
||||||
keyframes: list[KeyFrame] = []
|
keyframes: list[KeyFrame]
|
||||||
|
|
||||||
def __init__(self,FrameType):
|
def __init__(self,FrameType):
|
||||||
self.FrameType = FrameType
|
self.FrameType = FrameType
|
||||||
|
self.keyframes = []
|
||||||
|
self.node = resonite_types.string("")
|
||||||
|
self.property = resonite_types.string("")
|
||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
self.node.write(data)
|
self.node.write(data)
|
||||||
@@ -65,10 +59,12 @@ class ResoTrack(resonite_types.ResoType):
|
|||||||
def read(self, data:BytesIO):
|
def read(self, data:BytesIO):
|
||||||
self.node.read(data)
|
self.node.read(data)
|
||||||
self.property.read(data)
|
self.property.read(data)
|
||||||
|
|
||||||
track_amount: int = int(common.read7bitEncoded_ulong(data))
|
track_amount: int = int(common.read7bitEncoded_ulong(data))
|
||||||
|
#print(track_amount)
|
||||||
for i in range(0, track_amount):
|
for i in range(0, track_amount):
|
||||||
key: KeyFrame = KeyFrame()
|
key: KeyFrame = KeyFrame()
|
||||||
key.value = self.FrameType()
|
key.value = eval(self.FrameType+"()")
|
||||||
self.keyframes.append(key)
|
self.keyframes.append(key)
|
||||||
|
|
||||||
def removeKeyframe(self, time: float | int) -> bool:
|
def removeKeyframe(self, time: float | int) -> bool:
|
||||||
@@ -143,20 +139,24 @@ class ResoTrack(resonite_types.ResoType):
|
|||||||
|
|
||||||
|
|
||||||
class RawTrack(ResoTrack):
|
class RawTrack(ResoTrack):
|
||||||
interval: resonite_types.float = resonite_types.float(0)
|
interval: resonite_types.float
|
||||||
|
|
||||||
def __getattr__(self, name: str):
|
def __getattr__(self, name: str):
|
||||||
if name == "interval":
|
if name == "interval":
|
||||||
return self.Owner.interval.x
|
return self.Owner.interval
|
||||||
return super().__getattribute__(name)
|
return super().__getattribute__(name)
|
||||||
|
|
||||||
def __init__(self, FrameType):
|
def __init__(self, FrameType):
|
||||||
super().__init__(FrameType)
|
super().__init__(FrameType)
|
||||||
|
self.interval = resonite_types.float(0)
|
||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
super().write(data)
|
super().write(data)
|
||||||
self.interval.write(data)
|
self.interval.write(data)
|
||||||
for key in self.keyframes:
|
for key in self.keyframes:
|
||||||
|
if self.FrameType == "resonite_types.string":
|
||||||
|
resonite_types.writeNullable(data, key.value)
|
||||||
|
else:
|
||||||
key.value.write(data)
|
key.value.write(data)
|
||||||
|
|
||||||
|
|
||||||
@@ -164,6 +164,9 @@ class RawTrack(ResoTrack):
|
|||||||
super().read(data)
|
super().read(data)
|
||||||
self.interval.read(data)
|
self.interval.read(data)
|
||||||
for key in self.keyframes:
|
for key in self.keyframes:
|
||||||
|
if self.FrameType == "resonite_types.string":
|
||||||
|
resonite_types.readNullable(data, key.value)
|
||||||
|
else:
|
||||||
key.value.read(data)
|
key.value.read(data)
|
||||||
|
|
||||||
def addKeyframe(self, keyframe: KeyFrame) -> int:
|
def addKeyframe(self, keyframe: KeyFrame) -> int:
|
||||||
@@ -187,10 +190,28 @@ class DiscreteTrack(ResoTrack):
|
|||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
super().write(data)
|
super().write(data)
|
||||||
|
self.interval.write(data)
|
||||||
|
for key in self.keyframes:
|
||||||
|
if key.value == None:
|
||||||
|
key.value = eval(self.FrameType+"()")
|
||||||
|
if self.FrameType == "resonite_types.string":
|
||||||
|
resonite_types.writeNullable(data, key.value)
|
||||||
|
else:
|
||||||
|
key.value.write(data)
|
||||||
|
key.time.write(data)
|
||||||
|
|
||||||
|
|
||||||
def read(self, data:BytesIO):
|
def read(self, data:BytesIO):
|
||||||
super().read(data)
|
super().read(data)
|
||||||
|
self.interval.read(data)
|
||||||
|
for key in self.keyframes:
|
||||||
|
if key.value == None:
|
||||||
|
key.value = eval(self.FrameType+"()")
|
||||||
|
if self.FrameType == "resonite_types.string":
|
||||||
|
resonite_types.readNullable(data, key.value)
|
||||||
|
else:
|
||||||
|
key.value.read(data)
|
||||||
|
key.time.read(data)
|
||||||
|
|
||||||
def addKeyframe(self, keyframe: KeyFrame) -> int:
|
def addKeyframe(self, keyframe: KeyFrame) -> int:
|
||||||
num: int = super().addKeyframe(keyframe)
|
num: int = super().addKeyframe(keyframe)
|
||||||
@@ -203,23 +224,27 @@ class DiscreteTrack(ResoTrack):
|
|||||||
|
|
||||||
|
|
||||||
class CurveTrack(ResoTrack):
|
class CurveTrack(ResoTrack):
|
||||||
interpolations: bool = False
|
interpolations: bool
|
||||||
tangents: bool = False
|
tangents: bool
|
||||||
sharedinterpolation: resonite_types.int = resonite_types.int(-1)
|
sharedinterpolation: resonite_types.byte
|
||||||
|
|
||||||
def __getattr__(self, name: str):
|
def __getattr__(self, name: str):
|
||||||
if name == "interpolations":
|
if name == "interpolations":
|
||||||
|
integerframe: int = self.keyframes[0].interpolation.x
|
||||||
for key in self.keyframes:
|
for key in self.keyframes:
|
||||||
if key.interpolation.x != self.sharedinterpolation.x:
|
if key.interpolation.x != integerframe:
|
||||||
return True
|
return True
|
||||||
elif name == "tangents":
|
elif name == "tangents":
|
||||||
for key in self.keyframes:
|
for key in self.keyframes:
|
||||||
if key.interpolation.x == 3 or key.interpolation.x == 4:
|
if key.RequiresTangents():
|
||||||
return True
|
return True
|
||||||
return super().__getattribute__(name)
|
return super().__getattribute__(name)
|
||||||
|
|
||||||
def __init__(self, FrameType):
|
def __init__(self, FrameType):
|
||||||
super().__init__(FrameType)
|
super().__init__(FrameType)
|
||||||
|
self.sharedinterpolation = resonite_types.byte(-1)
|
||||||
|
self.interpolations = False
|
||||||
|
self.tangents = False
|
||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
super().write(data)
|
super().write(data)
|
||||||
@@ -234,43 +259,55 @@ class CurveTrack(ResoTrack):
|
|||||||
|
|
||||||
for key in self.keyframes:
|
for key in self.keyframes:
|
||||||
if key.value == None:
|
if key.value == None:
|
||||||
key.value = self.FrameType()
|
key.value = eval(self.FrameType+"()")
|
||||||
|
if self.FrameType == "resonite_types.string":
|
||||||
|
resonite_types.writeNullable(data, key.value)
|
||||||
|
else:
|
||||||
key.value.write(data)
|
key.value.write(data)
|
||||||
key.time.write(data)
|
key.time.write(data)
|
||||||
|
|
||||||
if(self.tangents):
|
if(self.tangents):
|
||||||
for key in self.keyframes:
|
for key in self.keyframes:
|
||||||
|
if self.FrameType == "resonite_types.string":
|
||||||
|
resonite_types.writeNullable(data, key.left_tan)
|
||||||
|
resonite_types.writeNullable(data, key.right_tan)
|
||||||
|
else:
|
||||||
key.left_tan.write(data)
|
key.left_tan.write(data)
|
||||||
key.right_tan.write(data)
|
key.right_tan.write(data)
|
||||||
|
|
||||||
def read(self, data:BytesIO):
|
def read(self, data:BytesIO):
|
||||||
super().read(data)
|
super().read(data)
|
||||||
flags: int = struct.unpack("<B",data.read(1))[0]
|
flags: int = struct.unpack("<B",data.read(1))[0]
|
||||||
self.interpolations = (flags & 1) > 0
|
interp: bool = (flags & 1) > 0
|
||||||
self.tangents = (flags & 2) > 0
|
tan: bool = (flags & 2) > 0
|
||||||
|
|
||||||
print(str(self.interpolations))
|
#print(str(interp))
|
||||||
print(str(self.tangents))
|
#print(str(tan))
|
||||||
|
#print(flags)
|
||||||
|
#print(len(self.keyframes))
|
||||||
|
|
||||||
if(self.interpolations):
|
if(interp):
|
||||||
for key in self.keyframes:
|
for key in self.keyframes:
|
||||||
|
#print("reading interp")
|
||||||
key.interpolation.read(data)
|
key.interpolation.read(data)
|
||||||
else:
|
else:
|
||||||
self.sharedinterpolation.read(data)
|
self.sharedinterpolation.read(data)
|
||||||
|
|
||||||
for key in self.keyframes:
|
for key in self.keyframes:
|
||||||
print("key read!")
|
|
||||||
if key.value == None:
|
if key.value == None:
|
||||||
key.value = self.FrameType()
|
key.value = eval(self.FrameType+"()")
|
||||||
|
if self.FrameType == "resonite_types.string":
|
||||||
|
resonite_types.readNullable(data, key.value)
|
||||||
|
else:
|
||||||
key.value.read(data)
|
key.value.read(data)
|
||||||
key.time.read(data)
|
key.time.read(data)
|
||||||
|
|
||||||
if(self.tangents):
|
if(tan):
|
||||||
for key in self.keyframes:
|
for key in self.keyframes:
|
||||||
if key.left_tan == None:
|
if self.FrameType == "resonite_types.string":
|
||||||
key.left_tan = self.FrameType()
|
resonite_types.readNullable(data, key.left_tan)
|
||||||
if key.right_tan == None:
|
resonite_types.readNullable(data, key.right_tan)
|
||||||
key.right_tan = self.FrameType()
|
else:
|
||||||
key.left_tan.read(data)
|
key.left_tan.read(data)
|
||||||
key.right_tan.read(data)
|
key.right_tan.read(data)
|
||||||
|
|
||||||
@@ -313,56 +350,56 @@ class BezierTrack(ResoTrack):
|
|||||||
"""PLACE HOLDER METHOD, DO NOT USE"""
|
"""PLACE HOLDER METHOD, DO NOT USE"""
|
||||||
raise Exception("BezierTrack track type is unsupported in resonite's code")
|
raise Exception("BezierTrack track type is unsupported in resonite's code")
|
||||||
#This is weird, but thank you python - @989onan
|
#This is weird, but thank you python - @989onan
|
||||||
TrackTypes: list[type] = [
|
TrackTypes: list[str] = [
|
||||||
RawTrack,
|
"RawTrack",
|
||||||
DiscreteTrack,
|
"DiscreteTrack",
|
||||||
CurveTrack,
|
"CurveTrack",
|
||||||
BezierTrack
|
"BezierTrack"
|
||||||
]
|
]
|
||||||
|
|
||||||
#TODO: add all types here
|
#TODO: add all types here
|
||||||
#wooooo - @989onan
|
#wooooo - @989onan
|
||||||
elementTypes: list[type[resonite_types.ResoType]] = [
|
elementTypes: list[str] = [
|
||||||
resonite_types.bool,
|
"resonite_types.bool",
|
||||||
resonite_types.bool2,
|
"resonite_types.bool2",
|
||||||
resonite_types.bool3,
|
"resonite_types.bool3",
|
||||||
resonite_types.bool4,
|
"resonite_types.bool4",
|
||||||
resonite_types.byte,
|
"resonite_types.byte",
|
||||||
resonite_types.ushort,
|
"resonite_types.ushort",
|
||||||
resonite_types.uint,
|
"resonite_types.uint",
|
||||||
resonite_types.ulong,
|
"resonite_types.ulong",
|
||||||
resonite_types.sbyte,
|
"resonite_types.sbyte",
|
||||||
resonite_types.short,
|
"resonite_types.short",
|
||||||
resonite_types.int,
|
"resonite_types.int",
|
||||||
resonite_types.long,
|
"resonite_types.long",
|
||||||
resonite_types.int2,
|
"resonite_types.int2",
|
||||||
resonite_types.int3,
|
"resonite_types.int3",
|
||||||
resonite_types.int4,
|
"resonite_types.int4",
|
||||||
resonite_types.uint2,
|
"resonite_types.uint2",
|
||||||
resonite_types.uint3,
|
"resonite_types.uint3",
|
||||||
resonite_types.uint4,
|
"resonite_types.uint4",
|
||||||
resonite_types.long2,
|
"resonite_types.long2",
|
||||||
resonite_types.long3,
|
"resonite_types.long3",
|
||||||
resonite_types.long4,
|
"resonite_types.long4",
|
||||||
resonite_types.float,
|
"resonite_types.float",
|
||||||
resonite_types.float2,
|
"resonite_types.float2",
|
||||||
resonite_types.float3,
|
"resonite_types.float3",
|
||||||
resonite_types.float4,
|
"resonite_types.float4",
|
||||||
resonite_types.floatQ,
|
"resonite_types.floatQ",
|
||||||
resonite_types.float2x2,
|
"resonite_types.float2x2",
|
||||||
resonite_types.float3x3,
|
"resonite_types.float3x3",
|
||||||
resonite_types.float4x4,
|
"resonite_types.float4x4",
|
||||||
resonite_types.double,
|
"resonite_types.double",
|
||||||
resonite_types.double2,
|
"resonite_types.double2",
|
||||||
resonite_types.double3,
|
"resonite_types.double3",
|
||||||
resonite_types.double4,
|
"resonite_types.double4",
|
||||||
resonite_types.doubleQ,
|
"resonite_types.doubleQ",
|
||||||
resonite_types.double2x2,
|
"resonite_types.double2x2",
|
||||||
resonite_types.double3x3,
|
"resonite_types.double3x3",
|
||||||
resonite_types.double4x4,
|
"resonite_types.double4x4",
|
||||||
resonite_types.color,
|
"resonite_types.color",
|
||||||
resonite_types.color32,
|
"resonite_types.color32",
|
||||||
resonite_types.string
|
"resonite_types.string"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -372,20 +409,27 @@ class AnimX():
|
|||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
To use Raw Track properly, please set interval (seconds between frames) after reading/creating.\n
|
To use Raw Track properly, please set interval (seconds between frames) after creating.\n
|
||||||
Represents data to be written to or read from an AnimX file.
|
Represents data to be written to or read from an AnimX file.\n
|
||||||
|
default interval to use would be 30.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
file_version: resonite_types.int = resonite_types.int()
|
file_version: resonite_types.int
|
||||||
track_amount: resonite_types.int = resonite_types.int()
|
track_amount: resonite_types.int
|
||||||
global_duration: resonite_types.float = resonite_types.float()
|
global_duration: resonite_types.float
|
||||||
name: resonite_types.string = resonite_types.string()
|
name: resonite_types.string
|
||||||
|
|
||||||
tracks: list[ResoTrack] = []
|
tracks: list[ResoTrack]
|
||||||
|
|
||||||
interval: resonite_types.float = resonite_types.float(1/25) #default value
|
interval: resonite_types.float
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.tracks = []
|
||||||
|
self.file_version = resonite_types.int()
|
||||||
|
self.track_amount = resonite_types.int()
|
||||||
|
self.global_duration = resonite_types.float()
|
||||||
|
self.name = resonite_types.string()
|
||||||
|
self.interval = resonite_types.float(1/25) #default value
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -424,7 +468,7 @@ class AnimX():
|
|||||||
|
|
||||||
self.track_amount.x = common.read7bitEncoded_ulong(data)
|
self.track_amount.x = common.read7bitEncoded_ulong(data)
|
||||||
self.global_duration.read(data)
|
self.global_duration.read(data)
|
||||||
print("track amont: "+str(self.track_amount.x))
|
print("track amount: "+str(self.track_amount.x))
|
||||||
print("file vers: "+str(self.file_version.x))
|
print("file vers: "+str(self.file_version.x))
|
||||||
|
|
||||||
self.name.read(data)
|
self.name.read(data)
|
||||||
@@ -453,8 +497,8 @@ class AnimX():
|
|||||||
data.read(8) #fuck off stream headers - @989onan
|
data.read(8) #fuck off stream headers - @989onan
|
||||||
data.read(8) #fuck off stream headers - @989onan
|
data.read(8) #fuck off stream headers - @989onan
|
||||||
filelmza: bytes = bytes(AnimX.decompress_lzma(data.read(), lzma.FORMAT_RAW, filters))
|
filelmza: bytes = bytes(AnimX.decompress_lzma(data.read(), lzma.FORMAT_RAW, filters))
|
||||||
|
#print("binary below:")
|
||||||
print(f"decompressed bytes: {filelmza[:100]}")
|
#print("b'{}'".format(''.join('\\x{:02x}'.format(b) for b in filelmza[:100])))
|
||||||
data = BytesIO(filelmza)
|
data = BytesIO(filelmza)
|
||||||
case _:
|
case _:
|
||||||
raise Exception("Invalid encoding")
|
raise Exception("Invalid encoding")
|
||||||
@@ -515,10 +559,10 @@ class AnimX():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def GetTrackType(cls, trackType2: int, value_type: type[resonite_types.ResoType], data: BytesIO) -> ResoTrack:
|
def GetTrackType(cls, trackType2: int, value_type: str, data: BytesIO) -> ResoTrack:
|
||||||
Track = TrackTypes[trackType2](value_type)
|
Track: ResoTrack = eval(TrackTypes[trackType2]+"(value_type)")
|
||||||
print(type(Track))
|
#print(value_type)
|
||||||
print(value_type)
|
#print(type(Track))
|
||||||
Track.read(data)
|
Track.read(data)
|
||||||
return Track
|
return Track
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,19 @@ class ResoType():
|
|||||||
def read(cls, data: BytesIO):
|
def read(cls, data: BytesIO):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def writeNullable(data: BytesIO, value: ResoType = None):
|
||||||
|
|
||||||
|
data.write(struct.pack("?", value == None))
|
||||||
|
if(value == None):
|
||||||
|
return
|
||||||
|
value.write(data)
|
||||||
|
|
||||||
|
def readNullable(data: BytesIO, value: ResoType = None):
|
||||||
|
|
||||||
|
hasval: bool = struct.unpack("?", data.read(1))
|
||||||
|
if not hasval:
|
||||||
|
return
|
||||||
|
value.read(data)
|
||||||
#These below are collection of the basic resonite typing made from C#. This is in order to store data in a sane way and decode/encode it.
|
#These below are collection of the basic resonite typing made from C#. This is in order to store data in a sane way and decode/encode it.
|
||||||
|
|
||||||
class color(ResoType):
|
class color(ResoType):
|
||||||
@@ -75,8 +88,8 @@ class byte(ResoType):
|
|||||||
def __int__(self):
|
def __int__(self):
|
||||||
return self.x
|
return self.x
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self,value=0):
|
||||||
pass
|
self.x = value
|
||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
data.write(struct.pack("<B", self.x))
|
data.write(struct.pack("<B", self.x))
|
||||||
@@ -120,8 +133,8 @@ class short(ResoType):
|
|||||||
def __int__(self):
|
def __int__(self):
|
||||||
return self.x
|
return self.x
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self,value=0):
|
||||||
pass
|
self.x = value
|
||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
data.write(struct.pack("<h", self.x))
|
data.write(struct.pack("<h", self.x))
|
||||||
@@ -264,7 +277,7 @@ class int4(int3):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def read(self,data: BytesIO):
|
def read(self,data: BytesIO):
|
||||||
super().write(data)
|
super().read(data)
|
||||||
self.w = struct.unpack("<i", data.read(4))[0]
|
self.w = struct.unpack("<i", data.read(4))[0]
|
||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
@@ -298,7 +311,7 @@ class uint2(uint):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def read(self,data: BytesIO):
|
def read(self,data: BytesIO):
|
||||||
super().write(data)
|
super().read(data)
|
||||||
self.y = struct.unpack("<I", data.read(4))[0]
|
self.y = struct.unpack("<I", data.read(4))[0]
|
||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
@@ -312,7 +325,7 @@ class uint3(uint2):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def read(self,data: BytesIO):
|
def read(self,data: BytesIO):
|
||||||
super().write(data)
|
super().read(data)
|
||||||
self.z = struct.unpack("<I", data.read(4))[0]
|
self.z = struct.unpack("<I", data.read(4))[0]
|
||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
@@ -326,7 +339,7 @@ class uint4(uint3):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def read(self,data: BytesIO):
|
def read(self,data: BytesIO):
|
||||||
super().write(data)
|
super().read(data)
|
||||||
self.w = struct.unpack("<I", data.read(4))[0]
|
self.w = struct.unpack("<I", data.read(4))[0]
|
||||||
|
|
||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
@@ -529,8 +542,7 @@ class doubleQ(double4):
|
|||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
super().write(data)
|
super().write(data)
|
||||||
|
|
||||||
@classmethod
|
def read(self, data: BytesIO):
|
||||||
def read(cls, data: BytesIO):
|
|
||||||
super().read(data)
|
super().read(data)
|
||||||
|
|
||||||
|
|
||||||
@@ -649,6 +661,5 @@ class floatQ(float4):
|
|||||||
def write(self, data: BytesIO):
|
def write(self, data: BytesIO):
|
||||||
super().write(data)
|
super().write(data)
|
||||||
|
|
||||||
@classmethod
|
def read(self, data: BytesIO):
|
||||||
def read(cls, data: BytesIO):
|
|
||||||
super().read(data)
|
super().read(data)
|
||||||
+94
-34
@@ -1,5 +1,7 @@
|
|||||||
|
from types import FrameType
|
||||||
import bpy
|
import bpy
|
||||||
import bpy_extras
|
import bpy_extras
|
||||||
|
from numpy import double
|
||||||
|
|
||||||
from .common import get_armature, get_selected_armature, simplify_bonename, is_valid_armature
|
from .common import get_armature, get_selected_armature, simplify_bonename, is_valid_armature
|
||||||
from bpy.types import Object, ShapeKey, Mesh, Context, Operator
|
from bpy.types import Object, ShapeKey, Mesh, Context, Operator
|
||||||
@@ -149,6 +151,14 @@ class AvatarToolKit_OT_ConvertToResonite(Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
def makeorexistingfcurve(action: bpy.types.Action,data_path: str,action_group: str, index=0) -> bpy.types.FCurve:
|
||||||
|
fcurve = action.fcurves.find(data_path=data_path,index=index)
|
||||||
|
if fcurve == None:
|
||||||
|
return action.fcurves.new(data_path,action_group=action_group,index=index)
|
||||||
|
else:
|
||||||
|
print("fcurve with data \""+data_path+"\" already exists")
|
||||||
|
return fcurve
|
||||||
|
|
||||||
@register_wrap
|
@register_wrap
|
||||||
class AvatarToolKit_OT_AnimX_Importer(Operator,bpy_extras.io_utils.ImportHelper):
|
class AvatarToolKit_OT_AnimX_Importer(Operator,bpy_extras.io_utils.ImportHelper):
|
||||||
bl_idname = 'avatar_toolkit.animx_importer'
|
bl_idname = 'avatar_toolkit.animx_importer'
|
||||||
@@ -169,7 +179,7 @@ class AvatarToolKit_OT_AnimX_Importer(Operator,bpy_extras.io_utils.ImportHelper)
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context: Context) -> bool:
|
def poll(cls, context: Context) -> bool:
|
||||||
return True
|
return context.active_object != None
|
||||||
|
|
||||||
def execute(self, context: Context) -> set:
|
def execute(self, context: Context) -> set:
|
||||||
|
|
||||||
@@ -177,62 +187,112 @@ class AvatarToolKit_OT_AnimX_Importer(Operator,bpy_extras.io_utils.ImportHelper)
|
|||||||
|
|
||||||
#decoding using self contained library:
|
#decoding using self contained library:
|
||||||
files = [file.name for file in self.files]
|
files = [file.name for file in self.files]
|
||||||
files.append(self.filepath)
|
#files.append(self.filepath)
|
||||||
for file in files:
|
for file in files:
|
||||||
froox_animation: resonite_animx.AnimX = resonite_animx.AnimX()
|
froox_animation: resonite_animx.AnimX = resonite_animx.AnimX()
|
||||||
froox_animation.interval.x = 1/25
|
froox_animation.interval.x = 30 #should be default fps
|
||||||
froox_animation.read(file = os.path.join(self.directory,file))
|
froox_animation.read(file = os.path.join(self.directory,file))
|
||||||
Froox_animations.append(froox_animation)
|
Froox_animations.append(froox_animation)
|
||||||
|
|
||||||
|
#TODO: Allow multiple targets and setting animations to each one somehow with an interface.
|
||||||
|
target: bpy.types.Object = context.active_object
|
||||||
|
if target.animation_data == None:
|
||||||
|
target.animation_data_create()
|
||||||
|
|
||||||
#Load data into Blender Animations.
|
#Load data into Blender Animations.
|
||||||
for froox_animation in Froox_animations:
|
for froox_animation in Froox_animations:
|
||||||
action: bpy.types.Action = bpy.data.actions.new(froox_animation.name.x)
|
action: bpy.types.Action = bpy.data.actions.new(froox_animation.name.x)
|
||||||
|
target.animation_data.action = action
|
||||||
action.use_fake_user = True
|
action.use_fake_user = True
|
||||||
for track in froox_animation.tracks:
|
for track in froox_animation.tracks:
|
||||||
print("hit here1")
|
data_path: str
|
||||||
print(track.FrameType)
|
actualproperty: str = track.property.x
|
||||||
if(track.FrameType != type(resonite_types.float) and track.FrameType != type(resonite_types.double)):
|
|
||||||
|
match(actualproperty):
|
||||||
|
case("Position"):
|
||||||
|
actualproperty = "location"
|
||||||
|
case("Rotation"):
|
||||||
|
actualproperty = "rotation_quaternion"
|
||||||
|
case("Scale"):
|
||||||
|
actualproperty = "scale"
|
||||||
|
data_path = actualproperty
|
||||||
|
|
||||||
|
if target.type == "ARMATURE":
|
||||||
|
data_path = "pose.bones[\""+track.node.x+"\"]."+data_path
|
||||||
|
|
||||||
|
for posebone in target.pose.bones:
|
||||||
|
posebone.rotation_mode = "QUATERNION"
|
||||||
|
|
||||||
|
print("reading frames for "+data_path)
|
||||||
|
if(track.FrameType == "resonite_types.double" or track.FrameType == "resonite_types.double"):
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=0),".x")
|
||||||
|
elif (track.FrameType == "resonite_types.float3" or track.FrameType == "resonite_types.double3"):
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=0),".x")
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=2),".y")
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=1),".z")
|
||||||
|
elif (track.FrameType == "resonite_types.float4" or track.FrameType == "resonite_types.double4"):
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=0),".x")
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=1),".y")
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=2),".z")
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=3),".w")
|
||||||
|
elif (track.FrameType == "resonite_types.doubleQ" or track.FrameType == "resonite_types.floatQ"):
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=3),".w")
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=0),".x")
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=2),".y")
|
||||||
|
self.readTrackData(track,makeorexistingfcurve(action=action,data_path=data_path,action_group=track.node.x,index=1),".z")
|
||||||
|
else:
|
||||||
continue
|
continue
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
data_path: str = track._node.x+"."+track._property.x
|
def readTrackData(self,track: resonite_animx.ResoTrack, fcurve_reso: bpy.types.FCurve, valuetype: str = ""):
|
||||||
fcurve_reso = action.fcurves.new(data_path,None,track._node.x)
|
tracktype = type(track)
|
||||||
|
match(tracktype):
|
||||||
print("hit here2")
|
case (resonite_animx.RawTrack):
|
||||||
match(type(track)):
|
|
||||||
case (type(resonite_animx.RawTrack)):
|
|
||||||
rawtrack: resonite_animx.RawTrack = track
|
rawtrack: resonite_animx.RawTrack = track
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for frame in rawtrack.keyframes:
|
fcurve_reso.keyframe_points.add(count=len(rawtrack.keyframes))
|
||||||
key: bpy.types.Keyframe = fcurve_reso.keyframe_points.insert(frame.time, float(frame.value))
|
# populate points
|
||||||
|
fcurve_reso.keyframe_points.foreach_set("co", [x for co in zip([frame.time.x*track.Owner.interval.x for frame in rawtrack.keyframes], [eval("frame.value"+valuetype) for frame in rawtrack.keyframes]) for x in co])
|
||||||
|
fcurve_reso.update()
|
||||||
|
|
||||||
|
case (resonite_animx.DiscreteTrack):
|
||||||
|
discretetrack: resonite_animx.DiscreteTrack = track
|
||||||
|
|
||||||
case (type(resonite_animx.DiscreteTrack)):
|
fcurve_reso.keyframe_points.add(count=len(discretetrack.keyframes))
|
||||||
discretetrack: resonite_animx.RawTrack = track
|
# populate points
|
||||||
for frame in rawtrack.keyframes:
|
fcurve_reso.keyframe_points.foreach_set("co", [x for co in zip([frame.time.x*track.Owner.interval.x for frame in discretetrack.keyframes], [eval("frame.value"+valuetype) for frame in discretetrack.keyframes]) for x in co])
|
||||||
key: bpy.types.Keyframe = fcurve_reso.keyframe_points.insert(frame.time, float(frame.value))
|
fcurve_reso.update()
|
||||||
|
|
||||||
|
case(resonite_animx.CurveTrack):
|
||||||
|
curvetrack: resonite_animx.CurveTrack = track
|
||||||
|
|
||||||
case(type(resonite_animx.CurveTrack)):
|
fcurve_reso.keyframe_points.add(count=len(curvetrack.keyframes))
|
||||||
curvetrack: resonite_animx.RawTrack = track
|
# populate points
|
||||||
for frame in curvetrack.keyframes:
|
fcurve_reso.keyframe_points.foreach_set("co", [x for co in zip([frame.time.x*track.Owner.interval.x for frame in curvetrack.keyframes], [eval("frame.value"+valuetype) for frame in curvetrack.keyframes]) for x in co])
|
||||||
key: bpy.types.Keyframe = fcurve_reso.keyframe_points.insert(frame.time, float(frame.value))
|
interp: bool = curvetrack.tangents
|
||||||
key.handle_left = float(frame.left_tan)
|
#print("has tangents? "+str(interp))
|
||||||
key.handle_left = float(frame.right_tan)
|
|
||||||
|
|
||||||
|
for idx,frame in enumerate(curvetrack.keyframes):
|
||||||
|
|
||||||
case(type(resonite_animx.BezierTrack)):
|
if interp:
|
||||||
beziertrack: resonite_animx.RawTrack = track
|
fcurve_reso.keyframe_points[idx].handle_left = float(eval("frame.left_tan"+valuetype))
|
||||||
|
fcurve_reso.keyframe_points[idx].handle_right = float(eval("frame.right_tan"+valuetype))
|
||||||
|
fcurve_reso.keyframe_points[idx].interpolation = "BEZIER"
|
||||||
|
fcurve_reso.keyframe_points[idx].easing = "EASE_IN"
|
||||||
|
fcurve_reso.update()
|
||||||
|
|
||||||
|
case(resonite_animx.BezierTrack):
|
||||||
|
beziertrack: resonite_animx.BezierTrack = track
|
||||||
# Bezier is not supported rn, ignore.
|
# Bezier is not supported rn, ignore.
|
||||||
|
case _:
|
||||||
|
print("invalid track type, ignoring")
|
||||||
|
print(track)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user