Python Fbx Sdk で立方体の作成

環境

python3.9 Fbx Sdk

概要

Python SDK 2020.3.2のsamplesのExportScene03.pyから立方体の作成部分を抽出したものです

サンプルのコードだとマテリアルが出力されないので修正

CreateTexture()の呼び出し位置をNodeに接続した後に変えています

このコードだと頂点数の多いよくわからない立方体ができるので、以下のリンクも参照する

Houdini ApprenticeでFbx出力 - テキトープログラム( ..)φメモ

import FbxCommon
from fbx import *

def CreateTexture(pSdkManager, pMesh):
    lMaterial = None

    lNode = pMesh.GetNode()
    lMaterial = lNode.GetSrcObject(FbxCriteria.ObjectType(FbxSurfacePhong.ClassId), 0)
    if not lMaterial:
        lMaterialName = "toto"
        lShadingName  = "Phong"
        lBlack = FbxDouble3(0.0, 0.0, 0.0)
        lRed = FbxDouble3(1.0, 0.0, 0.0)
        lDiffuseColor = FbxDouble3(0.75, 0.75, 0.0)
        lMaterial = FbxSurfacePhong.Create(pSdkManager, lMaterialName)

        lMaterial.Emissive.Set(lBlack)
        lMaterial.Ambient.Set(lRed)
        lMaterial.AmbientFactor.Set(1.)

        lMaterial.Diffuse.Set(lDiffuseColor)
        lMaterial.DiffuseFactor.Set(1.)
        lMaterial.TransparencyFactor.Set(0.4)
        lMaterial.ShadingModel.Set(lShadingName)
        lMaterial.Shininess.Set(0.5)
        lMaterial.Specular.Set(lBlack)
        lMaterial.SpecularFactor.Set(0.3)

        lNode.AddMaterial(lMaterial)

    lTexture = FbxFileTexture.Create(pSdkManager,"Diffuse Texture")

    lTexture.SetFileName("scene03.jpg") # Resource file is in current directory.
    lTexture.SetTextureUse(FbxTexture.eStandard)
    lTexture.SetMappingType(FbxTexture.eUV)
    lTexture.SetMaterialUse(FbxFileTexture.eModelMaterial)
    lTexture.SetSwapUV(False)
    lTexture.SetTranslation(0.0, 0.0)
    lTexture.SetScale(1.0, 1.0)
    lTexture.SetRotation(0.0, 0.0)

    lMaterial.Diffuse.ConnectSrcObject(lTexture)

    lTexture = FbxFileTexture.Create(pSdkManager,"Ambient Texture")

    lTexture.SetFileName("gradient.jpg") # Resource file is in current directory.
    lTexture.SetTextureUse(FbxTexture.eStandard)
    lTexture.SetMappingType(FbxTexture.eUV)
    lTexture.SetMaterialUse(FbxFileTexture.eModelMaterial)
    lTexture.SetSwapUV(False)
    lTexture.SetTranslation(0.0, 0.0)
    lTexture.SetScale(1.0, 1.0)
    lTexture.SetRotation(0.0, 0.0)

    lMaterial.Ambient.ConnectSrcObject(lTexture)

    lTexture = FbxFileTexture.Create(pSdkManager,"Emissive Texture")

    lTexture.SetFileName("spotty.jpg") # Resource file is in current directory.
    lTexture.SetTextureUse(FbxTexture.eStandard)
    lTexture.SetMappingType(FbxTexture.eUV)
    lTexture.SetMaterialUse(FbxFileTexture.eModelMaterial)
    lTexture.SetSwapUV(False)
    lTexture.SetTranslation(0.0, 0.0)
    lTexture.SetScale(1.0, 1.0)
    lTexture.SetRotation(0.0, 0.0)

    lMaterial.Emissive.ConnectSrcObject(lTexture)

