FBX SDK PYTHON (2020)を使ってみる

まず、仮想環境を構築

マニュアルを見ると、パイソン3.10しか対応してないのぽいので、 
まず、仮想環境を構築してみます。

これはCtrl+Shift+pで構築します。
あらかじめパイソン3.10をインストールしてそのバージョンを選択してください。(直接そのフォルダに3.10を配置することができるのかもしれないがやり方がわからない)

おわったらVSCを再起動して
実際に仮想環境で3.10か確認する。

ちゃんと仮想環境で実行されてるようです。

sdkをインストール

次にサイトから

FBX SDK 2020.3.7 Python (exe - 2799Kb)
をインストールしてインストールされた中から、whlファイルをプロジェクトフォルダに移動します。

whlとは


whlファイルは単体のモジュールインストールファイルで、もしパイソンのプラットフォーム(つまり、3.10以外)だったらエラーが起こるので気をつけてください。


無事インストールされました。

念の為pip listで再起動した後、仮想環境でインストールされてるかも確認します。

問題なくクリーンな環境でfbxのモジュールがインストールサれてることがわかりました。便利ですね。仮想環境って。

では簡単なfbxのスクリプトを実行してみます。


オブジェクトの全てのノードが表示されるスクリプトです。


import fbx

def print_node_hierarchy(node, indent=0):
    print("  " * indent + node.GetName())
    for i in range(node.GetChildCount()):
        print_node_hierarchy(node.GetChild(i), indent + 1)

def main():
    # FBXマネージャーの作成
    fbx_manager = fbx.FbxManager.Create()
    if not fbx_manager:
        print("Error: Unable to create FBX manager!")
        return

    # FBXインポーターの作成
    fbx_importer = fbx.FbxImporter.Create(fbx_manager, "")

    # FBXファイルを読み込む
    if not fbx_importer.Initialize("190_スマホ用ジンバル.fbx"):
        print("Error: Unable to initialize FBX importer!")
        return

    # ノードを取得する
    fbx_scene = fbx.FbxScene.Create(fbx_manager, "MyScene")
    fbx_importer.Import(fbx_scene)

    root_node = fbx_scene.GetRootNode()
    if root_node:
        print("Root node:", root_node.GetName())
        print("Node hierarchy:")
        print_node_hierarchy(root_node)

    # インポーターを解放する
    fbx_importer.Destroy()

    # FBXマネージャーを解放する
    fbx_manager.Destroy()

if __name__ == "__main__":
    main()

結果

Root node: RootNode
Node hierarchy:
RootNode
  ジンバル
    ジンバル_006
      ジンバル_002
        ジンバル_005
          ジンバル_001
            ジンバル_007
              ジンバル_003
              ジンバル_004
              スマホ


問題なく出力されてます。

次にボーンの情報を習得するスクリプトをサンプルを参考に作ってみました。

import fbx
# 列挙型の定義

from DisplayCommon import *
from fbx import FbxSkeleton
from fbx import FbxNodeAttribute

def display(pNode):
    objtype=None
    if pNode.GetNodeAttribute() == None:
        print("NULL Node Attribute\n")
        objtype="NULL"

    else:
        lAttributeType = (pNode.GetNodeAttribute().GetAttributeType())

        if lAttributeType == FbxNodeAttribute.EType.eMarker:
            print("###type : eMarker")
            objtype="eMarker"
        elif lAttributeType == FbxNodeAttribute.EType.eSkeleton:
            print("###type : eSkeleton")
            objtype="eSkeleton"

        elif lAttributeType == FbxNodeAttribute.EType.eMesh:
            print("###type : eMesh")
            objtype="eMesh"

        elif lAttributeType == FbxNodeAttribute.EType.eNurbs:
            print("###type : eNurbs")
            objtype="eNurbs"

        elif lAttributeType == FbxNodeAttribute.EType.ePatch:
            print("###type : ePatch")
            objtype="ePatch"

        elif lAttributeType == FbxNodeAttribute.EType.eCamera:
            print("###type : eCamera")
            objtype="eCamera"

        elif lAttributeType == FbxNodeAttribute.EType.eLight:
            print("###type : eLight")
            objtype="eLight"

    return objtype

def DisplaySkeleton(pNode):
    objtype=display(pNode)
    lSkeleton = pNode.GetNodeAttribute()
    if objtype=="eSkeleton":
        DisplayString("Skeleton Name: ", pNode.GetName())

        lSkeletonTypes = [ "Root", "Limb", "Limb Node", "Effector" ]

        DisplayString("    Type: ", lSkeletonTypes[lSkeleton.GetSkeletonType().value])

        if lSkeleton.GetSkeletonType() == FbxSkeleton.EType.eLimb:
            DisplayDouble("    Limb Length: ", lSkeleton.LimbLength.Get())
        elif lSkeleton.GetSkeletonType() == FbxSkeleton.EType.eLimbNode:
            DisplayDouble("    Limb Node Size: ", lSkeleton.Size.Get())
        elif lSkeleton.GetSkeletonType() == FbxSkeleton.EType.eRoot:
            DisplayDouble("    Limb Root Size: ", lSkeleton.Size.Get())

        DisplayColor("    Color: ", lSkeleton.GetLimbNodeColor())

    else:
        pass



    

def process_node_hierarchy(node):
    DisplaySkeleton(node)
    for i in range(node.GetChildCount()):
        process_node_hierarchy(node.GetChild(i))

