See More

import math from enum import IntEnum from .NamedObject import NamedObject from .PPtr import PPtr from ..enums import ClassIDType from ..math import Quaternion, Vector3 from ..streams import EndianBinaryReader def uint(num): if num < 0 or num > 4294967295: return num % 4294967296 return num class Keyframe: def __init__(self, reader, readerFunc): self.time = reader.read_float() self.value = readerFunc() self.inSlope = readerFunc() self.outSlope = readerFunc() if reader.version >= (2018,): # 2018 and up self.weightedMode = reader.read_int() self.inWeight = readerFunc() self.outWeight = readerFunc() class AnimationCurve: def __init__(self, reader, readerFunc): version = reader.version numCurves = reader.read_int() self.m_Curve = [Keyframe(reader, readerFunc) for _ in range(numCurves)] self.m_PreInfinity = reader.read_int() self.m_PostInfinity = reader.read_int() if version >= (5, 3): # 5.3 and up self.m_RotationOrder = reader.read_int() class QuaternionCurve: def __init__(self, reader): self.curve = AnimationCurve(reader, reader.read_quaternion) # self.path = reader.read_aligned_string() class PackedFloatVector: def __init__(self, reader): self.m_NumItems = reader.read_u_int() self.m_Range = reader.read_float() self.m_Start = reader.read_float() numData = reader.read_int() self.m_Data = reader.read_bytes(numData) reader.align_stream() self.m_BitSize = reader.read_byte() reader.align_stream() def save(self, writer): writer.write_u_int(self.m_NumItems) writer.write_float(self.m_Range) writer.write_float(self.m_Start) writer.write_int(len(self.m_Data)) writer.write_bytes(self.m_Data) writer.align_stream() writer.write_byte(self.m_BitSize) writer.align_stream() def UnpackFloats( self, itemCountInChunk: int, chunkStride: int, start: int = 0, numChunks: int = -1, ): bitPos: int = self.m_BitSize * start indexPos: int = bitPos // 8 bitPos %= 8 scale: float = (1.0 / self.m_Range) if self.m_Range else float("inf") if numChunks == -1: numChunks = self.m_NumItems // itemCountInChunk end = int(chunkStride * numChunks / 4) data = [] for index in range(0, end, chunkStride // 4): for i in range(itemCountInChunk): x = 0 # uint bits = 0 while bits < self.m_BitSize: x |= uint( (self.m_Data[indexPos] >> bitPos) << bits ) # (uint)((m_Data[indexPos] >> bitPos) << bits) num = min(self.m_BitSize - bits, 8 - bitPos) bitPos += num bits += num if bitPos == 8: # indexPos += 1 bitPos = 0 x &= uint((1 << self.m_BitSize) - 1) # (uint)(1 << m_BitSize) - 1u denomi = scale * ((1 << self.m_BitSize) - 1) data.append((x / denomi if denomi else float("inf")) + self.m_Start) return data class PackedIntVector: def __init__(self, reader): self.m_NumItems = reader.read_u_int() numData = reader.read_int() self.m_Data = reader.read_bytes(numData) reader.align_stream() self.m_BitSize = reader.read_byte() reader.align_stream() def save(self, writer): writer.write_u_int(self.m_NumItems) writer.write_int(len(self.m_Data)) writer.write_bytes(self.m_Data) writer.align_stream() writer.write_byte(self.m_BitSize) writer.align_stream() def UnpackInts(self): data = [0] * self.m_NumItems indexPos = 0 bitPos = 0 for i in range(self.m_NumItems): bits = 0 data[i] = 0 while bits < self.m_BitSize: data[i] |= (self.m_Data[indexPos] >> bitPos) << bits num = min(self.m_BitSize - bits, 8 - bitPos) bitPos += num bits += num if bitPos == 8: indexPos += 1 bitPos = 0 data[i] &= (1 << self.m_BitSize) - 1 return data class PackedQuatVector: def __init__(self, reader): self.m_NumItems = reader.read_u_int() numData = reader.read_int() self.m_Data = reader.read_bytes(numData) reader.align_stream() def UnpackQuats(self): m_Data = self.m_Data data = [None] * self.m_NumItems indexPos = 0 bitPos = 0 for i in range(self.m_NumItems): flags = 0 bits = 0 while bits < 3: flags |= (m_Data[indexPos] >> bitPos) << bits # unit num = min(3 - bits, 8 - bitPos) bitPos += num bits += num if bitPos == 8: # indexPos += 1 bitPos = 0 flags &= 7 q = Quaternion() sum = 0 for j in range(4): if (flags & 3) != j: # bitSize = 9 if ((flags & 3) + 1) % 4 == j else 10 x = 0 bits = 0 while bits < bitSize: x |= (m_Data[indexPos] >> bitPos) << bits # uint num = min(bitSize - bits, 8 - bitPos) bitPos += num bits += num if bitPos == 8: # indexPos += 1 bitPos = 0 x &= (1 << bitSize) - 1 # unit q[j] = x / (0.5 * ((1 << bitSize) - 1)) - 1 sum += q[j] * q[j] lastComponent = flags & 3 # int q[lastComponent] = math.sqrt(1 - sum) # float if (flags & 4) != 0: # 0u q[lastComponent] = -q[lastComponent] data.append(q) return data class CompressedAnimationCurve: def __init__(self, reader): self.m_Path = reader.read_aligned_string() self.m_Times = PackedIntVector(reader) self.m_Values = PackedQuatVector(reader) self.m_Slopes = PackedFloatVector(reader) self.m_PreInfinity = reader.read_int() self.m_PostInfinity = reader.read_int() class Vector3Curve: def __init__(self, reader): self.curve = AnimationCurve(reader, reader.read_vector3) # Vector3 self.path = reader.read_aligned_string() class FloatCurve: def __init__(self, reader): self.curve = AnimationCurve(reader, reader.read_float) # Float self.attribute = reader.read_aligned_string() self.path = reader.read_aligned_string() self.classID = ClassIDType(reader.read_int()) self.script = PPtr(reader) # MonoScript class PPtrKeyframe: def __init__(self, reader): self.time = reader.read_float() self.value = PPtr(reader) # Object class PPtrCurve: def __init__(self, reader): numCurves = reader.read_int() self.curve = [PPtrKeyframe(reader) for _ in range(numCurves)] self.attribute = reader.read_aligned_string() self.path = reader.read_aligned_string() self.classID = reader.read_int() self.script = PPtr(reader) # MonoScript class AABB: def __init__(self, reader): self.m_Center = reader.read_vector3() self.m_Extent = reader.read_vector3() def save(self, writer): writer.write_vector3(self.m_Center) writer.write_vector3(self.m_Extent) class xform: def __init__(self, reader): version = reader.version self.t = ( reader.read_vector3() if version >= (5, 4) else Vector3(reader.read_vector4()) ) # 5.4 and up self.q = reader.read_quaternion() self.s = ( reader.read_vector3() if version >= (5, 4) else Vector3(reader.read_vector4()) ) # 5.4 and up class HandPose: def __init__(self, reader): self.m_GrabX = xform(reader) self.m_DoFArray = reader.read_float_array() self.m_Override = reader.read_float() self.m_CloseOpen = reader.read_float() self.m_InOut = reader.read_float() self.m_Grab = reader.read_float() class HumanGoal: def __init__(self, reader): version = reader.version self.m_X = xform(reader) self.m_WeightT = reader.read_float() self.m_WeightR = reader.read_float() if version >= (5,): # 5.0 and up self.m_HintT = ( reader.read_vector3() if version >= (5, 4) else Vector3(reader.read_vector4()) ) # 5.4 and up self.m_HintWeightT = reader.read_float() class HumanPose: def __init__(self, reader): version = reader.version self.m_RootX = xform(reader) self.m_LookAtPosition = ( reader.read_vector3() if version >= (5, 4) else Vector3(reader.read_vector4()) ) # 5.4 and up self.m_LookAtWeight = reader.read_vector4() numGoals = reader.read_int() self.m_GoalArray = [HumanGoal(reader) for _ in range(numGoals)] self.m_LeftHandPose = HandPose(reader) self.m_RightHandPose = HandPose(reader) self.m_DoFArray = reader.read_float_array() if version >= (5, 2): # 5.2 and up numTDof = reader.read_int() self.m_TDoFArray = [ reader.read_vector3() if version >= (5, 4) else Vector3(reader.read_vector4()) # 5.4 and up for _ in range(numTDof) ] class StreamedCurveKey: def __init__(self, reader): self.index = reader.read_int() self.coeff = reader.read_float_array(4) self.outSlope = self.coeff[2] self.value = self.coeff[3] def CalculateNextInSlope(self, dx: float, rhs): """ :param dx: float :param rhs: StreamedCurvedKey :return: """ # Stepped if self.coeff[0] == 0 and self.coeff[1] == 0 and self.coeff[2] == 0: return float("inf") dx = max(dx, 0.0001) dy = rhs.value - self.value length = 1.0 / (dx * dx) d1 = self.outSlope * dx d2 = dy + dy + dy - d1 - d1 - self.coeff[1] / length return d2 / dx class StreamedFrame: def __init__(self, reader): self.time = reader.read_float() numKeys = reader.read_int() self.keyList = [StreamedCurveKey(reader) for _ in range(numKeys)] class StreamedClip: def __init__(self, reader): self.data = reader.read_u_int_array() self.curveCount = reader.read_u_int() def ReadData(self): frameList = [] buffer = b"".join(val.to_bytes(4, "big") for val in self.data) reader = EndianBinaryReader(buffer) while reader.Position < reader.Length: frameList.append(StreamedFrame(reader)) for frameIndex in range(2, len(frameList) - 1): frame = frameList[frameIndex] for curveKey in frame.keyList: i = frameIndex - 1 while i >= 0: preFrame = frameList[i] try: preCurveKey = [ x for x in preFrame.keyList if x.index == curveKey.index ][0] curveKey.inSlope = preCurveKey.CalculateNextInSlope( frame.time - preFrame.time, curveKey ) break except IndexError: pass i -= 1 return frameList class DenseClip: def __init__(self, reader): self.m_FrameCount = reader.read_int() self.m_CurveCount = reader.read_u_int() self.m_SampleRate = reader.read_float() self.m_BeginTime = reader.read_float() self.m_SampleArray = reader.read_float_array() class ConstantClip: def __init__(self, reader): self.data = reader.read_float_array() class ValueConstant: def __init__(self, reader): version = reader.version self.m_ID = reader.read_u_int() if version < (5, 5): # 5.5 down self.m_TypeID = reader.read_u_int() self.m_Type = reader.read_u_int() self.m_Index = reader.read_u_int() class ValueArrayConstant: def __init__(self, reader): numVals = reader.read_int() self.m_ValueArray = [ValueConstant(reader) for _ in range(numVals)] class Clip: def __init__(self, reader): version = reader.version self.m_StreamedClip = StreamedClip(reader) self.m_DenseClip = DenseClip(reader) if version >= (4, 3): # 4.3 and up self.m_ConstantClip = ConstantClip(reader) if version < (2018, 3): # 2018.3 down self.m_Binding = ValueArrayConstant(reader) class ValueDelta: def __init__(self, reader): self.m_Start = reader.read_float() self.m_Stop = reader.read_float() class ClipMuscleConstant: def __init__(self, reader): version = reader.version self.m_DeltaPose = HumanPose(reader) self.m_StartX = xform(reader) if version >= (5, 5): # 5.5 and up self.m_StopX = xform(reader) self.m_LeftFootStartX = xform(reader) self.m_RightFootStartX = xform(reader) if version < (5,): # 5.0 down self.m_MotionStartX = xform(reader) self.m_MotionStopX = xform(reader) self.m_AverageSpeed = ( reader.read_vector3() if version >= (5, 4) else Vector3(reader.read_vector4()) ) # 5.4 and up self.m_Clip = Clip(reader) self.m_StartTime = reader.read_float() self.m_StopTime = reader.read_float() self.m_OrientationOffsetY = reader.read_float() self.m_Level = reader.read_float() self.m_CycleOffset = reader.read_float() self.m_AverageAngularSpeed = reader.read_float() self.m_IndexArray = reader.read_int_array() if version < (4, 3): # 4.3 down self.m_AdditionalCurveIndexArray = reader.read_int_array() numDeltas = reader.read_int() self.m_ValueArrayDelta = [ValueDelta(reader) for _ in range(numDeltas)] if version >= (5, 3): # 5.3 and up self.m_ValueArrayReferencePose = reader.read_float_array() self.m_Mirror = reader.read_boolean() if version >= (4, 3): # 4.3 and up self.m_LoopTime = reader.read_boolean() self.m_LoopBlend = reader.read_boolean() self.m_LoopBlendOrientation = reader.read_boolean() self.m_LoopBlendPositionY = reader.read_boolean() self.m_LoopBlendPositionXZ = reader.read_boolean() if version >= (5, 5): # 5.5 and up self.m_StartAtOrigin = reader.read_boolean() self.m_KeepOriginalOrientation = reader.read_boolean() self.m_KeepOriginalPositionY = reader.read_boolean() self.m_KeepOriginalPositionXZ = reader.read_boolean() self.m_HeightFromFeet = reader.read_boolean() reader.align_stream() class GenericBinding: def __init__(self, reader): version = reader.version self.path = reader.read_u_int() self.attribute = reader.read_u_int() self.script = PPtr(reader) # Object if version >= (5, 6): # 5.6 and up self.typeID = ClassIDType(reader.read_int()) else: self.typeID = ClassIDType(reader.read_u_short()) self.customType = reader.read_byte() self.isPPtrCurve = reader.read_byte() reader.align_stream() class AnimationClipBindingConstant: def __init__(self, reader): numBindings = reader.read_int() self.genericBindings = [GenericBinding(reader) for _ in range(numBindings)] numMappings = reader.read_int() self.pptrCurveMapping = [PPtr(reader) for _ in range(numMappings)] # Object def FindBinding(self, index): curves = 0 for b in self.genericBindings: if b.typeID == ClassIDType.Transform: # switch = b.attribute if switch in [1, 3, 4]: # case 1: #kBindTransformPosition # case 3: #kBindTransformScale # case 4: #kBindTransformEuler curves += 3 elif switch == 2: # kBindTransformRotation curves += 4 else: curves += 1 else: curves += 1 if curves > index: return b return None class AnimationType(IntEnum): kLegacy = (1,) kGeneric = (2,) kHumanoid = 3 class AnimationClip(NamedObject): def __init__(self, reader): super().__init__(reader=reader) version = reader.version if version >= (5,): # 5.0 and up self.m_Legacy = reader.read_boolean() elif version >= (4,): # 4.0 and up self.m_AnimationType = AnimationType(reader.read_int()) if self.m_AnimationType == AnimationType.kLegacy: # self.m_Legacy = True else: self.m_Legacy = True self.m_Compressed = reader.read_boolean() if version >= (4, 3): # 4.3 and up self.m_UseHighQualityCurve = reader.read_boolean() reader.align_stream() numRCurves = reader.read_int() self.m_RotationCurves = [QuaternionCurve(reader) for _ in range(numRCurves)] numCRCurves = reader.read_int() self.m_CompressedRotationCurves = [ CompressedAnimationCurve(reader) for _ in range(numCRCurves) ] if version >= (5, 3): # 5.3 and up numEulerCurves = reader.read_int() self.m_EulerCurves = [Vector3Curve(reader) for _ in range(numEulerCurves)] numPCurves = reader.read_int() self.m_PositionCurves = [Vector3Curve(reader) for _ in range(numPCurves)] numSCurves = reader.read_int() self.m_ScaleCurves = [Vector3Curve(reader) for _ in range(numSCurves)] numFCurves = reader.read_int() self.m_FloatCurves = [FloatCurve(reader) for _ in range(numFCurves)] if version >= (4, 3): # 4.3 and up numPtrCurves = reader.read_int() self.m_PPtrCurves = [PPtrCurve(reader) for _ in range(numPtrCurves)] self.m_SampleRate = reader.read_float() self.m_WrapMode = reader.read_int() if version >= (3, 4): # 3.4 and up self.m_Bounds = AABB(reader) if version >= (4,): # 4.0 and up self.m_MuscleClipSize = reader.read_u_int() self.m_MuscleClip = ClipMuscleConstant(reader) if version >= (4, 3): # 4.3 and up self.m_ClipBindingConstant = AnimationClipBindingConstant(reader) # m_HasGenericRootTransform 2018.3 # m_HasMotionFloatCurves 2018.3 # numEvents = reader.read_int() # self.m_Events = [ # AnimationEvent(reader) # for _ in range(numEvents) # ]