def CreateCubeWithTexture(pSdkManager, pName):
    lMesh = FbxMesh.Create(pSdkManager,pName)

    lControlPoint0 = FbxVector4(-50, 0, 50)
    lControlPoint1 = FbxVector4(50, 0, 50)
    lControlPoint2 = FbxVector4(50, 100, 50)
    lControlPoint3 = FbxVector4(-50, 100, 50)
    lControlPoint4 = FbxVector4(-50, 0, -50)
    lControlPoint5 = FbxVector4(50, 0, -50)
    lControlPoint6 = FbxVector4(50, 100, -50)
    lControlPoint7 = FbxVector4(-50, 100, -50)
    
    lNormalXPos = FbxVector4(1, 0, 0)
    lNormalXNeg = FbxVector4(-1, 0, 0)
    lNormalYPos = FbxVector4(0, 1, 0)
    lNormalYNeg = FbxVector4(0, -1, 0)
    lNormalZPos = FbxVector4(0, 0, 1)
    lNormalZNeg = FbxVector4(0, 0, -1)
    
    lMesh.InitControlPoints(24)
    lMesh.SetControlPointAt(lControlPoint0, 0)
    lMesh.SetControlPointAt(lControlPoint1, 1)
    lMesh.SetControlPointAt(lControlPoint2, 2)
    lMesh.SetControlPointAt(lControlPoint3, 3)
    lMesh.SetControlPointAt(lControlPoint1, 4)
    lMesh.SetControlPointAt(lControlPoint5, 5)
    lMesh.SetControlPointAt(lControlPoint6, 6)
    lMesh.SetControlPointAt(lControlPoint2, 7)
    lMesh.SetControlPointAt(lControlPoint5, 8)
    lMesh.SetControlPointAt(lControlPoint4, 9)
    lMesh.SetControlPointAt(lControlPoint7, 10)
    lMesh.SetControlPointAt(lControlPoint6, 11)
    lMesh.SetControlPointAt(lControlPoint4, 12)
    lMesh.SetControlPointAt(lControlPoint0, 13)
    lMesh.SetControlPointAt(lControlPoint3, 14)
    lMesh.SetControlPointAt(lControlPoint7, 15)
    lMesh.SetControlPointAt(lControlPoint3, 16)
    lMesh.SetControlPointAt(lControlPoint2, 17)
    lMesh.SetControlPointAt(lControlPoint6, 18)
    lMesh.SetControlPointAt(lControlPoint7, 19)
    lMesh.SetControlPointAt(lControlPoint1, 20)
    lMesh.SetControlPointAt(lControlPoint0, 21)
    lMesh.SetControlPointAt(lControlPoint4, 22)
    lMesh.SetControlPointAt(lControlPoint5, 23)
    
    # Set the normals on Layer 0.
    lLayer = lMesh.GetLayer(0)
    if lLayer == None:
        lMesh.CreateLayer()
        lLayer = lMesh.GetLayer(0)

    # We want to have one normal for each vertex (or control point),
    # so we set the mapping mode to eByControlPoint.
    lLayerElementNormal= FbxLayerElementNormal.Create(lMesh, "")
    lLayerElementNormal.SetMappingMode(FbxLayerElement.eByControlPoint)
    
    # Here are two different ways to set the normal values.
    firstWayNormalCalculations = True
    if firstWayNormalCalculations:
        # The first method is to set the actual normal value
        # for every control point.
        lLayerElementNormal.SetReferenceMode(FbxLayerElement.eDirect)

        lLayerElementNormal.GetDirectArray().Add(lNormalZPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalZPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalZPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalZPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalXPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalXPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalXPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalXPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalZNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalZNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalZNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalZNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalXNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalXNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalXNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalXNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalYPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalYPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalYPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalYPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalYNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalYNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalYNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalYNeg)
    else:
        # The second method is to the possible values of the normals
        # in the direct array, and set the index of that value
        # in the index array for every control point.
        lLayerElementNormal.SetReferenceMode(FbxLayerElement.eIndexToDirect)

        # Add the 6 different normals to the direct array
        lLayerElementNormal.GetDirectArray().Add(lNormalZPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalXPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalZNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalXNeg)
        lLayerElementNormal.GetDirectArray().Add(lNormalYPos)
        lLayerElementNormal.GetDirectArray().Add(lNormalYNeg)

        # Now for each control point, we need to specify which normal to use
        lLayerElementNormal.GetIndexArray().Add(0) # index of lNormalZPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(0) # index of lNormalZPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(0) # index of lNormalZPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(0) # index of lNormalZPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(1) # index of lNormalXPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(1) # index of lNormalXPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(1) # index of lNormalXPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(1) # index of lNormalXPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(2) # index of lNormalZNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(2) # index of lNormalZNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(2) # index of lNormalZNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(2) # index of lNormalZNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(3) # index of lNormalXNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(3) # index of lNormalXNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(3) # index of lNormalXNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(3) # index of lNormalXNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(4) # index of lNormalYPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(4) # index of lNormalYPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(4) # index of lNormalYPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(4) # index of lNormalYPos in the direct array.
        lLayerElementNormal.GetIndexArray().Add(5) # index of lNormalYNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(5) # index of lNormalYNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(5) # index of lNormalYNeg in the direct array.
        lLayerElementNormal.GetIndexArray().Add(5) # index of lNormalYNeg in the direct array.

    lLayer.SetNormals(lLayerElementNormal)
    
    # Array of polygon vertices.
    lPolygonVertices = ( 0, 1, 2, 3,
        4, 5, 6, 7,
        8, 9, 10, 11,
        12, 13, 14, 15,
        16, 17, 18, 19,
        20, 21, 22, 23 )

    # Set texture mapping for diffuse channel.
    lTextureDiffuseLayer=FbxLayerElementTexture.Create(lMesh, "Diffuse Texture")
    lTextureDiffuseLayer.SetMappingMode(FbxLayerElement.eByPolygon)
    lTextureDiffuseLayer.SetReferenceMode(FbxLayerElement.eIndexToDirect)
    lLayer.SetTextures(FbxLayerElement.eTextureDiffuse, lTextureDiffuseLayer)

    # Set texture mapping for ambient channel.
    lTextureAmbientLayer=FbxLayerElementTexture.Create(lMesh, "Ambient Textures")
    lTextureAmbientLayer.SetMappingMode(FbxLayerElement.eByPolygon)
    lTextureAmbientLayer.SetReferenceMode(FbxLayerElement.eIndexToDirect)
    lLayer.SetTextures(FbxLayerElement.eTextureAmbient, lTextureAmbientLayer)

    # Set texture mapping for emissive channel.
    lTextureEmissiveLayer=FbxLayerElementTexture.Create(lMesh, "Emissive Textures")
    lTextureEmissiveLayer.SetMappingMode(FbxLayerElement.eByPolygon)
    lTextureEmissiveLayer.SetReferenceMode(FbxLayerElement.eIndexToDirect)
    lLayer.SetTextures(FbxLayerElement.eTextureEmissive, lTextureEmissiveLayer)

    # Create UV for Diffuse channel
    lUVDiffuseLayer = FbxLayerElementUV.Create(lMesh, "DiffuseUV")
    lUVDiffuseLayer.SetMappingMode(FbxLayerElement.eByPolygonVertex)
    lUVDiffuseLayer.SetReferenceMode(FbxLayerElement.eIndexToDirect)
    lLayer.SetUVs(lUVDiffuseLayer, FbxLayerElement.eTextureDiffuse)

    lVectors0 = FbxVector2(0, 0)
    lVectors1 = FbxVector2(1, 0)
    lVectors2 = FbxVector2(1, 1)
    lVectors3 = FbxVector2(0, 1)

    lUVDiffuseLayer.GetDirectArray().Add(lVectors0)
    lUVDiffuseLayer.GetDirectArray().Add(lVectors1)
    lUVDiffuseLayer.GetDirectArray().Add(lVectors2)
    lUVDiffuseLayer.GetDirectArray().Add(lVectors3)

    # Create UV for Ambient channel
    lUVAmbientLayer=FbxLayerElementUV.Create(lMesh, "AmbientUV")

    lUVAmbientLayer.SetMappingMode(FbxLayerElement.eByPolygonVertex)
    lUVAmbientLayer.SetReferenceMode(FbxLayerElement.eIndexToDirect)
    lLayer.SetUVs(lUVAmbientLayer, FbxLayerElement.eTextureAmbient)

    lVectors0.Set(0, 0)
    lVectors1.Set(1, 0)
    lVectors2.Set(0, 0.418586879968643)
    lVectors3.Set(1, 0.418586879968643)

    lUVAmbientLayer.GetDirectArray().Add(lVectors0)
    lUVAmbientLayer.GetDirectArray().Add(lVectors1)
    lUVAmbientLayer.GetDirectArray().Add(lVectors2)
    lUVAmbientLayer.GetDirectArray().Add(lVectors3)

    # Create UV for Emissive channel
    lUVEmissiveLayer=FbxLayerElementUV.Create(lMesh, "EmissiveUV")

    lUVEmissiveLayer.SetMappingMode(FbxLayerElement.eByPolygonVertex)
    lUVEmissiveLayer.SetReferenceMode(FbxLayerElement.eIndexToDirect)
    lLayer.SetUVs(lUVEmissiveLayer, FbxLayerElement.eTextureEmissive)

    lVectors0.Set(0.2343, 0)
    lVectors1.Set(1, 0.555)
    lVectors2.Set(0.333, 0.999)
    lVectors3.Set(0.555, 0.666)

    lUVEmissiveLayer.GetDirectArray().Add(lVectors0)
    lUVEmissiveLayer.GetDirectArray().Add(lVectors1)
    lUVEmissiveLayer.GetDirectArray().Add(lVectors2)
    lUVEmissiveLayer.GetDirectArray().Add(lVectors3)

    #Now we have set the UVs as eINDEX_TO_DIRECT reference and in eBY_POLYGON_VERTEX  mapping mode
    #we must update the size of the index array.
    lUVDiffuseLayer.GetIndexArray().SetCount(24)
    lUVEmissiveLayer.GetIndexArray().SetCount(24)
    lUVAmbientLayer.GetIndexArray().SetCount(24)

    #in the same way we with Textures, but we are in eBY_POLYGON and as we are doing a cube,
    #we should have 6 polygons (1 for each faces of the cube)
    lTextureDiffuseLayer.GetIndexArray().SetCount(6)
    lTextureAmbientLayer.GetIndexArray().SetCount(6)
    lTextureEmissiveLayer.GetIndexArray().SetCount(6)

    # Create polygons. Assign texture and texture UV indices.
    for i in range(6):
        #we won't use the default way of assigning textures, as we have
        #textures on more than just the default (diffuse) channel.
        lMesh.BeginPolygon(-1, -1, False)

        #Here we set the the index array for each channel
        lTextureDiffuseLayer.GetIndexArray().SetAt(i,0)
        lTextureAmbientLayer.GetIndexArray().SetAt(i,0)
        lTextureEmissiveLayer.GetIndexArray().SetAt(i,0)

        for j in range(4):
            #this function points 
            lMesh.AddPolygon(lPolygonVertices[i*4 + j]) # Control point index. 
            
            #Now we have to update the index array of the UVs for diffuse, ambient and emissive
            lUVDiffuseLayer.GetIndexArray().SetAt(i*4+j, j)
            lUVAmbientLayer.GetIndexArray().SetAt(i*4+j, j)
            lUVEmissiveLayer.GetIndexArray().SetAt(i*4+j, j)

        lMesh.EndPolygon()

    
    lNode = FbxNode.Create(pSdkManager,pName)
    lNode.SetNodeAttribute(lMesh)
    lNode.SetShadingMode(FbxNode.eTextureShading)
    CreateTexture(pSdkManager, lMesh)
    return lNode

def SetCubeDefaultPosition(pCube):
    pCube.LclTranslation.Set(FbxDouble3(-75.0, -50.0, 0.0))
    pCube.LclRotation.Set(FbxDouble3(0.0, 0.0, 0.0))
    pCube.LclScaling.Set(FbxDouble3(1.0, 1.0, 1.0))

def CreateScene(pSdkManager, pScene, pSampleFileName):
    lCube = CreateCubeWithTexture(pSdkManager, "Cube")
    SetCubeDefaultPosition(lCube)
    lRootNode = pScene.GetRootNode()
    lRootNode.AddChild(lCube)

def main():
    lSampleFileName = "D:/user/houdini/createBoxFbx.fbx"
    (lSdkManager, lScene) = FbxCommon.InitializeSdkObjects()
    lResult = CreateScene(lSdkManager, lScene, lSampleFileName)
    lResult = FbxCommon.SaveScene(lSdkManager, lScene, lSampleFileName)
    lSdkManager.Destroy()

main()