def main():
    # FBXマネージャーの作成
    fbx_manager = fbx.FbxManager.Create()
    if not fbx_manager:
        print("Error: Unable to create FBX manager!")
        return

    # FBXインポーターの作成
    fbx_importer = fbx.FbxImporter.Create(fbx_manager, "")

    # FBXファイルを読み込む
    if not fbx_importer.Initialize("body.fbx"):
        print("Error: Unable to initialize FBX importer!")
        return

    # ノードを取得する
    fbx_scene = fbx.FbxScene.Create(fbx_manager, "MyScene")
    fbx_importer.Import(fbx_scene)

    root_node = fbx_scene.GetRootNode()
    if root_node:
        print("Node and Attribute types:")
        process_node_hierarchy(root_node)

    # インポーターを解放する
    fbx_importer.Destroy()

    # FBXマネージャーを解放する
    fbx_manager.Destroy()

if __name__ == "__main__":
    main()


サンプルを利用してるのですが、DisplayCommon.pyの中身はこれです。

"""

 Copyright (C) 2001 - 2010 Autodesk, Inc. and/or its licensors.
 All Rights Reserved.

 The coded instructions, statements, computer programs, and/or related material 
 (collectively the "Data") in these files contain unpublished information 
 proprietary to Autodesk, Inc. and/or its licensors, which is protected by 
 Canada and United States of America federal copyright law and by international 
 treaties. 
 
 The Data may not be disclosed or distributed to third parties, in whole or in
 part, without the prior written consent of Autodesk, Inc. ("Autodesk").

 THE DATA IS PROVIDED "AS IS" AND WITHOUT WARRANTY.
 ALL WARRANTIES ARE EXPRESSLY EXCLUDED AND DISCLAIMED. AUTODESK MAKES NO
 WARRANTY OF ANY KIND WITH RESPECT TO THE DATA, EXPRESS, IMPLIED OR ARISING
 BY CUSTOM OR TRADE USAGE, AND DISCLAIMS ANY IMPLIED WARRANTIES OF TITLE, 
 NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE OR USE. 
 WITHOUT LIMITING THE FOREGOING, AUTODESK DOES NOT WARRANT THAT THE OPERATION
 OF THE DATA WILL BE UNINTERRUPTED OR ERROR FREE. 
 
 IN NO EVENT SHALL AUTODESK, ITS AFFILIATES, PARENT COMPANIES, LICENSORS
 OR SUPPLIERS ("AUTODESK GROUP") BE LIABLE FOR ANY LOSSES, DAMAGES OR EXPENSES
 OF ANY KIND (INCLUDING WITHOUT LIMITATION PUNITIVE OR MULTIPLE DAMAGES OR OTHER
 SPECIAL, DIRECT, INDIRECT, EXEMPLARY, INCIDENTAL, LOSS OF PROFITS, REVENUE
 OR DATA, COST OF COVER OR CONSEQUENTIAL LOSSES OR DAMAGES OF ANY KIND),
 HOWEVER CAUSED, AND REGARDLESS OF THE THEORY OF LIABILITY, WHETHER DERIVED
 FROM CONTRACT, TORT (INCLUDING, BUT NOT LIMITED TO, NEGLIGENCE), OR OTHERWISE,
 ARISING OUT OF OR RELATING TO THE DATA OR ITS USE OR ANY OTHER PERFORMANCE,
 WHETHER OR NOT AUTODESK HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS
 OR DAMAGE. 
 
"""

def DisplayString(pHeader, pValue="" , pSuffix=""):
    lString = pHeader
    lString += str(pValue)
    lString += pSuffix
    print(lString)

def DisplayBool(pHeader, pValue, pSuffix=""):
    lString = pHeader
    if pValue:
        lString += "true"
    else:
        lString += "false"
    lString += pSuffix
    print(lString)

def DisplayInt(pHeader, pValue, pSuffix=""):
    lString = pHeader
    lString += str(pValue)
    lString += pSuffix
    print(lString)

def DisplayDouble(pHeader, pValue, pSuffix=""):
    print("%s%f%s" % (pHeader, pValue, pSuffix))

def Display2DVector(pHeader, pValue, pSuffix=""):
    print("%s%f, %f%s" % (pHeader, pValue[0], pValue[1], pSuffix))

def Display3DVector(pHeader, pValue, pSuffix=""):
    print("%s%f, %f, %f%s" % (pHeader, pValue[0], pValue[1], pValue[2], pSuffix))

def Display4DVector(pHeader, pValue, pSuffix=""):
    print("%s%f, %f, %f, %f%s" % (pHeader, pValue[0], pValue[1], pValue[2], pValue[3], pSuffix))

def DisplayColor(pHeader, pValue, pSuffix=""):
    print("%s%f (red), %f (green), %f (blue)%s" % (pHeader, pValue.mRed, pValue.mGreen, pValue.mBlue, pSuffix))


こういった情報がずらっと出てきます。

###type : eSkeleton
Skeleton Name: right_skateC1_bb_
    Type: Limb Node
    Limb Node Size: 33.333333
    Color: 0.800000 (red), 0.800000 (green), 0.800000 (blue)
###type : eSkeleton
Skeleton Name: right_skateC_end_bb_
    Type: Limb Node
    Limb Node Size: 33.333333
    Color: 0.800000 (red), 0.800000 (green), 0.800000 (blue)
###type : eMesh


注意点として、ネットの情報だとスケルトンなどのオブジェクトタイプは

fbx.FbxNodeAttribute.eSkeleton:


などとチャットGPTなどで解答しますがFbxNodeAttributeの後は列挙型のクラスが続くので、厳密には

fbx.FbxNodeAttribute.EType.eSkeleton

といった感じで定義しましょう。(ETypeを入れるだけ)


とりあえず、サンプルを見ることをおすすめします。
サンプルはモジュールが入ってるexeを解凍したらドキュメントと一緒に入ってます。