2021年3月29日 星期一

從Blender裡萃取模組資料(11)

 從Blender裡萃取模組資料(11)

前言

  在之前的 從Blender裡萃取模組資料(9) 裡有示範如何萃取 Texture 的資料,但 Blender 2.8 以後的 Material 採取 Node 編輯的方式設定,所以該範例的方法已無法在  Blender 2.8 以後使用,這次就來研究如何萃取,在此把學習的過程做個紀錄。


內容

  在開始之前請先將 Material 調成以下

新增 Texture 給 Base Color


這次需要示範如何萃取 Image Texture 的資料,所以要新增一張圖並連結到"Base Color"。這次的範例會示範如何萃取連結到"Base Color"接口的 Image Texture,具體的範例如下

import bpy
import bmesh

tagMesh = bpy.data.meshes["Cube"]
#Create bmesh from mesh
tagBMesh = bmesh.new()
tagBMesh.from_mesh(tagMesh)

#
def find_node_by_name( srcNode , nodeName):
    if srcNode.name == nodeName:
        return srcNode
    tagNode = None
    for socket in srcNode.inputs:
        for link in socket.links:
            tagNode = find_node_by_name( link.from_node , nodeName )
            if tagNode != None:
                break
        if tagNode != None:
            break
    #
    return tagNode
#
def find_node_from_node_socket( srcNode , socketName , nodeName):
    tagSocket = srcNode.inputs.get( socketName )
    if tagSocket == None:
        return None
    tagNode = None
    for link in tagSocket.links:
        tagNode = find_node_by_name( link.from_node , nodeName )
        if tagNode != None:
            break
    #
    return tagNode
#Start extract
matList = tagMesh.materials

for mat in matList:
    print( 'material name:' , mat.name )
    outputNode = mat.node_tree.nodes.get( 'Material Output' )
    if outputNode != None:
        principledBSDFNode = find_node_by_name( outputNode , 'Principled BSDF' )
        if principledBSDFNode != None:
            imageNode = find_node_from_node_socket( principledBSDFNode , 'Base Color' , 'Image Texture' )
            if imageNode != None:
                print( 'Image name:' , imageNode.image.name)
                print( 'Image width: ', imageNode.image.size[0] )
                print( 'Image height: ', imageNode.image.size[1] )
                print( 'Image format: ', imageNode.image.file_format )
                print( 'Image path: ' , imageNode.image.filepath_from_user() )
                print( 'Image Node interpolation:' , imageNode.interpolation )
                print( 'Image Node projection:' , imageNode.projection )
                print( 'Image Node extension:' , imageNode.extension )
                print( 'Image source:' , imageNode.image.source )
                print( 'Image color space:' , imageNode.image.colorspace_settings.name )
            else:
                #Show default value
                socket = principledBSDFNode.inputs.get( 'Base Color' )
                for val in socket.default_value:
                    print( 'data:' , val )
  
#Free bmesh...
tagBMesh.free()


範例裡有兩個工具 Function ,分別為"find_node_by_name"與"find_node_from_node_socket",第一個是基於某個 Node 來從所有的輸入接口尋找目標 Node ,第二則是基於某個 Node 的指定接口來尋找目標 Node 。萃取的過程比想像中簡單,透過 Material 裡的"node_tree"來取得輸出的 Node (Material Output),接著透過"find_node_by_name"來尋找"Principled BSDF" Node ,用工具 Function 來尋找是因為不知道中間會過多少個 Node ,接著就透過"find_node_from_node_socket"從"Base Color"接口來尋找"Image Texture",範例在找到"Image Texture"時就取得 Node 的相關資料,當沒取得時就取得"Base Color"的顏色資料(當該接口沒有被連結時)。


  在取得"Base Color"的顏色資料時是透過"default_value",但官方的說明文件上沒有這個屬性,在 [ blender.stackexchange.com ] PyNodes API: transferring data between nodes with sockets 裡我才找到有這個屬性可以提取。這次範例的工具 Function 都只找出第一個目標 Node ,無法解決多個目標 Node 的狀況。由於 Material 採取 Node 編輯,但由於每個遊戲引擎的 Node 規格不太一樣,就算詳細萃取的每個 Node 的資料也很難可以匯出給其它的遊戲引擎,所以只提供這樣不完整的萃取方法,如果這樣不夠用以後在補新的。

 

參考資料

[ docs.blender.org ] Blender 2.92.0 Python API Documentation

[ blender.stackexchange.com ] PyNodes API: transferring data between nodes with sockets


相關文章

從Blender裡萃取模組資料(9)

沒有留言:

張貼留言