Last active
January 11, 2024 08:12
-
-
Save Hazuki-san/50e5ad0860d3b5e276eddf41bc2e570f to your computer and use it in GitHub Desktop.
File is from Modders' Heaven; MGSV modding community
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
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?
even though, I think it probably read the .ask file and auto generate them.
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?
the skel.fbx is only thing i cannot cover, that's why i think maybe need that one from you
@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
could you plz share skel_17.fbx of PES 2017? i need that skeleton file for process, thx!