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
|
||||
|
||||
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:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"language": 0,
|
||||
"last_update_check": 1734208835.8049936
|
||||
"last_update_check": 1734295375.2681296
|
||||
}
|
||||
@@ -1,12 +1,21 @@
|
||||
import ctypes
|
||||
import typing
|
||||
import struct
|
||||
import struct
|
||||
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:
|
||||
charamount = read7bitEncoded_int(data)
|
||||
string: str = data.read(charamount).decode('utf-8', errors="replace")
|
||||
print("read string: "+string)
|
||||
#print("read string: "+string)
|
||||
return string
|
||||
|
||||
def WriteCSharp_str(data: BytesIO, string: str) -> str:
|
||||
@@ -29,7 +38,7 @@ def read7bitEncoded_ulong(data: BytesIO) -> int:
|
||||
return num
|
||||
|
||||
def read7bitEncoded_int(data: BytesIO) -> int:
|
||||
num: int= int(0)
|
||||
num: int = int(0)
|
||||
num2:int = int(0)
|
||||
while (num2 != 35):
|
||||
b: int = int(struct.unpack('<B', data.read(1))[0])
|
||||
@@ -41,7 +50,7 @@ def read7bitEncoded_int(data: BytesIO) -> int:
|
||||
|
||||
def write7bitEncoded_ulong(data: BytesIO, integer: int) -> None:
|
||||
while integer > int(0):
|
||||
b: int = ctypes.c_ubyte(integer & int(127))
|
||||
b: int = int(integer & int(127))
|
||||
integer >>= 7
|
||||
if integer > int(0):
|
||||
b |= 128
|
||||
@@ -54,4 +63,4 @@ def write7bitEncoded_int(data: BytesIO, value: int) -> None:
|
||||
while(num >= int(128)):
|
||||
data.write(int(num | int(128)))
|
||||
num >>= 7
|
||||
data.Write(int(num))
|
||||
data.Write(int(num))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
from os import replace
|
||||
from re import S
|
||||
from types import FrameType
|
||||
|
||||
import lz4.block
|
||||
@@ -10,6 +11,7 @@ import typing
|
||||
import struct
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
KeyframeInterpolation: dict[str, int] = {
|
||||
"Hold": 1,
|
||||
"Linear": 2,
|
||||
@@ -18,28 +20,17 @@ KeyframeInterpolation: dict[str, int] = {
|
||||
}
|
||||
|
||||
class KeyFrame():
|
||||
time: resonite_types.float = resonite_types.float(0)
|
||||
interpolation: resonite_types.int = resonite_types.int(0)
|
||||
time: resonite_types.float
|
||||
interpolation: resonite_types.byte
|
||||
value: resonite_types.ResoType
|
||||
left_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):
|
||||
pass
|
||||
self.time = resonite_types.float(0)
|
||||
self.interpolation = resonite_types.byte(0)
|
||||
|
||||
|
||||
def RequiresTangents(self) -> bool:
|
||||
@@ -48,14 +39,17 @@ class KeyFrame():
|
||||
return False
|
||||
|
||||
class ResoTrack(resonite_types.ResoType):
|
||||
node: resonite_types.string = resonite_types.string("")
|
||||
property: resonite_types.string = resonite_types.string("")
|
||||
node: resonite_types.string
|
||||
property: resonite_types.string
|
||||
Owner: AnimX
|
||||
FrameType: type[resonite_types.ResoType]
|
||||
keyframes: list[KeyFrame] = []
|
||||
FrameType: str
|
||||
keyframes: list[KeyFrame]
|
||||
|
||||
def __init__(self,FrameType):
|
||||
self.FrameType = FrameType
|
||||
self.keyframes = []
|
||||
self.node = resonite_types.string("")
|
||||
self.property = resonite_types.string("")
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
self.node.write(data)
|
||||
@@ -65,10 +59,12 @@ class ResoTrack(resonite_types.ResoType):
|
||||
def read(self, data:BytesIO):
|
||||
self.node.read(data)
|
||||
self.property.read(data)
|
||||
|
||||
track_amount: int = int(common.read7bitEncoded_ulong(data))
|
||||
#print(track_amount)
|
||||
for i in range(0, track_amount):
|
||||
key: KeyFrame = KeyFrame()
|
||||
key.value = self.FrameType()
|
||||
key.value = eval(self.FrameType+"()")
|
||||
self.keyframes.append(key)
|
||||
|
||||
def removeKeyframe(self, time: float | int) -> bool:
|
||||
@@ -143,28 +139,35 @@ class ResoTrack(resonite_types.ResoType):
|
||||
|
||||
|
||||
class RawTrack(ResoTrack):
|
||||
interval: resonite_types.float = resonite_types.float(0)
|
||||
interval: resonite_types.float
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
if name == "interval":
|
||||
return self.Owner.interval.x
|
||||
return self.Owner.interval
|
||||
return super().__getattribute__(name)
|
||||
|
||||
def __init__(self, FrameType):
|
||||
super().__init__(FrameType)
|
||||
self.interval = resonite_types.float(0)
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
super().write(data)
|
||||
self.interval.write(data)
|
||||
for key in self.keyframes:
|
||||
key.value.write(data)
|
||||
if self.FrameType == "resonite_types.string":
|
||||
resonite_types.writeNullable(data, key.value)
|
||||
else:
|
||||
key.value.write(data)
|
||||
|
||||
|
||||
def read(self, data:BytesIO):
|
||||
super().read(data)
|
||||
self.interval.read(data)
|
||||
for key in self.keyframes:
|
||||
key.value.read(data)
|
||||
if self.FrameType == "resonite_types.string":
|
||||
resonite_types.readNullable(data, key.value)
|
||||
else:
|
||||
key.value.read(data)
|
||||
|
||||
def addKeyframe(self, keyframe: KeyFrame) -> int:
|
||||
num: int = super().addKeyframe(keyframe)
|
||||
@@ -187,10 +190,28 @@ class DiscreteTrack(ResoTrack):
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
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):
|
||||
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:
|
||||
num: int = super().addKeyframe(keyframe)
|
||||
@@ -203,23 +224,27 @@ class DiscreteTrack(ResoTrack):
|
||||
|
||||
|
||||
class CurveTrack(ResoTrack):
|
||||
interpolations: bool = False
|
||||
tangents: bool = False
|
||||
sharedinterpolation: resonite_types.int = resonite_types.int(-1)
|
||||
interpolations: bool
|
||||
tangents: bool
|
||||
sharedinterpolation: resonite_types.byte
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
if name == "interpolations":
|
||||
integerframe: int = self.keyframes[0].interpolation.x
|
||||
for key in self.keyframes:
|
||||
if key.interpolation.x != self.sharedinterpolation.x:
|
||||
if key.interpolation.x != integerframe:
|
||||
return True
|
||||
elif name == "tangents":
|
||||
for key in self.keyframes:
|
||||
if key.interpolation.x == 3 or key.interpolation.x == 4:
|
||||
if key.RequiresTangents():
|
||||
return True
|
||||
return super().__getattribute__(name)
|
||||
|
||||
def __init__(self, FrameType):
|
||||
super().__init__(FrameType)
|
||||
self.sharedinterpolation = resonite_types.byte(-1)
|
||||
self.interpolations = False
|
||||
self.tangents = False
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
super().write(data)
|
||||
@@ -234,45 +259,57 @@ class CurveTrack(ResoTrack):
|
||||
|
||||
for key in self.keyframes:
|
||||
if key.value == None:
|
||||
key.value = self.FrameType()
|
||||
key.value.write(data)
|
||||
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)
|
||||
|
||||
if(self.tangents):
|
||||
for key in self.keyframes:
|
||||
key.left_tan.write(data)
|
||||
key.right_tan.write(data)
|
||||
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.right_tan.write(data)
|
||||
|
||||
def read(self, data:BytesIO):
|
||||
super().read(data)
|
||||
flags: int = struct.unpack("<B",data.read(1))[0]
|
||||
self.interpolations = (flags & 1) > 0
|
||||
self.tangents = (flags & 2) > 0
|
||||
interp: bool = (flags & 1) > 0
|
||||
tan: bool = (flags & 2) > 0
|
||||
|
||||
print(str(self.interpolations))
|
||||
print(str(self.tangents))
|
||||
#print(str(interp))
|
||||
#print(str(tan))
|
||||
#print(flags)
|
||||
#print(len(self.keyframes))
|
||||
|
||||
if(self.interpolations):
|
||||
if(interp):
|
||||
for key in self.keyframes:
|
||||
#print("reading interp")
|
||||
key.interpolation.read(data)
|
||||
else:
|
||||
self.sharedinterpolation.read(data)
|
||||
|
||||
|
||||
for key in self.keyframes:
|
||||
print("key read!")
|
||||
if key.value == None:
|
||||
key.value = self.FrameType()
|
||||
key.value.read(data)
|
||||
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)
|
||||
|
||||
if(self.tangents):
|
||||
if(tan):
|
||||
for key in self.keyframes:
|
||||
if key.left_tan == None:
|
||||
key.left_tan = self.FrameType()
|
||||
if key.right_tan == None:
|
||||
key.right_tan = self.FrameType()
|
||||
key.left_tan.read(data)
|
||||
key.right_tan.read(data)
|
||||
if self.FrameType == "resonite_types.string":
|
||||
resonite_types.readNullable(data, key.left_tan)
|
||||
resonite_types.readNullable(data, key.right_tan)
|
||||
else:
|
||||
key.left_tan.read(data)
|
||||
key.right_tan.read(data)
|
||||
|
||||
|
||||
|
||||
@@ -313,56 +350,56 @@ class BezierTrack(ResoTrack):
|
||||
"""PLACE HOLDER METHOD, DO NOT USE"""
|
||||
raise Exception("BezierTrack track type is unsupported in resonite's code")
|
||||
#This is weird, but thank you python - @989onan
|
||||
TrackTypes: list[type] = [
|
||||
RawTrack,
|
||||
DiscreteTrack,
|
||||
CurveTrack,
|
||||
BezierTrack
|
||||
TrackTypes: list[str] = [
|
||||
"RawTrack",
|
||||
"DiscreteTrack",
|
||||
"CurveTrack",
|
||||
"BezierTrack"
|
||||
]
|
||||
|
||||
#TODO: add all types here
|
||||
#wooooo - @989onan
|
||||
elementTypes: list[type[resonite_types.ResoType]] = [
|
||||
resonite_types.bool,
|
||||
resonite_types.bool2,
|
||||
resonite_types.bool3,
|
||||
resonite_types.bool4,
|
||||
resonite_types.byte,
|
||||
resonite_types.ushort,
|
||||
resonite_types.uint,
|
||||
resonite_types.ulong,
|
||||
resonite_types.sbyte,
|
||||
resonite_types.short,
|
||||
resonite_types.int,
|
||||
resonite_types.long,
|
||||
resonite_types.int2,
|
||||
resonite_types.int3,
|
||||
resonite_types.int4,
|
||||
resonite_types.uint2,
|
||||
resonite_types.uint3,
|
||||
resonite_types.uint4,
|
||||
resonite_types.long2,
|
||||
resonite_types.long3,
|
||||
resonite_types.long4,
|
||||
resonite_types.float,
|
||||
resonite_types.float2,
|
||||
resonite_types.float3,
|
||||
resonite_types.float4,
|
||||
resonite_types.floatQ,
|
||||
resonite_types.float2x2,
|
||||
resonite_types.float3x3,
|
||||
resonite_types.float4x4,
|
||||
resonite_types.double,
|
||||
resonite_types.double2,
|
||||
resonite_types.double3,
|
||||
resonite_types.double4,
|
||||
resonite_types.doubleQ,
|
||||
resonite_types.double2x2,
|
||||
resonite_types.double3x3,
|
||||
resonite_types.double4x4,
|
||||
resonite_types.color,
|
||||
resonite_types.color32,
|
||||
resonite_types.string
|
||||
elementTypes: list[str] = [
|
||||
"resonite_types.bool",
|
||||
"resonite_types.bool2",
|
||||
"resonite_types.bool3",
|
||||
"resonite_types.bool4",
|
||||
"resonite_types.byte",
|
||||
"resonite_types.ushort",
|
||||
"resonite_types.uint",
|
||||
"resonite_types.ulong",
|
||||
"resonite_types.sbyte",
|
||||
"resonite_types.short",
|
||||
"resonite_types.int",
|
||||
"resonite_types.long",
|
||||
"resonite_types.int2",
|
||||
"resonite_types.int3",
|
||||
"resonite_types.int4",
|
||||
"resonite_types.uint2",
|
||||
"resonite_types.uint3",
|
||||
"resonite_types.uint4",
|
||||
"resonite_types.long2",
|
||||
"resonite_types.long3",
|
||||
"resonite_types.long4",
|
||||
"resonite_types.float",
|
||||
"resonite_types.float2",
|
||||
"resonite_types.float3",
|
||||
"resonite_types.float4",
|
||||
"resonite_types.floatQ",
|
||||
"resonite_types.float2x2",
|
||||
"resonite_types.float3x3",
|
||||
"resonite_types.float4x4",
|
||||
"resonite_types.double",
|
||||
"resonite_types.double2",
|
||||
"resonite_types.double3",
|
||||
"resonite_types.double4",
|
||||
"resonite_types.doubleQ",
|
||||
"resonite_types.double2x2",
|
||||
"resonite_types.double3x3",
|
||||
"resonite_types.double4x4",
|
||||
"resonite_types.color",
|
||||
"resonite_types.color32",
|
||||
"resonite_types.string"
|
||||
]
|
||||
|
||||
|
||||
@@ -372,20 +409,27 @@ class AnimX():
|
||||
|
||||
|
||||
"""
|
||||
To use Raw Track properly, please set interval (seconds between frames) after reading/creating.\n
|
||||
Represents data to be written to or read from an AnimX file.
|
||||
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.\n
|
||||
default interval to use would be 30.
|
||||
"""
|
||||
|
||||
file_version: resonite_types.int = resonite_types.int()
|
||||
track_amount: resonite_types.int = resonite_types.int()
|
||||
global_duration: resonite_types.float = resonite_types.float()
|
||||
name: resonite_types.string = resonite_types.string()
|
||||
file_version: resonite_types.int
|
||||
track_amount: resonite_types.int
|
||||
global_duration: resonite_types.float
|
||||
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):
|
||||
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
|
||||
|
||||
@classmethod
|
||||
@@ -424,7 +468,7 @@ class AnimX():
|
||||
|
||||
self.track_amount.x = common.read7bitEncoded_ulong(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))
|
||||
|
||||
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
|
||||
filelmza: bytes = bytes(AnimX.decompress_lzma(data.read(), lzma.FORMAT_RAW, filters))
|
||||
|
||||
print(f"decompressed bytes: {filelmza[:100]}")
|
||||
#print("binary below:")
|
||||
#print("b'{}'".format(''.join('\\x{:02x}'.format(b) for b in filelmza[:100])))
|
||||
data = BytesIO(filelmza)
|
||||
case _:
|
||||
raise Exception("Invalid encoding")
|
||||
@@ -515,10 +559,10 @@ class AnimX():
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def GetTrackType(cls, trackType2: int, value_type: type[resonite_types.ResoType], data: BytesIO) -> ResoTrack:
|
||||
Track = TrackTypes[trackType2](value_type)
|
||||
print(type(Track))
|
||||
print(value_type)
|
||||
def GetTrackType(cls, trackType2: int, value_type: str, data: BytesIO) -> ResoTrack:
|
||||
Track: ResoTrack = eval(TrackTypes[trackType2]+"(value_type)")
|
||||
#print(value_type)
|
||||
#print(type(Track))
|
||||
Track.read(data)
|
||||
return Track
|
||||
|
||||
|
||||
@@ -17,6 +17,19 @@ class ResoType():
|
||||
def read(cls, data: BytesIO):
|
||||
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.
|
||||
|
||||
class color(ResoType):
|
||||
@@ -75,8 +88,8 @@ class byte(ResoType):
|
||||
def __int__(self):
|
||||
return self.x
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self,value=0):
|
||||
self.x = value
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
data.write(struct.pack("<B", self.x))
|
||||
@@ -120,8 +133,8 @@ class short(ResoType):
|
||||
def __int__(self):
|
||||
return self.x
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self,value=0):
|
||||
self.x = value
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
data.write(struct.pack("<h", self.x))
|
||||
@@ -264,7 +277,7 @@ class int4(int3):
|
||||
pass
|
||||
|
||||
def read(self,data: BytesIO):
|
||||
super().write(data)
|
||||
super().read(data)
|
||||
self.w = struct.unpack("<i", data.read(4))[0]
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
@@ -298,7 +311,7 @@ class uint2(uint):
|
||||
pass
|
||||
|
||||
def read(self,data: BytesIO):
|
||||
super().write(data)
|
||||
super().read(data)
|
||||
self.y = struct.unpack("<I", data.read(4))[0]
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
@@ -312,7 +325,7 @@ class uint3(uint2):
|
||||
pass
|
||||
|
||||
def read(self,data: BytesIO):
|
||||
super().write(data)
|
||||
super().read(data)
|
||||
self.z = struct.unpack("<I", data.read(4))[0]
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
@@ -326,7 +339,7 @@ class uint4(uint3):
|
||||
pass
|
||||
|
||||
def read(self,data: BytesIO):
|
||||
super().write(data)
|
||||
super().read(data)
|
||||
self.w = struct.unpack("<I", data.read(4))[0]
|
||||
|
||||
def write(self, data: BytesIO):
|
||||
@@ -529,8 +542,7 @@ class doubleQ(double4):
|
||||
def write(self, data: BytesIO):
|
||||
super().write(data)
|
||||
|
||||
@classmethod
|
||||
def read(cls, data: BytesIO):
|
||||
def read(self, data: BytesIO):
|
||||
super().read(data)
|
||||
|
||||
|
||||
@@ -587,7 +599,7 @@ class float4(float3):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def read(self,data: BytesIO):
|
||||
def read(self, data: BytesIO):
|
||||
super().read(data)
|
||||
self.w = struct.unpack("<f", data.read(4))[0]
|
||||
|
||||
@@ -649,6 +661,5 @@ class floatQ(float4):
|
||||
def write(self, data: BytesIO):
|
||||
super().write(data)
|
||||
|
||||
@classmethod
|
||||
def read(cls, data: BytesIO):
|
||||
def read(self, data: BytesIO):
|
||||
super().read(data)
|
||||
+94
-34
@@ -1,5 +1,7 @@
|
||||
from types import FrameType
|
||||
import bpy
|
||||
import bpy_extras
|
||||
from numpy import double
|
||||
|
||||
from .common import get_armature, get_selected_armature, simplify_bonename, is_valid_armature
|
||||
from bpy.types import Object, ShapeKey, Mesh, Context, Operator
|
||||
@@ -149,6 +151,14 @@ class AvatarToolKit_OT_ConvertToResonite(Operator):
|
||||
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
|
||||
class AvatarToolKit_OT_AnimX_Importer(Operator,bpy_extras.io_utils.ImportHelper):
|
||||
bl_idname = 'avatar_toolkit.animx_importer'
|
||||
@@ -169,7 +179,7 @@ class AvatarToolKit_OT_AnimX_Importer(Operator,bpy_extras.io_utils.ImportHelper)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: Context) -> bool:
|
||||
return True
|
||||
return context.active_object != None
|
||||
|
||||
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:
|
||||
files = [file.name for file in self.files]
|
||||
files.append(self.filepath)
|
||||
#files.append(self.filepath)
|
||||
for file in files:
|
||||
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_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.
|
||||
for froox_animation in Froox_animations:
|
||||
action: bpy.types.Action = bpy.data.actions.new(froox_animation.name.x)
|
||||
target.animation_data.action = action
|
||||
action.use_fake_user = True
|
||||
for track in froox_animation.tracks:
|
||||
print("hit here1")
|
||||
print(track.FrameType)
|
||||
if(track.FrameType != type(resonite_types.float) and track.FrameType != type(resonite_types.double)):
|
||||
data_path: str
|
||||
actualproperty: str = track.property.x
|
||||
|
||||
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
|
||||
return {'FINISHED'}
|
||||
|
||||
def readTrackData(self,track: resonite_animx.ResoTrack, fcurve_reso: bpy.types.FCurve, valuetype: str = ""):
|
||||
tracktype = type(track)
|
||||
match(tracktype):
|
||||
case (resonite_animx.RawTrack):
|
||||
rawtrack: resonite_animx.RawTrack = track
|
||||
|
||||
data_path: str = track._node.x+"."+track._property.x
|
||||
fcurve_reso = action.fcurves.new(data_path,None,track._node.x)
|
||||
|
||||
print("hit here2")
|
||||
match(type(track)):
|
||||
case (type(resonite_animx.RawTrack)):
|
||||
rawtrack: resonite_animx.RawTrack = track
|
||||
|
||||
fcurve_reso.keyframe_points.add(count=len(rawtrack.keyframes))
|
||||
# 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()
|
||||
|
||||
|
||||
|
||||
for frame in rawtrack.keyframes:
|
||||
key: bpy.types.Keyframe = fcurve_reso.keyframe_points.insert(frame.time, float(frame.value))
|
||||
|
||||
case (resonite_animx.DiscreteTrack):
|
||||
discretetrack: resonite_animx.DiscreteTrack = track
|
||||
|
||||
case (type(resonite_animx.DiscreteTrack)):
|
||||
discretetrack: resonite_animx.RawTrack = track
|
||||
for frame in rawtrack.keyframes:
|
||||
key: bpy.types.Keyframe = fcurve_reso.keyframe_points.insert(frame.time, float(frame.value))
|
||||
fcurve_reso.keyframe_points.add(count=len(discretetrack.keyframes))
|
||||
# populate points
|
||||
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])
|
||||
fcurve_reso.update()
|
||||
|
||||
case(resonite_animx.CurveTrack):
|
||||
curvetrack: resonite_animx.CurveTrack = track
|
||||
|
||||
case(type(resonite_animx.CurveTrack)):
|
||||
curvetrack: resonite_animx.RawTrack = track
|
||||
for frame in curvetrack.keyframes:
|
||||
key: bpy.types.Keyframe = fcurve_reso.keyframe_points.insert(frame.time, float(frame.value))
|
||||
key.handle_left = float(frame.left_tan)
|
||||
key.handle_left = float(frame.right_tan)
|
||||
fcurve_reso.keyframe_points.add(count=len(curvetrack.keyframes))
|
||||
# populate points
|
||||
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])
|
||||
interp: bool = curvetrack.tangents
|
||||
#print("has tangents? "+str(interp))
|
||||
|
||||
for idx,frame in enumerate(curvetrack.keyframes):
|
||||
|
||||
if interp:
|
||||
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(type(resonite_animx.BezierTrack)):
|
||||
beziertrack: resonite_animx.RawTrack = track
|
||||
# Bezier is not supported rn, ignore.
|
||||
case(resonite_animx.BezierTrack):
|
||||
beziertrack: resonite_animx.BezierTrack = track
|
||||
# Bezier is not supported rn, ignore.
|
||||
case _:
|
||||
print("invalid track type, ignoring")
|
||||
print(track)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user