Skip to content

Instantly share code, notes, and snippets.

@Hazuki-san
Last active January 11, 2024 08:12
Show Gist options
  • Save Hazuki-san/50e5ad0860d3b5e276eddf41bc2e570f to your computer and use it in GitHub Desktop.
Save Hazuki-san/50e5ad0860d3b5e276eddf41bc2e570f to your computer and use it in GitHub Desktop.
File is from Modders' Heaven; MGSV modding community
import sys
import struct
import os
import math
import ctypes
PI = 22.0/7.0
def ToEuler(q):
# working version
x, y, z, w = q
ysqr = y*y
t0 = +2.0 * (w * x + y*z)
t1 = +1.0 - 2.0 * (x*x + ysqr)
X = math.degrees(math.atan2(t0, t1))
t2 = +2.0 * (w*y - z*x)
t2 = 1 if t2 > 1 else t2
t2 = -1 if t2 < -1 else t2
Y = math.degrees(math.asin(t2))
t3 = +2.0 * (w * z + x*y)
t4 = +1.0 - 2.0 * (ysqr + z*z)
Z = math.degrees(math.atan2(t3, t4))
return [X, Y, Z]
class ASkel():
def __init__(self, bstr):
#Init
sig = struct.unpack('i', bstr.read(4))[0]
self.count = struct.unpack('i', bstr.read(4))[0] +1
self.bone = [None] * self.count
#cache ids
#self.boneList = {}
#self.parentList = {}
for c in range(self.count):
if c == 0:
self.bone[0] = BoneData()
self.bone[0].name = "ROOT"
#self.boneList["root_main"]
else:
self.bone[c] = BoneData(bstr)
# print(self.bone)
class BoneData():
def __init__(self, bstr=None):
if bstr == None:
self.name = "ROOT"
self.parent = -1
self.pos = (0,0,0,0)
self.rot = (0,0,0,1)
else:
bstr.read(4)
nameOff = struct.unpack('i', bstr.read(4))[0]
bac = bstr.tell()
bstr.seek(bac+nameOff-4)
self.name = ""
while True:
c = struct.unpack("s", bstr.read(1))[0]
if c == b'\0':
break
else:
self.name += c.decode('utf-8')
bstr.seek(bac)
self.parent = struct.unpack('i', bstr.read(4))[0] +1
self.pos = struct.unpack('ffff', bstr.read(16))
self.rot = struct.unpack('ffff', bstr.read(16))
# Create a skeleton with 2 segments.
boneNameList = []
trackBoneNameList = {}
heirachy = {}
boneList = {}
boneStats = {}
def CreateScene(pSdkManager, pScene, isSkel=False):
# Create scene info
lSceneInfo = FbxDocumentInfo.Create(pSdkManager, "SceneInfo")
lSceneInfo.mTitle = ""
lSceneInfo.mSubject = ""
lSceneInfo.mAuthor = ""
lSceneInfo.mRevision = ""
lSceneInfo.mKeywords = ""
lSceneInfo.mComment = ""
pScene.SetSceneInfo(lSceneInfo)
FbxAxisSystem(FbxAxisSystem.eOpenGL).ConvertScene(pScene)
#proper scaling
if(pScene.GetGlobalSettings().GetSystemUnit() == FbxSystemUnit.cm):
FbxSystemUnit.ConvertScene(FbxSystemUnit.m, pScene)
CreateSkeleton(pSdkManager, pScene)
def CreateNode(pSdkManager, boneName, pos, rot, isRoot):
boneNameList.append(boneName)
#create attributes
lBoneAttribute = FbxSkeleton.Create(pSdkManager, boneName)
if isRoot:
#set if root
lBoneAttribute.SetSkeletonType(FbxSkeleton.eRoot)
else:
#set if child
lBoneAttribute.SetSkeletonType(FbxSkeleton.eLimbNode)
lBoneAttribute.Size.Set(1.0)
boneNode = FbxNode.Create(pSdkManager, boneName)
#Attact its attributes
boneNode.SetNodeAttribute(lBoneAttribute)
#set translation
boneNode.LclTranslation.Set(FbxDouble3(pos[0], pos[1], pos[2]))
#set rotation (actually quaternion) x, y, z, w = rot
q = FbxQuaternion(rot[0], rot[1], rot[2], rot[3])
q.Normalize()
mat = FbxAMatrix()
mat.SetTQS(FbxVector4(0,0,0,0),q,FbxVector4(1,1,1,1))
r = mat.GetR()
#print boneName, rot, pos
boneNode.LclRotation.Set(FbxDouble3(r[0], r[1], r[2]))
boneNode.LclScaling.Set(FbxDouble3(1, 1, 1))
return boneNode
def CreateSkeleton(pSdkManager, pScene):
# trackBoneNameList[0] = "root_main"
# trackBoneNameList[1] = "root_main"
trackBoneNameList[0] = "ROOT"
trackBoneNameList[1] = "ROOT"
trackBoneNameList[2] = "sk_root_hip"
trackBoneNameList[3] = "sk_root_hip"
trackBoneNameList[4] = "dsk_hip"
trackBoneNameList[5] = "IK_L"
trackBoneNameList[6] = "IK_L"
trackBoneNameList[7] = "sk_thigh_l"
trackBoneNameList[8] = "sk_leg_l"
trackBoneNameList[9] = "sk_foot_l"
trackBoneNameList[10] = "IK_R"
trackBoneNameList[11] = "IK_R"
trackBoneNameList[12] = "sk_thigh_r"
trackBoneNameList[13] = "sk_leg_r"
trackBoneNameList[14] = "sk_foot_r"
trackBoneNameList[15] = "sk_belly"
trackBoneNameList[16] = "sk_chest"
trackBoneNameList[17] = "sk_neck"
trackBoneNameList[18] = "sk_head"
trackBoneNameList[19] = "sk_shoulder_l"
trackBoneNameList[20] = "sk_upperarm_l"
trackBoneNameList[21] = "sk_forearm_l"
trackBoneNameList[22] = "sk_hand_l"
trackBoneNameList[23] = "sk_shoulder_r"
trackBoneNameList[24] = "sk_upperarm_r"
trackBoneNameList[25] = "sk_forearm_r"
trackBoneNameList[26] = "sk_hand_r"
skelDir = r"C:\MGSV_Tools\mtar_reader\mrdev_tool\body_anim_skel.ask"
bReader = open(skelDir, "rb")
ask = ASkel(bReader)
bReader.close()
#Test
for n in range(ask.count):
boneList[ask.bone[n].name] = CreateNode(pSdkManager, ask.bone[n].name, ask.bone[n].pos, ask.bone[n].rot, ask.bone[n].parent == -1)
#from body high skel
#hip y = 1.09612
boneList["sk_toe_l"] = CreateNode(pSdkManager, "sk_toe_l", [math.expm1(-1.238315e-05), -0.06206484, 0.1042944], [0, 0, 0, 1], True) #sk_foot_l
boneList["tip_sk_toe_l"] = CreateNode(pSdkManager, "tip_sk_toe_l", [0.001241739, math.expm1(7.493223e-05), 0.1221953], [0, 0, 0, 1], True) #sk_toe_l
boneList["sk_toe_r"] = CreateNode(pSdkManager, "sk_toe_r", [math.expm1(1.238315e-05), -0.06206484, 0.1042944], [0, 0, 0, 1], True) #sk_foot_r
boneList["tip_sk_toe_r"] = CreateNode(pSdkManager, "tip_sk_toe_r", [-0.001241739, math.expm1(7.493223e-05), 0.1221953], [0, 0, 0, 1], True) #sk_toe_r
boneList["tip_sk_head"] = CreateNode(pSdkManager, "tip_sk_head", [math.expm1(5.906457e-17), 0.2, math.expm1(3.406619e-07)], [0, 0, 0, 1], True) #sk_head
boneList["tip_sk_hand_l"] = CreateNode(pSdkManager, "tip_sk_hand_l", [0.1499943, math.expm1(-1.230769e-06), -0.001309092], [0, 0, 0, 1], True) #sk_hand_l
boneList["tip_sk_hand_r"] = CreateNode(pSdkManager, "tip_sk_hand_r", [-0.1499943, math.expm1(1.295646e-06), -0.001308869], [0, 0, 0, 1], True) #sk_hand_r
#Parenting
for n in range(ask.count):
parent = ask.bone[n].parent
if parent == -1:
pScene.GetRootNode().AddChild(boneList[ask.bone[n].name])
else:
boneList[boneNameList[parent]].AddChild(boneList[ask.bone[n].name])
#
boneList["sk_foot_l"].AddChild( boneList["sk_toe_l"])
boneList["sk_toe_l"].AddChild( boneList["tip_sk_toe_l"])
boneList["sk_foot_r"].AddChild( boneList["sk_toe_r"])
boneList["sk_toe_r"].AddChild( boneList["tip_sk_toe_r"])
boneList["sk_head"].AddChild( boneList["tip_sk_head"])
boneList["sk_hand_l"].AddChild( boneList["tip_sk_hand_l"])
boneList["sk_hand_r"].AddChild( boneList["tip_sk_hand_r"])
boneList[boneNameList[parent]].AddChild(boneList[ask.bone[n].name])
#create the animation stack
def CreateAnimation(pScene, bstr, isLinearLerp=False):
"""
Reads each file and saves to animation stack
"""
lTime = FbxTime()
fpsInterval = 1.0/30.0
# First animation stack.
#lAnimStackName = "{}".format(gVars.currGaniHash)
lAnimStackName = "{}".format( GetStringHash(gVars.currGaniHash) )
#print lAnimStackName
lAnimStack = FbxAnimStack.Create(pScene, lAnimStackName)
# The animation nodes can only exist on AnimLayers therefore it is mandatory to
# add at least one AnimLayer to the AnimStack. And for the purpose of this example,
# one layer is all we need.
lAnimLayer = FbxAnimLayer.Create(pScene, "Base Layer")
lAnimStack.AddMember(lAnimLayer)
yPluse_one = True
lCurve_tX = None
lCurve_tY = None
lCurve_tZ = None
lCurve_rX = None
lCurve_rY = None
lCurve_rZ = None
printDebug = False
xx1, yy1, zz1 = [0.0, 0.0, 0.0]
hexDump = ""
for info in gVars.animChannelInfo:
#animChannel = AnimChannel(bstr, info)
frameIndex = 0
lim = info.addr + info.size
bstr.seek(info.addr)
if info.index == 5 or info.index == 10:
#skip IK bones
continue
if info.index == 6 or info.index == 11:
#skip IK bones
continue
if info.bitSz == 32 or info.bitSz == 16:
#print "{} [{}]\n".format(bstr.tell(), info.index)
pNode = boneList[trackBoneNameList[info.index]]
lCurve_tX = pNode.LclTranslation.GetCurve(lAnimLayer, "X", True)
lCurve_tY = pNode.LclTranslation.GetCurve(lAnimLayer, "Y", True)
lCurve_tZ = pNode.LclTranslation.GetCurve(lAnimLayer, "Z", True)
lCurve_tX.KeyModifyBegin()
lCurve_tY.KeyModifyBegin()
lCurve_tZ.KeyModifyBegin()
#Add first frame
while(bstr.tell() < lim and frameIndex <= gVars.currGaniFrameCount):
if info.bitSz == 32:
#single precision float
xx1, yy1, zz1 = struct.unpack("fff", bstr.read(12))
else:
#half precision float
hx,hy,hz = struct.unpack("HHH", bstr.read(6))
xx1 = ReadHalfFloat(hx)
yy1 = ReadHalfFloat(hy)
zz1 = ReadHalfFloat(hz)
if trackBoneNameList[info.index] == "sk_root_hip":
if yPluse_one:
yy1 = yy1 + 1 # hip_Pos.y 1
time = float(frameIndex) * fpsInterval
lTime.SetSecondDouble(time)
lKeyIndex = lCurve_tX.KeyAdd(lTime)[0]
lCurve_tX.KeySetValue(lKeyIndex, xx1)
lKeyIndex = lCurve_tY.KeyAdd(lTime)[0]
lCurve_tY.KeySetValue(lKeyIndex, yy1)
lKeyIndex = lCurve_tZ.KeyAdd(lTime)[0]
lCurve_tZ.KeySetValue(lKeyIndex, zz1)
if isLinearLerp:
lCurve_tX.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
lCurve_tY.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
lCurve_tZ.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
else:
lCurve_tX.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_tY.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_tZ.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
frameStep = int(struct.unpack("b", bstr.read(1))[0])
frameIndex += frameStep
if frameIndex < gVars.currGaniFrameCount: #no final frame?
time = float(gVars.currGaniFrameCount) * fpsInterval
lTime.SetSecondDouble(time)
lKeyIndex = lCurve_tX.KeyAdd(lTime)[0]
lCurve_tX.KeySetValue(lKeyIndex, xx1)
lKeyIndex = lCurve_tY.KeyAdd(lTime)[0]
lCurve_tY.KeySetValue(lKeyIndex, yy1)
lKeyIndex = lCurve_tZ.KeyAdd(lTime)[0]
lCurve_tZ.KeySetValue(lKeyIndex, zz1)
if isLinearLerp:
lCurve_tX.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
lCurve_tY.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
lCurve_tZ.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
else:
lCurve_tX.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_tY.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_tZ.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_tX.KeyModifyEnd()
lCurve_tY.KeyModifyEnd()
lCurve_tZ.KeyModifyEnd()
if printDebug:
print("[{}]. {}".format(info.index, trackBoneNameList[info.index]))
print("\tpos -> bitSz:{}\n\taddr:{} size:{}\n\tend:{}".format(info.bitSz, info.addr,info.size, bstr.tell()))
print("\t{} of {} frame(s)".format(frameIndex,gVars.currGaniFrameCount))
elif info.bitSz == 12:
pNode = None
try:
pNode = boneList[trackBoneNameList[info.index]]
except KeyError as e:
print(trackBoneNameList[info.index], info.index, e)
sys.exit(0)
lCurve_rX = pNode.LclRotation.GetCurve(lAnimLayer, "X", True)
lCurve_rY = pNode.LclRotation.GetCurve(lAnimLayer, "Y", True)
lCurve_rZ = pNode.LclRotation.GetCurve(lAnimLayer, "Z", True)
lCurve_rX.KeyModifyBegin()
lCurve_rY.KeyModifyBegin()
lCurve_rZ.KeyModifyBegin()
frameData = struct.unpack("{}B".format(info.size), bstr.read(info.size))
strD = ""
bn = 0
while(frameIndex < gVars.currGaniFrameCount):
q0, bn = ReadBitsToQuaternion(frameData, bn)
q = FbxQuaternion(q0[0], q0[1], q0[2], q0[3])
mat = FbxAMatrix()
mat.SetTQS(FbxVector4(0,0,0,0),q,FbxVector4(1,1,1,1))
r= mat.GetR()
xx1 = r[0]
yy1 = r[1]
zz1 = r[2]
time = float(frameIndex) * fpsInterval
lTime.SetSecondDouble(time)
lKeyIndex = lCurve_rX.KeyAdd(lTime)[0]
lCurve_rX.KeySetValue(lKeyIndex, xx1)
lKeyIndex = lCurve_rY.KeyAdd(lTime)[0]
lCurve_rY.KeySetValue(lKeyIndex, yy1)
lKeyIndex = lCurve_rZ.KeyAdd(lTime)[0]
lCurve_rZ.KeySetValue(lKeyIndex, zz1)
if isLinearLerp:
lCurve_rX.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
lCurve_rY.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
lCurve_rZ.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
else:
lCurve_rX.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_rY.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_rZ.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
frameStep, bn = ReadBitsToInt(frameData,bit_size=8,index=bn)
frameIndex += frameStep
if frameStep == 0:
strD = "0 step break"
break
strD = "End of frames break"
if frameIndex < gVars.currGaniFrameCount: #no final frame?
time = float(gVars.currGaniFrameCount) * fpsInterval
lTime.SetSecondDouble(time)
lKeyIndex = lCurve_rX.KeyAdd(lTime)[0]
lCurve_rX.KeySetValue(lKeyIndex, xx1)
lKeyIndex = lCurve_rY.KeyAdd(lTime)[0]
lCurve_rY.KeySetValue(lKeyIndex, yy1)
lKeyIndex = lCurve_rZ.KeyAdd(lTime)[0]
lCurve_rZ.KeySetValue(lKeyIndex, zz1)
if isLinearLerp:
lCurve_rX.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
lCurve_rY.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
lCurve_rZ.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationLinear)
else:
lCurve_rX.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_rY.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_rZ.KeySetInterpolation(lKeyIndex, FbxAnimCurveDef.eInterpolationCubic)
lCurve_rX.KeyModifyEnd()
lCurve_rY.KeyModifyEnd()
lCurve_rZ.KeyModifyEnd()
if printDebug:
print("[{}]. {}".format(info.index, trackBoneNameList[info.index]))
print("\trot -> bitSz:{}\n\taddr:{} size:{}\n\tend:{}".format(info.bitSz, info.addr,info.size, bstr.tell()))
print("\t{} of {} frame(s)".format(frameIndex,gVars.currGaniFrameCount))
print("\tlast bit index {} (Size = {}) => {}".format(bn, info.size * 8, strD))
t0 = FbxTime()
t1 = FbxTime()
t0.SetSecondDouble(fpsInterval * 0 )
t1.SetSecondDouble(fpsInterval * gVars.currGaniFrameCount )
lAnimStack.SetLocalTimeSpan(FbxTimeSpan(t0, t1))
def ReadBitsToQuaternion(frameData,index):
#print "bits to Quat", index
bn = index
#read value bits
num1, bn = ReadBitsToFloat(frameData, bit_size=12, index=bn)
num2, bn = ReadBitsToFloat(frameData, bit_size=12, index=bn)
num3, bn = ReadBitsToFloat(frameData, bit_size=12, index=bn)
#read sign bit
num4, bn = ReadBitsToInt(frameData, bit_size=1, index=bn)
num5, bn = ReadBitsToInt(frameData, bit_size=1, index=bn)
num6, bn = ReadBitsToInt(frameData, bit_size=1, index=bn)
num7 = 1.0 - num2 - num3
# python useS radians dont convert to degrees
num8 = 1.0 / math.sqrt((num2 * num2) + (num3 * num3) + (num7 * num7))
num9 = num1 #* 3.14159202575684 / 2.0 #radians to degrees
num10 = math.sin(num9) * num8
_i = num2 * num10
_j = num3 * num10
_k = num7 * num10
if num4 > 0:
_i = -_i
if num5 > 0:
_j = -_j
if num6 > 0:
_k = -_k
#x, y, z, w
return [_i, _j, _k, math.cos(num9)], bn
def ReadBitsToFloat(nBits, bit_size=-1, index = 0):
#converts to binary and returns value/total possible binary value
#print "bits to float", bit_size, index
num1 = 0.0
num2 = 1.0
bn = index
if bit_size == -1:
#checks if we are reading from persistent byte array and how much to read
bit_size = len(nBits)*8 #convert to bits
for i in range( bit_size ):
fByte = nBits[math.floor(bn/8)] #get byte to which our bit belongs to
if not (((fByte >> (bn % 8)) & 1) == 0): #get actual bit value
num1 += num2
bn += 1 #next bit
num2 *= 2.0
return (num1/ num2), bn
def ReadBitsToInt(nBits, bit_size=-1, index = 0):
#print "bits to int", bit_size, index
num1 = 0
num2 = 1
bn = index
if bit_size == -1:
#checks if we are reading from persistent byte array and how much to read
bit_size = len(nBits)*8 #convert to bits
for i in range( bit_size ):
fByte = nBits[math.floor(bn/8)] #get byte to which our bit belongs to
if not (((fByte >> (bn % 8)) & 1) == 0): #get actual bit value
num1 += num2
bn += 1 #next bit
num2 *= 2
return num1, bn
def ReadHalfFloat(data):
num1 = (data & 0x7C00)
if (num1 == 0x7C00):
return float('nan')
if num1 > 0 :
num1 = (num1 + 0x1A400) << 13
num1 |= ((data & 0x8000) << 16) | ((data & 0x3FF) << 13)
floathex = struct.pack('I', num1 )
return struct.unpack('f', floathex)[0]
def BuildSkeletonScene(dir):
# Prepare the FBX SDK.
(lSdkManager, lScene) = FbxCommon.InitializeSdkObjects()
# Create the scene.
lResult = CreateScene(lSdkManager, lScene, True)
if lResult == False:
print("\n\nAn error occurred while creating the scene...\n")
lSdkManager.Destroy()
sys.exit(1)
print("Skeleton File")
# Save the scene.
# A default output file name is given otherwise.
lResult = SaveScene(lSdkManager, lScene, dir)
if lResult == False:
print("\n\nAn error occurred while saving the scene...\n")
lSdkManager.Destroy()
sys.exit(1)
# Destroy all objects created by the FBX SDK.
lSdkManager.Destroy()
def SaveScene(pSdkManager, pScene, pFilename, pFileFormat = -1, pEmbedMedia = False):
lExporter = FbxExporter.Create(pSdkManager, "")
if pFileFormat < 0 or pFileFormat >= pSdkManager.GetIOPluginRegistry().GetWriterFormatCount():
pFileFormat = pSdkManager.GetIOPluginRegistry().GetNativeWriterFormat()
if not pEmbedMedia:
lFormatCount = pSdkManager.GetIOPluginRegistry().GetWriterFormatCount()
for lFormatIndex in range(lFormatCount):
if pSdkManager.GetIOPluginRegistry().WriterIsFBX(lFormatIndex):
lDesc = pSdkManager.GetIOPluginRegistry().GetWriterFormatDescription(lFormatIndex)
pFileFormat = pSdkManager.GetIOPluginRegistry().FindWriterIDByDescription("FBX binary (*.fbx)")
break
if not pSdkManager.GetIOSettings():
ios = FbxIOSettings.Create(pSdkManager, IOSROOT)
pSdkManager.SetIOSettings(ios)
pSdkManager.GetIOSettings().SetBoolProp(EXP_FBX_MATERIAL, False)
pSdkManager.GetIOSettings().SetBoolProp(EXP_FBX_TEXTURE, False)
pSdkManager.GetIOSettings().SetBoolProp(EXP_FBX_EMBEDDED, False)
pSdkManager.GetIOSettings().SetBoolProp(EXP_FBX_SHAPE, False)
pSdkManager.GetIOSettings().SetBoolProp(EXP_FBX_GOBO, False)
pSdkManager.GetIOSettings().SetBoolProp(EXP_FBX_ANIMATION, True)
pSdkManager.GetIOSettings().SetBoolProp(EXP_FBX_GLOBAL_SETTINGS, True)
result = lExporter.Initialize(pFilename, pFileFormat, pSdkManager.GetIOSettings())
if result == True:
result = lExporter.Export(pScene)
lExporter.Destroy()
return result
class AnimChannelInfo():
def __init__(self, bstr):
bac = bstr.tell()
addr = struct.unpack('I', bstr.read(4))[0]
self.addr = addr + bac #from offset to actual addr
self.index = struct.unpack('h', bstr.read(2))[0]
self.typeFlag = hex(struct.unpack('B', bstr.read(1))[0] & ~0x80)
self.bitSz = int(struct.unpack('b', bstr.read(1))[0])
self.size = 0
#print bac, addr, self.addr, self.index, self.typeFlag, self.bitSz
class GlobalVars():
def __init__(self):
self.currGaniHash = 0
self.currGaniHash2 = 0
self.currGaniAddr = 0
self.currGaniSize = 0
self.currMtarOffset = 0
self.currMtarIndex = 0
self.currGaniFrameCount = 0
self.trackGrpCnt = 0
self.totalChannelCount = 0
self.fpsInterval = 0
self.unknownC = 0
self.animChannelInfo = []
self.log = ""
gVars = GlobalVars()
def ReadGaniEntryHeader(bstr, mtarIndex):
#read current from file offset
gVars.currMtarIndex = mtarIndex
gVars.currGaniHash, gVars.unkn, gVars.currGaniAddr, gVars.currGaniSize = struct.unpack('IiII', bstr.read(16))
gVars.currMtarOffset = bstr.tell()
ReadGaniFile(bstr)
trackEntry = None
def ReadGaniFile(bstr):
global idDict
#Go to offset and start actual read
bstr.seek(gVars.currGaniAddr)
#bstr.seek(8,os.SEEK_CUR)
#sig, addr, filesize
hdr = struct.unpack('IIII', bstr.read(16))
if not hdr[2] == gVars.currGaniSize:
print("File IOError: filesize from mtar and gani file mis-match")
exit(1)
#cos padding is inconsistent from pes to MGS
bstr.seek(gVars.currGaniAddr + hdr[1], os.SEEK_SET) #32
gVars.currGaniHash2 = struct.unpack('I', bstr.read(4))[0] #+4 -> 36
#skip rest of MainGani header
bstr.seek(44, os.SEEK_CUR) #+44 -> 80
#skip MotionHdr (+64 -> 144)
#OLD: Skip whole MotionHdr (+64 -> 144) //112
motionAddr = bstr.tell() #80
bstr.seek(112, os.SEEK_SET )
id_ = struct.unpack('I', bstr.read(4))[0]
bstr.seek(motionAddr, os.SEEK_SET ) #90
bstr.seek(64, os.SEEK_CUR) #+64
motionAddr = bstr.tell()
gVars.trackGrpCnt, gVars.totalChannelCount, gVars.fpsInterval, gVars.currGaniFrameCount, gVars.unknownC = struct.unpack('IIIII', bstr.read(20))
gVars.log += "Hash: {}, unkn: {}, FrameCount: {}\tAddr: {}, Size: {}, unknC: {}\n".format(gVars.currGaniHash, gVars.unkn, gVars.currGaniFrameCount, gVars.currGaniAddr, gVars.currGaniSize, gVars.unknownC)
idDict[gVars.unkn] = "{} {}".format(GetStringHash(gVars.currGaniHash), id_)
trackAddr = [None] * gVars.trackGrpCnt
for t in range(gVars.trackGrpCnt):
trackAddr[t] = struct.unpack('I', bstr.read(4))[0]
#read channels
#print "read channel info"
gVars.animChannelInfo = []
for t in range(gVars.trackGrpCnt):
bstr.seek(motionAddr + trackAddr[t]) #go to track
trackHash = struct.unpack('I', bstr.read(4))[0]
channelCount = int(struct.unpack('B', bstr.read(1))[0])
bstr.seek(3, os.SEEK_CUR) #+3
for c in range(channelCount):
gVars.animChannelInfo.append( AnimChannelInfo(bstr))
#calculate size
#print "sizes:"
for c in range(gVars.totalChannelCount):
if c == (gVars.totalChannelCount-1):
#This is wrong.
gVars.animChannelInfo[c].size = (gVars.currGaniAddr + gVars.currGaniSize) - gVars.animChannelInfo[c].addr
else:
gVars.animChannelInfo[c].size = gVars.animChannelInfo[c+1].addr - gVars.animChannelInfo[c].addr
#print gVars.animChannelInfo[c].size
#Do actual FBX
if __name__ == "__main__":
try:
import FbxCommon
from fbx import *
except ImportError:
print("Error: module FbxCommon and/or fbx failed to import.\n")
print("Copy the files located in the compatible sub-folder lib/python<version> into your python interpreter site-packages folder.")
import platform
if platform.system() == 'Windows' or platform.system() == 'Microsoft':
print('For example: copy ..\\..\\lib\\Python27_x64\\* C:\\Python27\\Lib\\site-packages')
sys.exit(1)
lZero = FbxVector4(0,0,0)
lOne= FbxVector4(1,1,1)
BuildSkeletonScene(r"C:\MGSV_Tools\mtar_reader\mrdev_tool\outDir\skel.fbx") #("E:\\x360\\Pes\\skel_17.fbx")
inDir = [r"C:\MGSV_Tools\mtar_reader\mrdev_tool\inDir\\"]
outDir = [r"C:\MGSV_Tools\mtar_reader\mrdev_tool\outDir\\"]
#Create/Read Dictionary from file
hashDict = dict()
idDict = dict()
# f = open(r"C:\MGSV_Tools\mtar_reader\mrdev_tool\PES2017-Anim_HashStrings.txt", 'r')
# lines = f.readlines()
# f.close()
# for line in lines:
# line = line.lstrip().rstrip()
# k,v = line.split(' ',2)
# k = int(k)
# hashDict[k] = v
# print("Built string Dictionary")
def GetStringHash(k):
global hashDict
v = k
if k in hashDict:
v = hashDict[k]
#print "{} : {}".format(k,v)
return v
def HashToString(hash):
global hashDict
if hash in hashDict:
return hashDict[hash]
return ""+hash
testmode = False #True
if testmode:
for i in range(len(inDir)):
#remember we set the dir here
os.chdir(inDir[i])
print(inDir[i])
tempList = os.listdir(os.getcwd())
fileList = []
#obtain our list of motion archives
for t in tempList:
if t.endswith(".mtar"):
fileList.append(t)
print("\n\tprocessing...\n")
#read Mtar
for f in fileList:
bReader = open(f, "rb")
#read header
sig, fileCount = struct.unpack('II', bReader.read(8))
print(fileCount, " Animation Entries")
boneCount, channelCount = struct.unpack('hh', bReader.read(4))
bReader.seek(20, os.SEEK_CUR) #int padd[5]
outFile = outDir[i] + f.split(".mtar")[0]
#DoFbX
# Prepare the FBX SDK.
(lSdkManager, lScene) = FbxCommon.InitializeSdkObjects()
# Create the scene.
lResult = CreateScene(lSdkManager, lScene)
if lResult == False:
print("\n\nAn error occurred while creating the scene...\n")
lSdkManager.Destroy()
sys.exit(1)
outFName = "{}_test0.fbx".format(outFile)
print(outFName)
for idx in range(1): #range(10): #
ReadGaniEntryHeader(bReader,idx)
#covert animation
outp = CreateAnimation(lScene, bReader)
#End restore mtar offset
bReader.seek(gVars.currMtarOffset, os.SEEK_SET)
# Save the scene.
# A default output file name is given otherwise.
lResult = SaveScene(lSdkManager, lScene, outFName)
if lResult == False:
print("\n\nAn error occurred while saving the scene...\n")
lSdkManager.Destroy()
sys.exit(1)
# Destroy all objects created by the FBX SDK.
lSdkManager.Destroy()
bReader.close()
break
else:
for i in range(len(inDir)):
#remember we set the dir here
os.chdir(inDir[i])
print(inDir[i])
tempList = os.listdir(os.getcwd())
fileList = []
#obtain our list of motion archives
for t in tempList:
if t.endswith(".mtar"):
fileList.append(t)
print("\n\tprocessing...\n")
#read Mtar
for f in fileList:
bReader = open(f, "rb")
#read header
sig, fileCount = struct.unpack('II', bReader.read(8))
print(fileCount, " Animation Entries")
boneCount, channelCount = struct.unpack('hh', bReader.read(4))
bReader.seek(20, os.SEEK_CUR) #int padd[5]
subs = fileCount/ 100
outFile = outDir[i] + f.split(".mtar")[0]
for s in range(0,int(subs)+1):
#DoFbX
# Prepare the FBX SDK.
(lSdkManager, lScene) = FbxCommon.InitializeSdkObjects()
# Create the scene.
lResult = CreateScene(lSdkManager, lScene)
if lResult == False:
print("\n\nAn error occurred while creating the scene...\n")
lSdkManager.Destroy()
sys.exit(1)
sz = min( (s*100)+100, fileCount)
outFName = "{}_{:0>2}.fbx".format(outFile,s)
print(outFName)
for idx in range(s*100, sz): #range(1): #
ReadGaniEntryHeader(bReader,idx)
#covert animation
outp = CreateAnimation(lScene, bReader)
#End restore mtar offset
bReader.seek(gVars.currMtarOffset, os.SEEK_SET)
# Save the scene.
# A default output file name is given otherwise.
lResult = SaveScene(lSdkManager, lScene, outFName)
if lResult == False:
print("\n\nAn error occurred while saving the scene...\n")
lSdkManager.Destroy()
sys.exit(1)
# Destroy all objects created by the FBX SDK.
lSdkManager.Destroy()
bReader.close()
#gVars.log
f= open(r"C:\MGSV_Tools\mtar_reader\mrdev_tool\outDir\pes2017-AnimIDs.txt", 'w')
idKeys = list(idDict.keys())
idKeys.sort()
for id in idKeys:
f.write("{} {}\n".format(id, idDict[id]))
f.close()
@redtankbai
Copy link

could you plz share skel_17.fbx of PES 2017? i need that skeleton file for process, thx!

@Hazuki-san
Copy link
Author

Hazuki-san commented Jan 11, 2024

could you plz share skel_17.fbx of PES 2017? i need that skeleton file for process, thx!

@redtankbai Sadly, I don't have them and so does the MGSV modding community but I think I could generate them using PES2019 .ask file. Do you want me to?

@Hazuki-san
Copy link
Author

even though, I think it probably read the .ask file and auto generate them.

@redtankbai
Copy link

i met some issue when running this script, that's why i assume missing skeleton fbx is the key
do you have discord that we can talk more?

@redtankbai
Copy link

here is the thing

  1. i changed path in script
    C

  2. these files are in the path
    B

  3. run script. got error
    A

@redtankbai
Copy link

the skel.fbx is only thing i cannot cover, that's why i think maybe need that one from you

@Hazuki-san
Copy link
Author

@redtankbai I'm still trying to figure it out. But here's my Discord: eaglejump

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment