跳轉至內容

Blender 3D:新手到專業/高階教程/Python指令碼/匯出指令碼

來自Wikibooks,開放世界中的開放書籍

此頁面是最初為Blender 2.44構建的優秀教程的更新。

Blender不僅可用於建立完整的動畫,它還是一個很棒的建模器。你可以在Blender中構建完整的3D場景,然後將其匯出為有用的格式。事實上,你可以用它做更多的事情,例如我將其用作其他人制作的免費2D遊戲的關卡編輯器。遊戲需要在短時間內完成,但在截止日期前的兩週,仍然沒有它的關卡編輯器。它有一個自定義的ASCII關卡格式,由材料、頂點、三角形和物件的列表組成。因此,我記起了Blender Python匯出器,自願編寫了一個Blender匯出指令碼,以便可以用作關卡編輯器。結果非常好,Blender現在可以完全用作該遊戲的關卡編輯器。

在本教程中,我們將學習如何為Blender編寫一個簡單的Python匯出指令碼。無需任何Python基礎知識,它將解釋如何查詢場景中的物件,以及如何將它們寫入檔案。它還將透過展示如何在匯出時處理資料來演示匯出指令碼的實用性,從而實現使用任何其他現有格式都無法實現的功能。

因此,開啟Blender,確保載入預設場景,然後開始吧…

瞭解場景中的事物

[編輯 | 編輯原始碼]
Blender259 EditorSelector
Blender259編輯器選擇器

在我們可以匯出任何東西之前,我們必須知道要匯出什麼。獲取此資訊的一種方法是“大綱”視窗(SHIFT-F9)。它將列出Blender當前知道的所有內容。現在,我們希望從指令碼中獲取相同的資訊。點選編輯器選擇按鈕,然後從彈出選單中選擇Python控制檯

現在,你已準備好迎接激動人心的時刻,你即將執行第一個Blender指令碼命令。鍵入以下內容並按回車鍵(或者你可以在指令碼視窗的頂部輸入匯入Blender,然後在它下面的這些行中,在所有“dir(x)”行之前加上print,並選擇檔案->執行)

list(bpy.data.objects)

結果,你應該會看到如下內容

[bpy.data.objects["Camera"], bpy.data.objects["Cube"], bpy.data.objects["Lamp"]]

現在,剛剛發生了什麼?在“list(bpy.data.objects)”中賦予“list”的變數由三個單片語成,用兩個點隔開。點將不同的東西分隔開來。第一個,bpy,表示使用來自bpy模組的函式。data是Blender的子模組。最後,objects是bpy.data.的迭代器。list()函式用於迴圈遍歷bpy.data.objects中的所有資料,並將其作為所有可用物件的列表返回。在我們的例子中,這是一個相機、一個立方體和一個燈。

要獲取有關物件的更多資訊,可以使用物件名稱作為bpy.data.objects中的鍵,並將其分配給變數,如下所示

camera = bpy.data.objects["Camera"]
cube = bpy.data.objects["Cube"]
lamp = bpy.data.objects["Lamp"]

我們剛剛將三個物件分配給了三個變數:camera、cube和lamp。要檢視變數的內容,只需鍵入其名稱即可

cube 
bpy.data.objects['Cube']
camera 
bpy.data.objects['Camera']
lamp 
bpy.data.objects['Lamp']

有時使用Python的dir()函式獲取有關物件的更多資訊會很有用。例如

dir(cube)

將寫入物件的所有函式和屬性的名稱。相當多。但不用擔心,很快你就會知道如何使用它們。你可能還想找出某物的型別,你可以這樣做

type(cube)

在這種情況下,只需鍵入“cube”即可顯示型別,但在實際指令碼中,你將使用type()。另一個可能有用的功能是檢視Python物件的文件。為此,在變數或物件上使用help()函式。

help(bpy.data.objects)

這將列印我們使用的bpy.data.objects函式的文件。當然,檢視文件的更簡單方法是使用線上HTML幫助。點選幫助->Python API參考。希望現在你的瀏覽器開啟並顯示Blender Python API的線上文件。如果沒有,你也可以在這裡找到它

http://www.blender.org/documentation/blender_python_api_2_59_2/

在文件中,點選bpy,然後點選data,你可以看到更多示例。當你需要在指令碼中執行教程中未涉及的操作時,使用文件將變得絕對至關重要。你將需要這樣做,否則你根本不想學習指令碼編寫。

根據你在指令碼編寫方面走多遠,你將需要另一個資源,即Python參考

https://docs.python.club.tw/

對於本教程,也許可以閱讀python文件中的“教程”部分,但即使不閱讀,你也能理解所有內容。

現在,讓我們嘗試更多地瞭解我們的立方體。鍵入

cube.type

它將告訴我們立方體實際上是Blender中的網格物件。在線上文件中查詢“type”。由於變數cube儲存了一個物件,而“type”是該物件的屬性,因此點選物件。在那裡,你會找到它的“type”。

現在我們知道立方體是網格,讓我們進一步瞭解網格。

cubedata = bpy.data["Cube"]

'注意:在Blender 2.6.0中,指令碼命令實際上是這樣的,但上述命令仍然有效:(Chronus001)

'注意:在Blender 2.6.5中,上述命令不再有效

cubedata = bpy.data.meshes['Cube']

每個Blender物件都分配有資料,具體取決於型別。對於網格,資料型別為Mesh。在文件中,再次回到頂部,查詢Mesh模組。它將包含Mesh型別的文件。你還可以嘗試

dir(cubedata)

以瞭解可用的函式和屬性。試試這些

list(cubedata.vertices)
list(cubedata.faces)

第一行將列出立方體網格的8個頂點。第二行將列出它的6個面。


注意:在Blender 2.77(以及可能接下來的版本中),cubedata.faces成員已被cubedata.polygons替換。


要獲取面的數量(或多邊形),以下命令可以完成任務 

print(len(cubedata.faces))

或者

print(len(cubedata.polygons))


要從列表中獲取成員,你需要在方括號中指定索引,從0開始。所以

v = cubedata.vertices[0]

這將把立方體的第一個頂點分配給變數v。到目前為止,你已經知道如何使用dir()獲取v中可能有趣的事物的列表,使用type()瞭解其型別,以及在哪裡查詢API文件。它在Blender/Mesh模組中,當你點選“類”下的一個“MVert”時。

v.co

這將顯示第一個頂點的3D座標。現在,如果我們想知道所有頂點的座標怎麼辦?當然,我們可以將它們全部分配給一個變數,但真正的做法是使用迴圈結構。有很多方法可以做到這一點,但一種簡單的方法如下所示

for v in cubedata.vertices: print(v.co)

for variable in list:結構將列表的每個元素依次分配給變數,然後執行冒號後面的命令,變數具有特定列表元素的值。在實際指令碼中,冒號後面會有不止一條命令 - 因此你將在接下來的行中編寫它們。

到目前為止,你應該已經瞭解了足夠的內容,可以在下一節中嘗試編寫真正的指令碼。

建立指令碼

[編輯 | 編輯原始碼]

你可以在外部文字編輯器或Blender內建的文字編輯器中編寫指令碼。移動到要更改為文字編輯器的面板,點選編輯器選擇按鈕並選擇“文字編輯器”(快捷鍵)SHIFT+F11。點選底部的+新建按鈕。如果需要,你可以使用底部的按鈕啟用行號和語法著色。使用檔案新建建立一個新指令碼,將下面的程式碼貼上到其中,並儲存。或者,將下面的程式碼貼上到檔案中,然後在Blender中使用檔案開啟開啟該檔案。作為名稱,選擇以.py為副檔名的名稱,例如wikibooks.py。將其放入Blender的使用者指令碼路徑。

對於不同的作業系統,路徑如下:

  • Linux:~/.blender/scripts
  • Windows XP:C:\Program Files\Blender Foundation\Blender\.blender\scripts
  • Windows XP(備選):C:\Documents and Settings\USERNAME\Application Data\Blender Foundation\Blender\.blender\scripts


  • Windows Vista:C:\Users\USERNAME\AppData\Roaming\Blender Foundation\Blender\.blender\scripts
  • Mac OS X
    • 在Mac OSX下,路徑實際上隱藏在blender.app中,因此要了解路徑,你必須知道指令碼資料夾實際上隱藏在blender.app本身中。假設Blender位於應用程式目錄中,則路徑為“/Applications/blender/blender.app/Contents/MacOS/.blender/scripts”如果你嘗試從Finder中開啟.app內容,你會注意到路徑的.blender部分不可見,但Blender仍然可以導航到此資料夾。

    • 右鍵點選(或按住ctrl鍵並點選)檔案“blender”,然後在彈出選單中選擇“顯示包內容”。它將顯示blender資料夾下所有隱藏的檔案,並在其中選擇“scripts”資料夾。
    • 要從OSX終端檢視此資料夾,請在列出的路徑的MacOS資料夾中使用ls -a命令(列出所有資料夾/檔案,即使是隱藏的)。最好在“/Applications/blender-2.37a-OSX-10.3-powerpc”資料夾中為scripts資料夾建立一個別名,以便可以透過Finder輕鬆操作指令碼。我知道Blender的指令碼資料夾應該埋藏在應用程式內部這一點令人困惑,但這是為了保持應用程式的可移植性並且不需要安裝。
    • 比上述方法更安全的方法是將您的指令碼儲存在您的主資料夾中的某個位置:使用此方案,當您升級blender應用程式時,不會存在刪除指令碼的風險,因為它們不包含在應用程式資料夾中。遵循此原則的一種方法如下:在您自己的主目錄中建立一個將包含您的指令碼(或其中一些指令碼)的資料夾;然後,而不是將您的檔案直接放在上面討論的.../.blender/scripts/資料夾中,只需在.../.blender/scripts/資料夾中新增一個指向您的指令碼目錄的連結(例如使用“ln -s”Unix命令,或透過執行“open /Applications/blender-2.37a-OSX-10.3-powerpc/blender.app/Contents/MacOS/.blender/scripts/”[根據您的blender版本調整],然後透過Finder建立連結,使用檔案->建立別名)。Blender現在將找到您放在主目錄中的所有指令碼:它將遵循您在其.../.blender/scripts/資料夾中建立的連結,轉到您自己目錄中的相應資料夾,並找到您放置在那裡的所有python指令碼。

注意:對於2.78+版本,此指令碼頭部已棄用。更多資訊:[[1]] Blender 2.78 - 附加元件教程

#!BPY

"""
Name: 'Wikibooks'
Blender: 259
Group: 'Export'
Tooltip: 'Wikibooks sample exporter'
"""
import Blender
import bpy

def write(filename):
    out = open(filename, "w")
    sce= bpy.data.scenes.active
    for ob in sce.objects:
        out.write(ob.type + ": " + ob.name + "\n")
    out.close()

Blender.Window.FileSelector(write, "Export")

現在,返回指令碼視窗,並在其選單中點選指令碼更新選單。如果您將其儲存到正確的路徑,從現在開始,檔案匯出選單中應該有一個“Wikibooks”條目。嘗試使用它匯出任何場景。它應該開啟檔案選擇器對話方塊,在您選擇一個檔案並按下“匯出”按鈕後,將場景中所有物件的列表寫入其中。每行一個物件,包含型別,後跟冒號和名稱。

它是如何工作的?如果您檢視指令碼,您可能已經知道了。但以防萬一,讓我們逐行檢視指令碼。

#!BPY

它告訴Blender這是一個Blender指令碼,因此它在掃描指令碼時會考慮它。接下來簡單地跟隨一個字串,用三個引號括起來,因此它可以跨越多行。

"""
Name: 'Wikibooks'
Blender: 259
Group: 'Export'
Tooltip: 'Wikibooks sample exporter'
"""

它包含四個專案,Blender使用它們將指令碼放置到其選單中。名稱、組(子選單名稱)和工具提示,都用單引號括起來。以及此版本的Blender。請注意,組名稱必須是Blender預定義的名稱之一(檢查其指令碼選單中的子選單以檢視有效名稱);如果Blender無法識別組名稱,則指令碼將放在“Misc”子選單中。

import Blender
import bpy

還記得我們說過bpy模組中的所有函式都以“Blender.”開頭嗎?在互動式shell中,我們可以簡單地使用它們,但在python指令碼中,所有使用的模組都必須使用import語句宣告(如果您想在指令碼中直接使用Blender模組中的函式,您可以簡單地將上面的import語句替換為“from Blender import *”:不再需要“Blender.”字首;但是,這會減慢指令碼的載入速度)。因此,以上只是允許我們在指令碼中使用Blender模組中的函式。

bpy模組是新的,將替換Blender用於資料訪問。

def write(filename):

這在Python中定義了一個函式。語法是def name(parameters):。在我們的例子中,名稱是“write”,我們有一個引數,稱為“filename”。

    out = open(filename, "w")

在這裡,我們開啟一個用於寫入的檔案(“w”),其名稱傳遞給函式(filename)。python函式“open”將開啟檔案,並返回對它的引用,我們將其儲存在變數“out”中。

   sce= bpy.data.scenes.active
   for ob in sce.objects:
       out.write(ob.type + ": " + ob.name + "\n")

這三行是我們真正的匯出指令碼。您已經知道第一行做了什麼——首先我們獲取當前場景,然後獲取該場景中所有物件的列表,for迴圈依次將每個物件分配給變數“ob”。第二行寫入檔案——首先是物件的型別,然後是字串“: ”,然後是物件的名稱,最後是一個換行符。

Blender.Window.FileSelector(write, "Export")

這是指令碼開始執行的地方。它只是呼叫一個Blender函式(在API文件中查詢),它開啟檔案選擇器。它將顯示一個“匯出”按鈕,當用戶點選它時,我們上面定義的函式“write”將被呼叫並傳遞選定的檔名。

這個指令碼目前還沒有什麼用,但它展示了基本知識。您現在應該能夠例如列出場景中的所有材質。(提示:它們就像物件一樣,嘗試在API文件中找到它們。)

在下一節中,我們將學習如何將有關物件的更多資訊匯出到我們的文字檔案。

匯出網格

[編輯 | 編輯原始碼]

我們的匯出指令碼列出了每個物件的型別和名稱,但這還沒有什麼用。如果我們想在另一個應用程式中載入匯出的資料,我們需要更多資訊。讓我們嘗試以OBJ格式匯出網格物件。

下面的示例是在OBJ檔案格式中的立方體。

v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000001 1.000000 1.000000
v 0.999999 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
f 1 2 3 4
f 5 8 7 6
f 1 5 6 2
f 2 6 7 3
f 3 7 8 4
f 5 1 4 8

這是一個簡單的obj匯出指令碼,它匯出選定的網格物件,用於匯出上面的OBJ檔案。

import Blender
import bpy

def write_obj(filepath):
	out = file(filepath, 'w')
	sce = bpy.data.scenes.active
	ob = sce.objects.active
	mesh = ob.getData(mesh=1)
	for vert in mesh.verts:
		out.write( 'v %f %f %f\n' % (vert.co.x, vert.co.y, vert.co.z) )
	
	for face in mesh.faces:
		out.write('f')
		
		for vert in face.v:
			out.write( ' %i' % (vert.index + 1) )
		out.write('\n')
	out.close()
Blender.Window.FileSelector(write_obj, "Export")

此指令碼將匯出一個可以被許多應用程式讀取的OBJ檔案。讓我們看看發生了什麼。

	sce = bpy.data.scenes.active
	ob = sce.objects.active

在這裡,我們獲取您在當前場景中最後選擇的物體。如果沒有任何選定的物體,這將引發錯誤,但這是一種測試新匯出器的簡單方法。

	mesh = ob.getData(mesh=1)

這獲取了物件的連結資料塊。目前我們不知道它是一個網格,另一個需要新增錯誤檢查的情況。

	for vert in mesh.verts:
		out.write( 'v %f %f %f\n' % (vert.co.x, vert.co.y, vert.co.z) )

在這裡,我們為每個頂點寫一行,使用字串格式化將左邊的“%f”替換為右邊的3個值。

	for face in mesh.faces:
		out.write('f')
		
		for vert in face.v:
			out.write( ' %i' % (vert.index + 1) )
		out.write('\n')

在OBJ格式中,每個面都引用了許多頂點索引。對於每個面,我們有一行以“f”開頭,然後遍歷面中的頂點。就像mesh.verts是網格中所有頂點的列表一樣,face.v是面中頂點的列表,最多4個頂點。(其中mesh和face是分配給Mesh和MFace物件的任意變數名)每個頂點都在同一行上寫入其索引,並加1。這是因為在OBJ檔案格式中,第一個頂點的索引為1,而在Python和Blender中,列表中的第一個專案的索引為0。

寫入新的一行,以便下一個面從新的一行開始。——在python中,“\n”表示寫入檔案時換行。

華夏公益教科書