跳轉到內容

Blender 3D:融入 Python/菜譜

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

影像函式

[編輯 | 編輯原始碼]

在外部程式中編輯影像

[編輯 | 編輯原始碼]

這在 Linux (可能任何 Unix) 中執行,並啟動 The Gimp。它可能可以修改為在 Windows 中啟動 Photoshop。
在 Gnome、KDE 和 Mac OS X 中,您可以使用命令使用預設應用程式或指定應用程式開啟文件。

  • KDE:kfmclient openURL <URL,相對路徑或絕對路徑>
  • Gnome:gnome-open <Gnome 理解的任何 URL 或路徑>
  • Mac OS X:open [-a <應用程式名稱>] <路徑>

Win32 也有一些 open 命令,也許有人可以新增它。

#!BPY
"""
Name: 'Edit Image in the Gimp'
Blender: 232
Group: 'UV'
Tooltip: 'Edit Image in the Gimp.'
"""
from Blender import *
import os

def main():
	image = Image.GetCurrent()
	if not image: # Image is None
		print 'ERROR: You must select an active Image.'
		return
	imageFileName = sys.expandpath( image.filename )
	
	
	#appstring = 'xnview "%f"'
	#appstring = 'gqview "%f"'
	appstring = 'gimp-remote "%f"'
	
	# -------------------------------
	
	appstring = appstring.replace('%f', imageFileName)
	os.system(appstring)

if __name__ == '__main__':
	main()

查詢影像

[編輯 | 編輯原始碼]

此指令碼遞迴地搜尋具有損壞檔案引用的影像。
它透過向用戶提供根路徑來工作,然後查詢並重新連結該路徑內的所有影像。
在將專案遷移到不同的計算機時,它非常有用。

#!BPY
"""
Name: 'Find all image files'
Blender: 232
Group: 'UV'
Tooltip: 'Finds all image files from this blend an relinks'
"""

__author__ = "Campbell Barton AKA Ideasman"
__url__ = ["http://members.iinet.net.au/~cpbarton/ideasman/", "blender", "elysiun"]

__bpydoc__ = """\
Blah
"""

from Blender import *
import os

#==============================================#
# Strips the slashes from the back of a string #
#==============================================#
def stripPath(path):
	return path.split('/')[-1].split('\\')[-1]

# finds the file starting at the root.
def findImage(findRoot, imagePath):
	newImageFile = None
	
	imageFile = imagePath.split('/')[-1].split('\\')[-1]
	
	# ROOT, DIRS, FILES
	pathWalk = os.walk(findRoot)
	pathList = [True]
	
	matchList = [] # Store a list of (match, size), choose the biggest.
	while True:
		try:
			pathList  = pathWalk.next()
		except:
			break
		
		for file in pathList[2]:
			# FOUND A MATCH
			if file.lower() == imageFile.lower():
				name = pathList[0] + sys.sep + file
				try:
					size = os.path.getsize(name)
				except:
					size = 0
					
				if size:
					print '   found:', name 
					matchList.append( (name, size) )
		
	if matchList == []:
		print 'no match for:', imageFile
		return None
	else:
		# Sort by file size
		matchList.sort(key=lambda x: x[1], reverse=True )
		
		print 'using:', matchList[0][0]
		# First item is the largest
		return matchList[0][0] # 0 - first, 0 - pathname
		

# Makes the pathe relative to the blend file path.
def makeRelative(path):
	blendBasePath = sys.expandpath('//')
	if path.startswith(blendBasePath):
		path = path.replace(blendBasePath, '//')
		path = path.replace('//\\', '//')
	return path

def find_images(findRoot):
	print findRoot
	
	# findRoot = Draw.PupStrInput ('find in: ', '', 100)
	
	if findRoot == '':
		Draw.PupMenu('No Directory Selected')
		return
	
	# Account for //
	findRoot = sys.expandpath(findRoot)
	
	# Strip filename
	while findRoot[-1] != '/' and findRoot[-1] != '\\':
		findRoot = findRoot[:-1]
	
	
	if not findRoot.endswith(sys.sep):
		findRoot += sys.sep
	
	
	if findRoot != '/' and not sys.exists(findRoot[:-1]):
		Draw.PupMenu('Directory Dosent Exist')
	
	
	Window.WaitCursor(1)
	# ============ DIR DONE\
	images = Image.Get()
	len_images = float(len(images))
	for idx, i in enumerate(images):

		progress = idx / len_images
		Window.DrawProgressBar(progress, 'searching for images')
		
		# If files not there?
		if not sys.exists(sys.expandpath(i.filename )):	
			newImageFile = findImage(findRoot, i.filename)
			if newImageFile != None:
				newImageFile = makeRelative(newImageFile)
				print 'newpath:', newImageFile
				i.filename = newImageFile
				i.reload()
	
	Window.RedrawAll()
	Window.DrawProgressBar(1.0, '')
	Window.WaitCursor(0)


if __name__ == '__main__':
	Window.FileSelector(find_images, 'SEARCH ROOT DIR', sys.expandpath('//'))

刪除重複影像

[編輯 | 編輯原始碼]

此指令碼查詢被引用多次的影像,並檢查所有網格的紋理面,並只分配其中一個影像。
如果一個面沒有使用者,則該影像將被刪除。
這很有用,因為當一個影像被載入多次時,它也會被載入到系統記憶體和顯示卡記憶體中多次,從而浪費資源。
對影像型別紋理的支援還需要完成。

#!BPY 
""" 
Name: 'Remove Double Images' 
Blender: 232 
Group: 'UV' 
Tooltip: 'Remove Double Images'
"""

from Blender import *

def main():
	# Sync both lists 
	fNameList = []# 
	bImageList = [] # Sync with the one abovr.
	
	bImageReplacePointer = dict() # The length of IMage.Get()
	
	imgIdx = 0
	
	# Sort by name lengths so image.001 will be replaced by image
	Images = Image.Get()
	Images.sort(key=lambda x: len(x.name), reverse=True )
	
	for bimg in Images:
		expendedFName = sys.expandpath(bimg.filename)
		bImageReplacePointer[expendedFName] = bimg
	
	print 'Remove Double Images, loading mesh data...',
	uniqueMeshNames = []
	# get all meshs
	doubles = 0
	for ob in Object.Get():
		if ob.getType() == 'Mesh' and ob.getData(1) not in uniqueMeshNames:
			m = ob.getData(mesh=1)
			uniqueMeshNames.append(ob.getData(1))
			
			# We Have a new mesh,
			imageReplaced = 0
			for f in m.faces:
				image = None
				try: image = f.image
				except: pass
				if image:
					replaceImage = bImageReplacePointer[ sys.expandpath(f.image.filename) ]
					if replaceImage.name != image.name:
						f.image = replaceImage
						imageReplaced = 1
			
			if imageReplaced:
				doubles += 1
				m.update()
				print '\tchanged', m.name
			else:
				print '\tunchanged', m.name
			
	print 'Done, %i doubles removed.' % doubles

if __name__ == '__main__':
	main()

材質函式

[編輯 | 編輯原始碼]

卡通材質批次轉換指令碼

[編輯 | 編輯原始碼]

此指令碼將您當前開啟的混合檔案中的 *所有* 材質更改為卡通材質。執行後,檢查 Blender 控制檯以獲取指令碼輸出。

由於此指令碼會更改您當前開啟的混合檔案中的 *所有* 材質設定,因此您 *不應* 在未儲存的專案上執行它!使用此指令碼時對材質所做的更改無法撤消!

注意:除非您在執行此指令碼後選擇儲存檔案,否則更改不會永久提交到混合檔案。

import Blender

from Blender import Material, Scene
from Blender.Scene import Render

print "\nTOON MATERIAL CONVERSION SCRIPT V1.0 STARTED...\n"

# Get list of active materials from Blender
materials = Blender.Material.Get()

# Get render information needed for edge setting
scn = Scene.GetCurrent()
context = scn.getRenderingContext()

print "PROGRESS: CONVERTING ALL MATERIALS TO TOON TYPE..."

# Change materials to Toon Diffuse/Specular
for m in materials:

   # Diffuse Shader (2 = Toon)
   m.setDiffuseShader(2)
   
   # Specular Shader (3 = Toon)
   m.setSpecShader(3)

   # THE FOLLOWING SETTINGS CAN
   # BE CHANGED TO DIFFERENT
   # VALUES WITHIN THE SPECIFIED
   # RANGE OF ACCEPTABLE NUMBERS:

   # Diffuse Size (0 to 3.14)
   m.setDiffuseSize(1.5)

   # Diffuse Smooth (0 to 1.0)
   m.setDiffuseSmooth(.5)

   # Reflect Amount (0 to 1.0)
   # - optionally here to help you
   # with any necessary batch changes
   # to all material reflection values
   # Remove "#" from line below to use:
   # m.setRef(.75)

   # Specular (0 to 2.0)
   m.setSpec(.3)

   # Specular Smooth (0 to 1.0)
   m.setSpecSmooth(.5)

   # Specular Size (0 to 3.14)
   m.setSpecSize(.4)

   # Enable toon edge: 0 = off, 1 = on
   context.enableToonShading(1)

   # Edge Intension (0 to 255)
   context.edgeIntensity(30)

print "PROGRESS: CONVERSION FINISHED!\nTWEAK MATERIALS AND LIGHTING AS NECESSARY."

Blender.Redraw()

曲線函式

[編輯 | 編輯原始碼]

曲線的長度

[編輯 | 編輯原始碼]

此函式獲取所有邊的總長度。最有用的是獲取曲線的長度。小心,因為它會獲取曲線物件中每條曲線的長度!

請注意,此函式在獲取長度時不會考慮物件的變換。

from Blender import Mesh, Object
def curve_length(ob): # Can realy be any object
    me= Mesh.New()
    me.getFromObject(cu_ob.name)
    totlength= 0.0
    for ed in me.edges:
        # Blender 2.42 can simply do
        # totlength+= ed.length
        totlength+= (ed.v1.co-ed.v2.co).length
    
    return totlength

# TEST THE FUNCTION
cu_ob= Object.Get('mycurve')
print curve_length(cu_ob)

文字函式

[編輯 | 編輯原始碼]

在 Unix 中貼上文字

[編輯 | 編輯原始碼]

在 X11 中貼上文字,需要 uclip [[1]]

#!BPY
"""
Name: 'Text from Clipboard'
Blender: 234
Group: 'Add'
Tooltip: 'Text from Clipboard X11'
""" 
from Blender import Text
import os

clip = os.popen('uclip -o')

clipTxt = clip.read()
text = Text.New(clipTxt[0:10])
text.write(clipTxt)

將所有文字儲存為檔案

[編輯 | 編輯原始碼]

將所有文字編輯器文字儲存為當前工作目錄中的檔案。警告:這會覆蓋具有相同名稱的檔案!

import Blender

texts=Blender.Text.Get()

for text in texts:
        out=file(text.name, 'w')
        for line in text.asLines():
                out.write(line+'\n')

網格函式

[編輯 | 編輯原始碼]

Blender 的 NMesh、GMesh 和新 Mesh 模組的示例和函式。

網格工具模板

[編輯 | 編輯原始碼]

將此用作編輯模式網格工具的基礎。

#!BPY
""" Registration info for Blender menus:
Name: 'Template Mesh Editmode tool...'
Blender: 237
Group: 'Mesh'
Tooltip: 'Change this template text tooltip'
"""

__author__ = "Your Name"
__url__ = ("blender", "elysiun")
__version__ = "1.0"

__bpydoc__ = """\
Multilin Script Help
Document your script here.
"""

from Blender import *

def main():
	scn = Scene.GetCurrent()
	ob = scn.getActiveObject() # Gets the current active object (If Any)
	
	if ob == None or ob.getType() != 'Mesh': # Checks the active objects a mesh
		Draw.PupMenu('ERROR%t|Select a mesh object.')
		return
	
	Window.WaitCursor(1) # So the user knowns the script is busy.
	
	is_editmode = Window.EditMode() # Store edit mode state
	if is_editmode: Window.EditMode(0) # Python must get a mesh in object mode.
	
	me = ob.getData()
	
	#================#
	# EDIT MESH HERE #
	#================#
	for v in me.verts:
		if v.sel: # Operating on selected verts is what the user expects.
			v.co.x = v.co.x * 2
	
	#================#
	# FINISH EDITING #
	#================#
	
	me.update() # Writes the mesh back into Blender.
	
	# Go back into editmode if we started in edit mode.
	if is_editmode: Window.EditMode(1)
	Window.WaitCursor(0)


if __name__ == '__main__': # Dont run the script if its imported by another script.
	main()

對網格頂點顏色進行去飽和

[編輯 | 編輯原始碼]

使用新 Mesh 模組。僅限 Blender 2.4。使用與 Photoshop 相同的加權進行去飽和。

from Blender import Mesh, Object
for ob in Object.GetSelected():
        if ob.getType() == 'Mesh':
                me = ob.getData(mesh=1)
                if me.faceUV:
                        for f in me.faces:
                                for c in f.col:
                                        # Weighted colour conversion, as used by photoshop.
                                        c.r = c.g = c.b = int(((c.r*30) + (c.g*59) + (c.b*11)) / 100.0)


點在網格內

[編輯 | 編輯原始碼]

此函式根據提供的點是否在網格內返回 1/0。它依賴於網格具有連續的皮膚,沒有孔洞。(否則問題就沒有意義。)

它使用的方法是檢視從該點到網格邊界外某個點之間的線段上有多少個面交叉點。

偶數個交叉點意味著它在外部,奇數個意味著它在內部。因此我們返回 len(intersections) % 2,其中intersections生成交叉點的列表。

此函式使用 Z 方向向量,因此我們可以透過首先進行 X/Y 邊界測試來節省一些 CPU 週期,以檢視點是否可能交叉,然後再進行完整的射線交叉測試。

from Blender import *

def pointInsideMesh(ob, pt):
	Intersect = Mathutils.Intersect # 2 less dict lookups.
	Vector = Mathutils.Vector
	
	def ptInFaceXYBounds(f, pt):
			
		co= f.v[0].co
		xmax= xmin= co.x
		ymax= ymin= co.y
		
		co= f.v[1].co
		xmax= max(xmax, co.x)
		xmin= min(xmin, co.x)
		ymax= max(ymax, co.y)
		ymin= min(ymin, co.y)
		
		co= f.v[2].co
		xmax= max(xmax, co.x)
		xmin= min(xmin, co.x)
		ymax= max(ymax, co.y)
		ymin= min(ymin, co.y)
		
		if len(f.v)==4: 
			co= f.v[3].co
			xmax= max(xmax, co.x)
			xmin= min(xmin, co.x)
			ymax= max(ymax, co.y)
			ymin= min(ymin, co.y)
		
		# Now we have the bounds, see if the point is in it.
		return xmin <= pt.x <= xmax and \
			ymin <= pt.y <= ymax
	
	def faceIntersect(f):
		isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, ray, obSpacePt, 1) # Clipped.
		if not isect and len(f.v) == 4:
			isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, ray, obSpacePt, 1) # Clipped.
				
		return bool(isect and isect.z > obSpacePt.z) # This is so the ray only counts if its above the point. 
	
	
	obImvMat = Mathutils.Matrix(ob.matrixWorld)
	obImvMat.invert()
	pt.resize4D()
	obSpacePt = pt* obImvMat
	pt.resize3D()
	obSpacePt.resize3D()
	ray = Vector(0,0,-1)
	me= ob.getData(mesh=1)
	
	# Here we find the number on intersecting faces, return true if an odd number (inside), false (outside) if its true.
	return len([None for f in me.faces if ptInFaceXYBounds(f, obSpacePt) if faceIntersect(f)]) % 2

# Example, see if the cursor is inside the mesh.
if __name__ == '__main__':
	scn= Scene.GetCurrent()
	ob= scn.getActiveObject()
	pt= Mathutils.Vector(Window.GetCursorPos())
	print 'Testing if cursor is inside the mesh',
	inside= pointInsideMesh(ob, pt)
	print inside

用於匯入的掃描填充

[編輯 | 編輯原始碼]

此函式接收一個網格和一個表示 ngon 的頂點索引列表。它返回一個三角形索引列表,這些索引構成了掃描填充的面。這對匯入程式更有用。

它還處理掃描填充無法正常工作的情況,方法是返回一個三角形扇形。

它可能比對原始網格使用 mesh.fill() 函式更快,因為迴圈編輯模式在大量資料上可能很慢。此函式相對於簡單使用 fill() 的另一個優點是,您可以確保面將根據索引的順序以正確的方向翻轉。

from Blender import *
def ngon(from_mesh, indicies):
	if len(indicies) < 4:
		return [indicies]
	is_editmode= Window.EditMode()
	if is_editmode:
		Window.EditMode(0)
	temp_mesh = Mesh.New()
	temp_mesh.verts.extend( [from_mesh.verts[i].co for i in indicies] )
	temp_mesh.edges.extend( [(temp_mesh.verts[i], temp_mesh.verts[i-1]) for i in xrange(len(temp_mesh.verts))] )
	
	oldmode = Mesh.Mode()
	Mesh.Mode(Mesh.SelectModes['VERTEX'])
	for v in temp_mesh.verts:
		v.sel= 1
	
	# Must link to scene
	scn= Scene.GetCurrent()
	temp_ob= Object.New('Mesh')
	temp_ob.link(temp_mesh)
	scn.link(temp_ob)
	temp_mesh.fill()
	scn.unlink(temp_ob)
	Mesh.Mode(oldmode)
	
	new_indicies= [ [v.index for v in f.v]  for f in temp_mesh.faces ]
	
	if not new_indicies: # JUST DO A FAN, Cant Scanfill
		print 'Warning Cannot scanfill!- Fallback on a triangle fan.'
		new_indicies = [ [indicies[0], indicies[i-1], indicies[i]] for i in xrange(2, len(indicies)) ]
	else:
		# Use real scanfill.
		# See if its flipped the wrong way.
		flip= None
		for fi in new_indicies:
			if flip != None:
				break
			
			for i, vi in enumerate(fi):
				if vi==0 and fi[i-1]==1:
					flip= 0
					break
				elif vi==1 and fi[i-1]==0:
					flip= 1
					break
		if flip:
			for fi in new_indicies:
				fi.reverse()
	
	if is_editmode:
		Window.EditMode(1)
	return new_indicies


# === ===== EG
scn= Scene.GetCurrent()
me = scn.getActiveObject().getData(mesh=1)
ind= [v.index for v in me.verts if v.sel] # Get indicies

indicies = ngon(me, ind) # fill the ngon.

# Extand the faces to show what the scanfill looked like.
print len(indicies)
me.faces.extend([[me.verts[ii] for ii in i] for i in indicies])

三角化 NMesh

[編輯 | 編輯原始碼]

這是一個供其他指令碼使用的函式,如果您想透過只處理三角形來簡化您的工作,它會很有用。
最短邊方法用於將四邊形劃分為 2 個三角形。

def triangulateNMesh(nm):
	'''
	Converts the meshes faces to tris, modifies the mesh in place.
	'''
	
	#============================================================================#
	# Returns a new face that has the same properties as the origional face      #
	# but with no verts                                                          #
	#============================================================================#
	def copyFace(face):
		newFace = NMesh.Face()
		# Copy some generic properties
		newFace.mode = face.mode
		if face.image != None:
			newFace.image = face.image
		newFace.flag = face.flag
		newFace.mat = face.mat
		newFace.smooth = face.smooth
		return newFace
	
	# 2 List comprehensions are a lot faster then 1 for loop.
	tris = [f for f in nm.faces if len(f) == 3]
	quads = [f for f in nm.faces if len(f) == 4]
	
	
	if quads: # Mesh may have no quads.
		has_uv = quads[0].uv 
		has_vcol = quads[0].col
		for quadFace in quads:
			
			# Triangulate along the shortest edge
			if (quadFace.v[0].co - quadFace.v[2].co).length < (quadFace.v[1].co - quadFace.v[3].co).length:
				# Method 1
				triA = 0,1,2
				triB = 0,2,3
			else:
				# Method 2
				triA = 0,1,3
				triB = 1,2,3
				
			for tri1, tri2, tri3 in (triA, triB):
				newFace = copyFace(quadFace)
				newFace.v = [quadFace.v[tri1], quadFace.v[tri2], quadFace.v[tri3]]
				if has_uv: newFace.uv = [quadFace.uv[tri1], quadFace.uv[tri2], quadFace.uv[tri3]]
				if has_vcol: newFace.col = [quadFace.col[tri1], quadFace.col[tri2], quadFace.col[tri3]]
				
				nm.addEdge(quadFace.v[tri1], quadFace.v[tri3]) # Add an edge where the 2 tris are devided.
				tris.append(newFace)
		
		nm.faces = tris

修復“蝴蝶結”四邊形中的頂點繞序

[編輯 | 編輯原始碼]

有時您可能會遇到四邊形面,儘管它們正確地共面,但並不完全“完整”。這是由於頂點的順序錯誤,導致面自我重疊,通常在法線指向錯誤方向的地方留下不必要的孔洞和黑色區域。要檢視這到底意味著什麼,只需建立一個平面,然後交換任意邊上的頂點位置。連線的邊將交叉。由於面的法線在這種情況下沒有意義,因此指令碼無法保證在完成處理後法線指向外部。

#!BPY
"""
Name: 'Quadsorter'
Blender: 233
Group: 'Mesh'
Tip: 'Fix winding order for quad faces for all selected meshes'
Author: Yann Vernier (LoneTech)
"""

from Blender.Mathutils import Vector, CrossVecs, DotVecs

def sortface(f):
  if len(f) != 4:
    return f
  v=[Vector(list(p)) for p in f]
  v2m0=v[2]-v[0]
  # The normal of the plane
  n=CrossVecs(v[1]-v[0], v2m0)
  #k=DotVecs(v[0],n)
  #if DotVecs(v[3],n) != k:
  #  raise ValueError("Not Coplanar")
  # Well, the above test would be a good hint to make triangles.
  # Get a vector pointing along the plane perpendicular to v[0]-v[2]
  n2=CrossVecs(n, v2m0)
  # Get the respective distances along that line
  k=[DotVecs(p,n2) for p in v[1:]]
  # Check if the vertices are on the proper side
  if cmp(k[1],k[0]) == cmp(k[1],k[2]):
    #print "Bad",v
    f.v=[f[0],f[2],f[3],f[1]]

from Blender.Object import GetSelected
for obj in GetSelected():
  if obj.getType() == 'Mesh':
    mesh=obj.data
    for face in mesh.faces:
      sortface(face)
    mesh.update()

刪除頂點而不刪除整個面

[編輯 | 編輯原始碼]

使用上面網格模板的指令碼。刪除頂點,但周圍的四邊形將轉換為三角形。
注意 這隻適用於 NMesh。

        #================#
        # EDIT MESH HERE #
        #================#
        for f in me.faces:
            face_verts = f.v[:] # make a copy of the list.
            for v in face_verts:
                if v.sel:
                    f.v.remove(v)
        
        # Remove all with less then 3 verts,
        # When removing objects from a list its best to loop backwards
        fIdx = len(me.faces)
        while fIdx:
            fIdx -=1
            f = me.faces[fIdx]
            if len(f.v) < 3:
                del me.faces[fIdx]
        
        # Remove all selected verts
        # Loop backwards.
        vIdx = len(me.verts)
        while vIdx:
            vIdx -=1
            v = me.verts[vIdx]
            if v.sel:
                del me.verts[vIdx]
        #================#
        # FINISH EDITING #
        #================#

GMesh 自動平滑網格

[編輯 | 編輯原始碼]

此函式使用 GMesh 來自動平滑流形網格,它需要 GMesh 模組。

小心,因為它會就地平滑您的網格,因此如果您不想修改它,請複製您的原始物件。

from Blender import *
import GMesh

smooth = Draw.PupIntInput('smooth:', 20,1,89)

for ob in Object.GetSelected():
        mesh = ob.getData()
        gmesh = GMesh.NMesh2GMesh(mesh)

        try:
                gmesh.autoSmooth(smooth)
        except:
                print 'Error non manifold mesh'
                continue # go onto the next item

        mesh = GMesh.GMesh2NMesh(gmesh)

        # Make the faces smooth
        for f in mesh.faces:
                f.smooth = 1

        ob.link(mesh) # Link the new mesh with the original object


掃描填充

[編輯 | 編輯原始碼]

模擬在 Blender 中鍵入“Shift F”以建立掃描填充的選定邊迴圈(僅限編輯模式)

這隻有在 3D 檢視開啟的情況下才能工作。

import Blender

winid = Blender.Window.GetScreenInfo(Blender.Window.Types.VIEW3D)[0]['id']
Blender.Window.SetKeyQualifiers(Blender.Window.Qual.SHIFT)
Blender.Window.QAdd(winid, Blender.Draw.FKEY,1)
Blender.Window.QHandle(winid)
Blender.Window.SetKeyQualifiers(0)


擴充套件的掃描填充函式

[編輯 | 編輯原始碼]

自包含的掃描填充函式,基於上面的程式碼。注意,此函式需要 3D 檢視可用

import Blender

# Take a list of points and return a scanfilled NMesh
def scanFillPoints(pointList):
        Blender.Window.EditMode(0)

        nme = Blender.NMesh.New()
        # 2.37 compatability, not needed in 2.4
        if not nme.edges:
                nme.addEdgesData() 

        for p in pointList:
                v = Blender.NMesh.Vert( p[0], p[1], p[2] )
                nme.verts.append(v)
                v.sel = 1

                if len(nme.verts) >= 2:
                        nme.addEdge(nme.verts[-2], nme.verts[-1])

        nme.addEdge(nme.verts[0], nme.verts[-1])


        scn = Blender.Scene.GetCurrent()

        actOb = scn.getActiveObject()
        if actOb:
                actSel = actOb.sel
        else:
                actSel = 0


        ob = Blender.Object.New('Mesh')
        ob.link(nme)
        scn.link(ob)
        scn.layers = range(1,20)
        ob.sel = 1
        Blender.Window.EditMode(1)

        winid = Blender.Window.GetScreenInfo(Blender.Window.Types.VIEW3D)[0]['id']
        Blender.Window.SetKeyQualifiers(Blender.Window.Qual.SHIFT)
        Blender.Window.QAdd(winid, Blender.Draw.FKEY,1)
        Blender.Window.QHandle(winid)
        Blender.Window.SetKeyQualifiers(0)

        Blender.Window.EditMode(0)
        # scn.unlink(ob)

        # Select the old active object.
        if actOb:
                actOb.sel = actSel

        # Reture the scanfilled faces.
        return ob.getData()

示例函式用法。

scanFillPoints([[-1,-1,0], [1,-1,1], [1,1,0], [0,0,0.2], [0,1,-.1], [0.1,1,-0.3] ])

複製 NMesh 面

[編輯 | 編輯原始碼]

返回一個新面,它具有與原始面相同的屬性,但沒有頂點

def faceCopy(face):
  newFace = NMesh.Face()
  # Copy some generic properties
  newFace.mode = face.mode
  if face.image != None:
    newFace.image = face.image
  newFace.flag = face.flag
  newFace.mat = face.mat
  newFace.smooth = face.smooth
  return newFace


返回面的中心點作為向量

[編輯 | 編輯原始碼]

注意,Blender 的 Mesh API 現在有 face.cent 訪問

接收 1 個 NMFace 並返回其中心點作為向量,如果提供,將使用現有的向量物件“cent”。

				
def faceCent(f, cent=None):
	x = y = z = 0
	for v in f.v:
		x+=v.co[0]
		y+=v.co[1]
		z+=v.co[2]
	if not cent:
		return Mathutils.Vector([x/len(f.v), y/len(f.v), z/len(f.v)])
	
	# Modify the provided vec
	cent.x = x/len(f.v)
	cent.y = y/len(f.v)
	cent.z = z/len(f.v)

翻轉面向上

[編輯 | 編輯原始碼]

我使用此指令碼將許多地形網格中的所有面翻轉為向上。它使用 Mesh 而不是 NMesh。

from Blender import *

#==================#
# Apply Tpransform #
#==================# Used for skin
def apply_transform(vec, matrix):
        x, y, z = vec
        xloc, yloc, zloc = matrix[3][0], matrix[3][1], matrix[3][2]
        vec.x = x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0] + xloc
        vec.y = x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1] + yloc
        vec.z = x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2] + zloc

def apply_transform3x3(vec, matrix):
        x, y, z = vec
        vec.x = x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0]
        vec.y = x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1]
        vec.z = x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2]


# Point to z up.
noVec = Mathutils.Vector(0,0,-10000)
cent = Mathutils.Vector(0,0,0)

for ob in Object.GetSelected():
        if ob.getType() != 'Mesh':
                continue
        mat = ob.matrixWorld


        me = ob.getData(mesh=1)
        # We know were mesh

        # Select none
        for f in me.faces: f.sel = 0

        # Flip based on facing.
        for f in me.faces:
                no = f.no
                apply_transform3x3(no, mat)
                
                # Get the faces centre
                cent.x, cent.y, cent.z = 0,0,0
                for v in f.v:
                        cent += v.co
                cent = cent * (1.0 / len(f.v))
                apply_transform(cent, mat)
                
                # Move the vec over the centre of the face.
                noVec.x = cent.x
                noVec.y = cent.y
                
                # Are we not facing up?, if not then select and flip later.
                if ((cent+no)-noVec).length <= (cent-noVec).length:
                        f.sel = 1
        me.flipNormals()

縮放 UV

[編輯 | 編輯原始碼]

縮放所有選中網格物件的 uv 座標。

from Blender import *
def main():
        # Scale the UV down.
        # This examples scales down by 1 pixel on a 512x512 image.
        shrink = 1-(1/512.0)

        for ob in Object.GetSelected():
                if ob.getType() == 'Mesh':
                        me = ob.getData(mesh=1)
                        if me.faceUV:
                                for f in me.faces:
                                        f.uv =\
                                        tuple([ Mathutils.Vector(\
                                        ((uv[0]-0.5)*shrink)+0.5,\
                                        ((uv[1]-0.5)*shrink)+0.5,\
                                         ) for uv in f.uv])

if __name__ == '__main__':
        main()

在所有場景中的所有網格中查詢材料

[編輯 | 編輯原始碼]

有時您有很多網格物件和材料,您不希望它們中的任何一個使用。此指令碼可以幫助您找到這些物件。

#!BPY
"""
Name: 'Find Mesh with Material'
Blender: 234
Group: 'Object'
Tooltip: 'Find Mesh with Material'
""" 

from Blender import *

def main():
	matToFind = Draw.PupStrInput('matName:', '', 21)
	if matToFind == None:
		return
	
	Window.WaitCursor(1)
	for scn in Scene.Get():
		for ob in scn.getChildren():
			if ob.getType() == 'Mesh':
				for mat in ob.getData(mesh=1).materials:
					matname = None
					try:
						matname = mat.name
					except:
						# Material must be None
						continue

					if matname == matToFind:
						# Unselect all in the scene
						for ob_ in scn.getChildren():
							ob_.sel = 0

						# Select the found object
						ob.sel = 1

						scn.makeCurrent()
						Draw.PupMenu('Material "%s" found in object "%s".' % (matToFind, ob.name))
						Window.WaitCursor(0)
						return

	Window.WaitCursor(0)
	Draw.PupMenu('Material "%s" Not found.' % matToFind)

if __name__ == '__main__':
	main()


面共享一條邊

[編輯 | 編輯原始碼]

這兩個面共享一條邊,最好確保您沒有比較相同的面,並刪除第一個“if”。

# Do the 2 faces share an edge?
# return true or false.
def faceShareEdge(face1, face2):
	# Are we using the same verts. could be more comprehensive, since vert order may differ but still be the same.
	if face1.v == face2.v: 
		return False
	firstMatch = None
	for v1 in face1:
		if v1 in face2:
			if firstMatch is None:
				firstMatch = True
			else:
				return True
	return False

獲取邊角度

[編輯 | 編輯原始碼]

返回一個角度列表,所有使用這些邊的面的組合角度差。返回的角度與 mesh.edges 同步。具有 0 或 1 個面的邊將具有零角度。

此函式使用 Blender.Mesh 而不是 Blender.NMesh 網格資料。

def getEdgeAngles(me):
	Ang= Blender.Mathutils.AngleBetweenVecs
	Vector= Blender.Mathutils.Vector
	
	edges = dict( [ (ed.key,  (i, [])) for i, ed in enumerate(me.edges) ] )
	
	for f in me.faces:
		#print f.index
		for key in f.edge_keys:
			edges[key][1].append(f.no)
	
	edgeAngles=[0.0] * len(me.edges)
	for eIdx, angles in edges.itervalues():
		angles_len= len(angles)
		
		if angles_len < 2:
			pass
		if angles_len==2:
			edgeAngles[eIdx] = Ang(angles[0], angles[1])
		else:
			totAngDiff=0
			for j in reversed(xrange(angles_len)):
				for k in reversed(xrange(j)):
					totAngDiff+= (Ang(angles[j], angles[k])/180) # /180 isnt needed, just to keeop the vert small.
			edgeAngles[eIdx] = totAngDiff
	return edgeAngles

網格射線相交

[編輯 | 編輯原始碼]

將射線與網格相交,假設網格沒有位置/大小/旋轉。

import Blender
from Blender import Window, Mathutils, Object
Vector= Mathutils.Vector
Intersect= Mathutils.Intersect
Matrix= Mathutils.Matrix

def meshRayIntersect(me, Origin, Direction):
	def faceIntersect(f):
		isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, Direction, Origin, 1) # Clipped.
		if isect:
			return isect
		elif len(f.v) == 4:
			isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, Direction, Origin, 1) # Clipped.
		return isect
	
	
	''' Ray is a tuple of vectors (Origin, Direction) '''
	isect= best_isect= None
	dist_from_orig= 1<<30
	
	for f in me.faces:
		isect= faceIntersect(f)
		if isect:
			l= (isect-Origin).length
			if l < dist_from_orig:
				dist_from_orig= l
				best_isect= isect
	return best_isect, dist_from_orig


將頂點 UV 複製到面 UV

[編輯 | 編輯原始碼]

將頂點 UV 座標(粘性)複製到面 UV 座標(TexFace)。

#!BPY

#sticky2uv.py

""" Registration info for Blender menus:
Name: 'Vertex UV to face UV'
Blender: 241
Group: 'Mesh'
Tooltip: 'Copy vertex UV to face UV'
"""
__author__ = "Brandano"
__url__ = ("blender", "elysiun")
__version__ = "1.0"
__bpydoc__ = """\
Copies the Vertex UV coordinates (Sticky) to face UV coordinates (TexFace).
Warning: the original face UV's will be overwritten.
"""
import Blender
from Blender import Mesh

if (Blender.Object.GetSelected() != None):
    for me in [ob.getData(mesh=1) for ob in Blender.Object.GetSelected() if ob.getType() == "Mesh"]:
        if me.vertexUV:
            me.faceUV = 1
            for f in me.faces: f.uv = [v.uvco for v in f.verts]
        me.update()

數學函式

[編輯 | 編輯原始碼]

這裡是新增數學示例的地方,它們可以是 Blender 特定的或通用的 Python 數學函式。

更改旋轉軸順序

[編輯 | 編輯原始碼]

如果您在不同旋轉系統之間轉換時遇到問題,則可能是旋轉順序出了問題。

import Blender
RotationMatrix= Blender.Mathutils.RotationMatrix

MATRIX_IDENTITY_3x3 = Blender.Mathutils.Matrix([1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0])
def eulerRotateOrder(x,y,z): 
	x,y,z = x%360,y%360,z%360 # Clamp all values between 0 and 360, values outside this raise an error.
	xmat = RotationMatrix(x,3,'x')
	ymat = RotationMatrix(y,3,'y')
	zmat = RotationMatrix(z,3,'z')
	# Standard BVH multiplication order, apply the rotation in the order Z,X,Y
	# Change the order here
	return (ymat*(xmat * (zmat * MATRIX_IDENTITY_3x3))).toEuler()

獲取 3 個點之間的角度

[編輯 | 編輯原始碼]

獲取線段 AB 和 BC 之間的角度,其中 b 是肘部。

import Blender
AngleBetweenVecs = Blender.Mathutils.AngleBetweenVecs

def getAng3pt3d(avec, bvec, cvec):
	try:
		ang = AngleBetweenVecs(avec - bvec,  cvec - bvec)
		if ang != ang:
			raise  "ERROR angle between Vecs"
		else:
			return ang
	except:
		print '\tAngleBetweenVecs failed, zero length?'
		return 0

點在三角形內 (2D)

[編輯 | 編輯原始碼]

如果 pt 在三角形內,則返回 True。
僅當 pt 位於三角形的平面上時才會給出正確的結果。

from Blender import Mathutils
SMALL_NUM = 0.000001
def pointInTri2D(pt, tri1, tri2, tri3):
	a = Mathutils.TriangleArea(tri1, tri2, tri3)
	othera = Mathutils.TriangleArea(pt, tri1, tri2) + SMALL_NUM
	if othera > a: return False
	othera += Mathutils.TriangleArea(pt, tri2, tri3)
	if othera > a: return False
	othera += Mathutils.TriangleArea(pt, tri3, tri1)
	if othera > a: return False
	return True

應用矩陣

[編輯 | 編輯原始碼]

將由 object.getMatrix() 返回的 4x4 變換應用於向量(空間中的 3D 點)

這對查詢頂點在世界空間中的位置很有用。

Blender 2.43 支援直接透過 “newvwec = vec*matrix” 來實現這一點,但瞭解如何手動執行這一點也很有幫助。

#==================#
# Apply Tpransform #
#==================#
def apply_transform(vec, matrix):
	x, y, z = vec
	xloc, yloc, zloc = matrix[3][0], matrix[3][1], matrix[3][2]
	return	x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0] + xloc,\
			x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1] + yloc,\
			x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2] + zloc

def apply_transform3x3(vec, matrix):
	x, y, z = vec
	return x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0],\
			x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1],\
			x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2]

二維線段相交

[編輯 | 編輯原始碼]

使兩條線段相交,如果相交,則返回交點位置。

如果沒有相交,則返回的 X 值將為 None,而 y 將是錯誤程式碼。

第一條線段為(x1,y1, x2,y2),第二條為(_x1,_y1, _x2,_y2)

SMALL_NUM = 0.000001
def lineIntersection2D(x1,y1, x2,y2, _x1,_y1, _x2,_y2):
	
	# Bounding box intersection first.
	if min(x1, x2) > max(_x1, _x2) or \
	max(x1, x2) < min(_x1, _x2) or \
	min(y1, y2) > max(_y1, _y2) or \
	max(y1, y2) < min(_y1, _y2):
		return None, 100 # Basic Bounds intersection TEST returns false.
	
	# are either of the segments points? Check Seg1
	if abs(x1 - x2) + abs(y1 - y2) <= SMALL_NUM:
		return None, 101
	
	# are either of the segments points? Check Seg2
	if abs(_x1 - _x2) + abs(_y1 - _y2) <= SMALL_NUM:
		return None, 102
	
	# Make sure the HOZ/Vert Line Comes first.
	if abs(_x1 - _x2) < SMALL_NUM or abs(_y1 - _y2) < SMALL_NUM:
		x1, x2, y1, y2, _x1, _x2, _y1, _y2 = _x1, _x2, _y1, _y2, x1, x2, y1, y2
	
	if abs(x2-x1) < SMALL_NUM: # VERTICLE LINE
		if abs(_x2-_x1) < SMALL_NUM: # VERTICLE LINE SEG2
			return None, 111 # 2 verticle lines dont intersect.
		
		elif abs(_y2-_y1) < SMALL_NUM:
			return x1, _y1 # X of vert, Y of hoz. no calculation.		
		
		yi = ((_y1 / abs(_x1 - _x2)) * abs(_x2 - x1)) + ((_y2 / abs(_x1 - _x2)) * abs(_x1 - x1))
		
		if yi > max(y1, y2): # New point above seg1's vert line
			return None, 112
		elif yi < min(y1, y2): # New point below seg1's vert line
			return None, 113
			
		return x1, yi # Intersecting.
	
	
	if abs(y2-y1) < SMALL_NUM: # HOZ LINE
		if abs(_y2-_y1) < SMALL_NUM: # HOZ LINE SEG2
			return None, 121 # 2 hoz lines dont intersect.
		
		# Can skip vert line check for seg 2 since its covered above.	
		xi = ((_x1 / abs(_y1 - _y2)) * abs(_y2 - y1)) + ((_x2 / abs(_y1 - _y2)) * abs(_y1 - y1))
		if xi > max(x1, x2): # New point right of seg1's hoz line
			return None, 112
		elif xi < min(x1, x2): # New point left of seg1's hoz line
			return None, 113
		
		return xi, y1 # Intersecting.
	
	# Accounted for hoz/vert lines. Go on with both anglular.
	b1 = (y2-y1)/(x2-x1)
	b2 = (_y2-_y1)/(_x2-_x1)
	a1 = y1-b1*x1
	a2 = _y1-b2*_x1
	
	if b1 - b2 == 0.0:
		return None, None	
	
	xi = - (a1-a2)/(b1-b2)
	yi = a1+b1*xi
	if (x1-xi)*(xi-x2) >= 0 and (_x1-xi)*(xi-_x2) >= 0 and (y1-yi)*(yi-y2) >= 0 and (_y1-yi)*(yi-_y2)>=0:
		return xi, yi
	else:
		return None, None

四捨五入到 2 的冪

[編輯 | 編輯原始碼]

此函式取任何數字,並將其四捨五入到最接近的 2 的冪值(2,4,8,16,32,64,128,256,512,1024,2048, 4096...)。這對將紋理四捨五入到載入到顯示卡記憶體中的尺寸很有用。

它返回 3 個值:向下舍入、最接近的舍入、向上舍入。

def roundPow2(roundVal):
	base2val = 1
	while roundVal >= base2val:
		base2val*=2
	
	# dont round up if there the same, just give the same vars
	if roundVal == base2val/2:
		return base2val/2, base2val/2, base2val/2 # Round down and round up.
	
	
	smallRound = base2val/2
	largeRound = base2val
	
	# closest to the base 2 value
	diffLower = abs(roundVal - smallRound)
	diffHigher = abs(roundVal - largeRound)
	if diffLower < diffHigher:
		mediumRound = smallRound
	else:
		mediumRound = largeRound
	
	smallRound = base2val/2
	largeRound = base2val
	
	return smallRound, mediumRound, largeRound # round down, round mid and round up.

最接近點(捕捉)

[編輯 | 編輯原始碼]

返回最接近點點的向量。適用於捕捉。

def getSnapVec(point, snap_points):
	'''
	Returns the closest vec to snap_points
	'''
	close_dist= 1<<30
	close_vec= None

	x= point[0]
	y= point[1]
	z= point[2]
	for v in snap_points:
		# quick length cmp before a full length comparison.
		if abs(x-v[0]) < close_dist and\
		abs(y-v[1]) < close_dist and\
		abs(z-v[2]) < close_dist:
			l= (v-point).length
			if l<close_dist:
				close_dist= l
				close_vec= v
	return close_vec

Blender 物件指令碼

[編輯 | 編輯原始碼]

連結複製物件

[編輯 | 編輯原始碼]

B:Python 中沒有函式可以建立物件的連結複製(Alt+D),因此這裡有一個為您完成此操作的函式。

注意自從編寫本文以來,Blender.Object.Duplicate() 以及 object.copy() 已被新增。

from Blender import *

# Like pressing Alt+D
def linkedCopy(ob, scn=None): # Just like Alt+D
	if not scn:
		scn = Scene.GetCurrent()
	type = ob.getType()
	newOb = Object.New(type)
	if type != 'Empty':
	  newOb.shareFrom(ob)
	scn.link(newOb)
	newOb.setMatrix(ob.getMatrix())
	# Copy other attributes.
	newOb.setDrawMode(ob.getDrawMode())
	newOb.setDrawType(ob.getDrawType())
	newOb.Layer = ob.Layer
        # Update the view 
        ob.select(0)
        newOb.select(1)
	return newOb

# You can call the function like this
try:
	ob2duplicate = Object.GetSelected()[0]
	linkedCopy(ob2duplicate)
        Redraw() 
except:
	print "Nothing Selected"

選擇雙重物件

[編輯 | 編輯原始碼]

查詢雙重物件 - 具有相同資料名稱、型別和位置/大小/旋轉的物件。

#!BPY
"""
Name: 'Select only double objects.'
Blender: 232
Group: 'Object'
Tooltip: 'Select double objects from the existing selection.'
"""

from Blender import *

def main():
	# Collect the extra object data once only, so we dont need to request it again.
	obinfo = [{'object':ob, 'dataname':ob.getData(1), 'type':ob.getType(), 'matrix':tuple(ob.matrixWorld)} for ob in Object.GetSelected() ]
	print '\n\n\nStarting to select doubles for %i objects.' % len(obinfo)
	doubleObs = [] # store doubles in this list
	doubles = 0
	
	# Comparison loop, compare items in the list only once.
	obIdx1 = len(obinfo)
	while obIdx1:
		obIdx1 -=1
		ob1 = obinfo[obIdx1]
		
		# Deselect as we go, any doubles will be selected again.		ob1['object'].sel = 0
		ob1['object'].sel = 0
		
		obIdx2 = obIdx1
		
		while obIdx2:
			obIdx2 -=1
			ob2 = obinfo[obIdx2]
			# Comparison loop done.
			
			
			# Now we have both objects we can compare ob2 against ob1.
			if \
			ob1['dataname'] == ob2['dataname'] and\
			ob1['type']     == ob2['type'] and\
			ob1['matrix']   == ob2['matrix']:
				# We have a double, print output and add to the double list.
				doubles +=1
				print '\t%i doubles found: "%s", "%s"' % (doubles, ob1['object'].name, ob2['object'].name)
				doubleObs.append(ob2)

	for ob in doubleObs:
		ob['object'].sel = 1

if __name__ == '__main__':
	t = sys.time()
	main()
	print 'Done in %.4f seconds.' % (sys.time()-t)

NLA 軌道示例

[編輯 | 編輯原始碼]

操作 NLA 軌道和動作軌道的示例程式碼。

# Nov 16 2006
#
# Mike Stramba
# mstramba@sympatico.ca
# BlenderArtists  Mike_S
#

import Blender
from Blender import *
from Blender.Armature import NLA


ctr = 1
numStrips = 1

vflags ={32:'LOCK_ACTION',1:'SELECT',2:'STRIDE_PATH',8:'HOLD',16:'ACTIVE'}
	
def tranflag(flag):
	if flag:
		print
		for v in vflags:
			t = flag & v
			if t:
				print '\t\t',v,vflags[t]

def showStrip(strip):
	print ctr,'/',numStrips
	print strip.action.name
	print '\tstripStart',strip.stripStart
	print '\tstripEnd',strip.stripEnd
	print '\tactionStart',strip.actionStart
	print '\tactionEnd',strip.actionEnd
	print '\tblendin',strip.blendIn
	print '\tblendout',strip.blendOut


	print '\tflag',strip.flag,
	tranflag(strip.flag)
	
	print '\tmode',strip.mode
	print '\tbrepeat',strip.repeat
	print '\tstrideAxis',strip.strideAxis
	print '\tstrideBone',strip.strideBone
	print '\tstrideLength',strip.strideLength


armOb=Object.Get('Armature')

actions=Armature.NLA.GetActions()

#
#  Actions named 'rot', 'move', 'Run' assumed to exist, or substitute
#  your own action names
#

rotAct = actions['rot']
movAct = actions['move']
runAct = actions['Run']

#
# get all NLA strips for this object
#

Char1NLAstrips = armOb.actionStrips

#
# set the current frame to where you want NLA strips to initially appear
# in the NLA editor

frame = 1
Blender.Set('curframe',frame)


#
# remove all NLA strips
#


Char1NLAstrips[:] = []



#
#  some different ways of adding action strips to the NLA editor
#

Blender.Object.Get('Armature').actionStrips.append(Blender.Armature.NLA.GetActions()['tester'])

Char1NLAstrips.append(Blender.Armature.NLA.GetActions()['UpDown'])

armOb.actionStrips.append(rotAct)

Char1NLAstrips.append(movAct)

Char1NLAstrips.append(actions['Run'])


#
#  get a strip
#

strip0 = Char1NLAstrips[0]

print '\nstrip0.action.name ="'+strip0.action.name+'"'

#
# show it's properties
#

showStrip(strip0)

#
# change it's stripStart, stripEND (add 50 frames)
#  (effectively moving the strip

strip0.stripEnd   += 50
strip0.stripStart += 50

#
# show the changes  
#

showStrip(strip0)

#
# select the strip in the NLA editor
#

strip0.flag += NLA.Flags['SELECT']
Blender.Window.RedrawAll()

showStrip(strip0)

#
# move all strips by FrameOffset 
#

def moveallStrips(FrameOffset):
	for strip in Char1NLAstrips:
		strip.stripEnd   += FrameOffset
		strip.stripStart += FrameOffset


moveallStrips(30)


#
# show all strips Properties
#

print 
print '============  ALL STRIPS ================'

numStrips = len(Char1NLAstrips)
print numStrips,' NLA strips for ',armOb
for strip in Char1NLAstrips:
	showStrip(strip)

Blender 視窗和使用者介面指令碼

[編輯 | 編輯原始碼]

滑鼠位置 3D 空間

[編輯 | 編輯原始碼]
import Blender
from Blender import Mathutils, Window, Scene, Draw, Mesh
from Blender.Mathutils import Matrix, Vector, Intersect


# DESCRIPTION:
# screen_x, screen_y the origin point of the pick ray
# it is either the mouse location
# localMatrix is used if you want to have the returned values in an objects localspace.
#    this is usefull when dealing with an objects data such as verts.
# or if useMid is true, the midpoint of the current 3dview
# returns
# Origin - the origin point of the pick ray
# Direction - the direction vector of the pick ray
# in global coordinates
epsilon = 1e-3 # just a small value to account for floating point errors

def getPickRay(screen_x, screen_y, localMatrix=None, useMid = False):
	
	# Constant function variables
	p = getPickRay.p
	d = getPickRay.d
	
	for win3d in Window.GetScreenInfo(Window.Types.VIEW3D): # we search all 3dwins for the one containing the point (screen_x, screen_y) (could be the mousecoords for example) 
		win_min_x, win_min_y, win_max_x, win_max_y = win3d['vertices']
		# calculate a few geometric extents for this window

		win_mid_x  = (win_max_x + win_min_x + 1.0) * 0.5
		win_mid_y  = (win_max_y + win_min_y + 1.0) * 0.5
		win_size_x = (win_max_x - win_min_x + 1.0) * 0.5
		win_size_y = (win_max_y - win_min_y + 1.0) * 0.5

		#useMid is for projecting the coordinates when we subdivide the screen into bins
		if useMid: # == True
			screen_x = win_mid_x
			screen_y = win_mid_y
		
		# if the given screencoords (screen_x, screen_y) are within the 3dwin we fount the right one...
		if (win_max_x > screen_x > win_min_x) and (  win_max_y > screen_y > win_min_y):
			# first we handle all pending events for this window (otherwise the matrices might come out wrong)
			Window.QHandle(win3d['id'])
			
			# now we get a few matrices for our window...
			# sorry - i cannot explain here what they all do
			# - if you're not familiar with all those matrices take a look at an introduction to OpenGL...
			pm	= Window.GetPerspMatrix()   # the prespective matrix
			pmi  = Matrix(pm); pmi.invert() # the inverted perspective matrix
			
			if (1.0 - epsilon < pmi[3][3] < 1.0 + epsilon):
				# pmi[3][3] is 1.0 if the 3dwin is in ortho-projection mode (toggled with numpad 5)
				hms = getPickRay.hms
				ortho_d = getPickRay.ortho_d
				
				# ortho mode: is a bit strange - actually there's no definite location of the camera ...
				# but the camera could be displaced anywhere along the viewing direction.
				
				ortho_d.x, ortho_d.y, ortho_d.z = Window.GetViewVector()
				ortho_d.w = 0
				
				# all rays are parallel in ortho mode - so the direction vector is simply the viewing direction
				#hms.x, hms.y, hms.z, hms.w = (screen_x-win_mid_x) /win_size_x, (screen_y-win_mid_y) / win_size_y, 0.0, 1.0
				hms[:] = (screen_x-win_mid_x) /win_size_x, (screen_y-win_mid_y) / win_size_y, 0.0, 1.0
				
				# these are the homogenious screencoords of the point (screen_x, screen_y) ranging from -1 to +1
				p=(hms*pmi) + (1000*ortho_d)
				p.resize3D()
				d[:] = ortho_d[:3]
				

			# Finally we shift the position infinitely far away in
			# the viewing direction to make sure the camera if outside the scene
			# (this is actually a hack because this function
			# is used in sculpt_mesh to initialize backface culling...)
			else:
				# PERSPECTIVE MODE: here everything is well defined - all rays converge at the camera's location
				vmi  = Matrix(Window.GetViewMatrix()); vmi.invert() # the inverse viewing matrix
				fp = getPickRay.fp
				
				dx = pm[3][3] * (((screen_x-win_min_x)/win_size_x)-1.0) - pm[3][0]
				dy = pm[3][3] * (((screen_y-win_min_y)/win_size_y)-1.0) - pm[3][1]
				
				fp[:] = \
				pmi[0][0]*dx+pmi[1][0]*dy,\
				pmi[0][1]*dx+pmi[1][1]*dy,\
				pmi[0][2]*dx+pmi[1][2]*dy
				
				# fp is a global 3dpoint obtained from "unprojecting" the screenspace-point (screen_x, screen_y)
				#- figuring out how to calculate this took me quite some time.
				# The calculation of dxy and fp are simplified versions of my original code
				#- so it's almost impossible to explain what's going on geometrically... sorry
				p[:] = vmi[3][:3]
				
				# the camera's location in global 3dcoords can be read directly from the inverted viewmatrix
				d[:] = p.x-fp.x, p.y-fp.y, p.z-fp.z
				
			
			# the direction vector is simply the difference vector from the virtual camera's position
			#to the unprojected (screenspace) point fp
			
			# Do we want to return a direction in object's localspace?
			if localMatrix:
				localInvMatrix = Matrix(localMatrix)
				localInvMatrix.invert()
				p = p*localInvMatrix
				d = d*localInvMatrix # normalize_v3
				p.x += localInvMatrix[3][0]
				p.y += localInvMatrix[3][1]
				p.z += localInvMatrix[3][2]
				
			#else: # Worldspace, do nothing
			
			d.normalize()
			return True, p, d # Origin, Direction	
	
	# Mouse is not in any view, return None.
	return False, None, None

# Constant function variables
getPickRay.d = Vector(0,0,0) # Perspective, 3d
getPickRay.p = Vector(0,0,0)
getPickRay.fp = Vector(0,0,0)

getPickRay.hms = Vector(0,0,0,0) # ortho only 4d
getPickRay.ortho_d = Vector(0,0,0,0) # ortho only 4d



			
# TEST FUNCTION
# MOVES & VERTS ON THE ACTIVE MESH.
def main():
	ob = Scene.GetCurrent().getActiveObject()
	me = ob.getData(mesh=1)
	
	# Loop until the mouse is in the view.
	mouseInView = False
	while not mouseInView: 
		screen_x, screen_y = Window.GetMouseCoords()
		mouseInView, Origin, Direction = getPickRay(screen_x, screen_y)
	
	if Window.GetMouseButtons() == 1 and mouseInView:
		i = 0
		time = Blender.sys.time()
		while Window.GetMouseButtons() == 1:
			i+=1
			screen_x, screen_y = Window.GetMouseCoords()
			mouseInView, Origin, Direction = getPickRay(screen_x, screen_y, ob.matrix)
			if mouseInView:
				
				me.verts[0].co.x = Origin.x
				me.verts[0].co.y = Origin.y
				me.verts[0].co.z = Origin.z
				
				me.verts[1].co.x = Origin.x - (Direction.x*1000)
				me.verts[1].co.y = Origin.y - (Direction.y*1000)
				me.verts[1].co.z = Origin.z - (Direction.z*1000)
				Window.Redraw(Window.Types.VIEW3D)
		print '100 draws in %.6f' % (((Blender.sys.time()-time) / float(i))*100)

if __name__ == '__main__':
	main()

自動按鈕

[編輯 | 編輯原始碼]

自動按鈕是在任何指令碼中新增一堆按鈕的非常簡單的方法。
將 AutoButtons 文字新增到任何指令碼的底部,任何以 _bgui 結尾的函式都將有一個呼叫它的按鈕。

# All functions to be displayed as buttons must use this suffix
GUI_SUFFIX= '_bgui'
BUTTON_LIST = [] # A list if dicts
EVENT = 1000
EVENTNUM = 1000

for func in dir():
	if func.endswith(GUI_SUFFIX):
		newButton = {}
		newButton['name'] = func[:-5].replace('_', ' ')[2:]
		newButton['func'] = func + '()'
		newButton['event'] = EVENT
		BUTTON_LIST.append( newButton )
		EVENT+=1
		
def draw_gui():
	# find the width of the widest button
	button_height = 16; button_width = 100; ROW = 0
	for button in BUTTON_LIST:
		Draw.PushButton(button['name'], button['event'], 0, button_height*ROW, button_width, button_height, ''); ROW+=1
def handle_event(evt, val):
	if evt in (Draw.ESCKEY, Draw.QKEY) and not val:
		Draw.Exit()
def handle_button_event(evt):
	if evt >= EVENTNUM and evt < EVENTNUM + len(BUTTON_LIST):
		exec(BUTTON_LIST[evt - EVENTNUM]['func'])
	else:
		print 'invalid', evt
Draw.Register(draw_gui, handle_event, handle_button_event)


可以使用此功能的示例函式

def Print_Object_Selection_bgui():
	Blender.Draw.PupMenu('|'.join(ob.name for ob in Blender.Object.GetSelected()))
[編輯 | 編輯原始碼]

此指令碼獲取您通常傳遞給 Draw.PupMenu() 的字串,並根據組大小將選單分開。

def PupMenuLess(menu, groupSize=30):
	'''
	Works like Draw.PupMenu but will add a more/less buttons if the number of
	items is greater then the groupSize.
	'''
	more = ['   more...']
	less = ['   less...']
	
	menuList= menu.split('|')
	
	# No Less Needed, just call.
	if len(menuList) < groupSize:
		return Draw.PupMenu(menu)
	
	title = menuList[0].split('%t')[0]
	
	# Split the list into groups
	menuGroups = [[]]
	for li in menuList[1:]:
		if len(menuGroups[-1]) < groupSize:
			menuGroups[-1].append(li)
		else:
			menuGroups.append([li])
	
	# Stores the current menu group we are looking at
	groupIdx = 0
	while True:
		# Give us a title with the menu number
		numTitle = [ ' '.join([title, str(groupIdx + 1), 'of', str(len(menuGroups)), '%t'])]
		if groupIdx == 0:
			menuString = '|'.join(numTitle + menuGroups[groupIdx] + more)
		elif groupIdx == len(menuGroups)-1:
			menuString = '|'.join(numTitle + less + menuGroups[groupIdx])
		else: # In the middle somewhere so Show a more and less
			menuString = '|'.join(numTitle + less + menuGroups[groupIdx] + more)
		result = Draw.PupMenu(menuString)
		# User Exit
		if result == -1:
			return -1
		
		if groupIdx == 0: # First menu
			if result-1 < groupSize:
				return result
			else: # must be more
				groupIdx +=1
		elif groupIdx == len(menuGroups): # Last Menu
			if result == 1: # Must be less
				groupIdx -= 1
			else: # Must be a choice
				return result + (groupIdx*groupSize)
			
		else:	
			if result == 1: # Must be less
				groupIdx -= 1
			elif result-2 == groupSize:
				groupIdx +=1
			else:
				return result - 1 + (groupIdx*groupSize)

通用 Python

[編輯 | 編輯原始碼]

這裡新增通用 Python 程式碼,在 Python 指令碼編寫時很有用。

基準測試指令碼

[編輯 | 編輯原始碼]

計時不同函式的指令碼。

def loopFor():
	'''For loop test'''
	for i in xrange(1000000):
		a=i
	
def loopWhile():
	'''While loop test'''
	i=0
	while i<1000000:
		a=i
		i+=1

def time_func(bench_func, iter=4):
	''' Run the function 10 times '''
	print '',bench_func.__doc__
	t= Blender.sys.time()
	for i in xrange(iter):
		bench_func()
	tme= (Blender.sys.time()-t) / 10
	print '\tBenchmark %.4f average sec' % tme
	return tme

def main():
	print '\nRunning tests'
	time_func(loopFor)
	time_func(loopWhile)

if __name__ == '__main__':
	main()

迭代多個列表

[編輯 | 編輯原始碼]

有時您想同時迴圈多個列表。如果您處理的列表很大,那麼為了這個目的建立一個新列表會很慢,並且會使用太多記憶體。此類會獲取多個列表,並將它們視為一個大型列表。而無需建立新列表。

type_list= type([])
type_tuple= type(())
class listIter:
	def __init__(self, lists):
		if type(lists) != type_list:
			self.lists= list(lists)
		else:
			self.lists= lists
		self.idx= self.lidx= 0
	
	def next(self):
		if self.lidx==len(self.lists):
			raise StopIteration
		idx=self.idx
		lidx=self.lidx
		self.idx+=1
		if self.idx==len(self.lists[self.lidx]):
			self.idx= 0
			self.lidx+=1
		
		return self.lists[lidx][idx]
	
	def __iter__(self):
		return self
	def __getitem__(self, index):
		i=0
		for l in self.lists:
			if i+len(l)>index:
				return l[index-i]
			i+=len(l)
		raise IndexError
	def __setitem__(self, index, value):
		i=0
		for l in self.lists:
			if i+len(l)>index:
				l[index-i]= value
				return
			i+=len(l)
		raise IndexError
	
	def __len__(self):
		length=0
		for l in self.lists:
			length+=len(l)
		return length
	
	def index(self, value):
		i=0
		for l in self.lists:
			for li in l:
				if li == value:
					return i
				i+=1
		raise ValueError
	
	def remove(self, value):
		for l in self.lists:
			if value in li:
				l.remove(i)
				return
		raise ValueError
	
	def count(self, value):
		return sum(l.count(value) for l in self.lists)
	
	def extend(self, value):
		for i in value: # See its an iterator
			break
		self.lists.append(value)
		
	def pop(self, index):
		i=0
		for l in self.lists:
			if i+len(l)>index:
				return l.pop(index-i)
			i+=len(l)
		raise IndexError
	def __str__(self):
		return '['+ ''.join(str(l)[1:-1] for l in self.lists) +']'
	
	def sort(self):
		'''Cant to a full sort, just do a par'''
		self.lists.sort()
		for l in self.lists:
			l.sort()
	
	def append(self, value):
		self.lists[-1].append(value)
	
	def reverse(self):
		for l in self.lists:
			l.reverse()
		self.lists.reverse()

一些示例

for i in listIter( (range(10), range(22), range(5)) ):
	print i

另一個示例,使用此迭代器和列表推導從 3 個網格中獲取頂點,並將它們新增到一個網格中。

from Blender import Mesh
newme= Mesh.New()

# Using the iterator
newme.verts.extend( [v.co for v in listIter((me1.verts, me2.verts, me3.verts))] )

# Without the iterator
newme.verts.extend( [v.co for v in me1.verts ] )
newme.verts.extend( [v.co for v in me2.verts ] )
newme.verts.extend( [v.co for v in me3.verts ] )

二進位制轉換(不使用結構體)

[編輯 | 編輯原始碼]

感謝 SJH 07/29/2004 20:26:03

nybblechr_to_01_dqs={'-':'-','0':'0000', '1':'0001', '2':'0010', '3':'0011',
                             '4':'0100', '5':'0101', '6':'0110', '7':'0111',
                             '8':'1000', '9':'1001', 'A':'1010', 'B':'1011',
                             'C':'1100', 'D':'1101', 'E':'1110', 'F':'1111'}

# Int to binary
def i2b(j, wd=0):
	return ''.join(nybblechr_to_01_dqs[x] for x in '%02X' % j))[-wd:].zfill(wd)
	
# Char to binary
def c2b(c, wd=0):
	return i2b(ord(c))
	
# String to binary
def s2b(s, wd=0):
	return ''.join(nybblechr_to_01_dqs[x] for x in ''.join('%02X' % ord(c) for c in s))[-wd:].zfill(wd)
	  
# Binary to char
def b2c(b):
	chr(int(b,2))

隨機化列表

[編輯 | 編輯原始碼]

返回隨機化的列表。注意如果可以匯入 random,請使用 random.shuffle(ls) 替代。

def randList(ls):
	lsCopy = ls[:]
	randList = []
	lenList = len(lsCopy)
	while lenList != len(randList):
		randIndex = int( Noise.random() * len(lsCopy)  )
		randList.append( lsCopy.pop( randIndex ) )
	return randList

刪除列表中的重複項

[編輯 | 編輯原始碼]

從列表中刪除重複項,修改原始列表。(將使用物件的 cmp() 函式)

def RemDoubles(List):
	lIdx = 0
	while lIdx < len(List):
		if List.count(List[lIdx]) > 1:
			List.pop(lIdx)
			continue
		lIdx+=1

從列表中刪除重複項 (雜湊)

[編輯 | 編輯原始碼]

返回一個沒有重複項的新列表。

def RemDoublesHash(myList):
	return list(set(myList))

獲取和的標誌屬性

[編輯 | 編輯原始碼]

Blender 中的許多屬性都是標誌,並存儲在 2 的指數和中。要找出特定標誌是否已設定幷包含在和中,請嘗試使用此函式

def powList(self, x):
	tmpx = x
	exp = 0
	expList = []
	while tmpx != 0:	
		tmp = 2**exp
		if tmp > tmpx:
			elem = 2**(exp-1)
			expList.append(elem)
			tmpx -= elem	
			exp = 0
		else:
			exp += 1; 
	return expList

以這種方式呼叫函式

lmp = Lamp.Get(thisObj.data.getName())
lmpMode = lmp.getMode()
lmpFlags = self.powList(lmpMode)
if 16 in lmpFlags:
	...

分數資料型別

[編輯 | 編輯原始碼]

允許在實數系統中建立分數資料以及對分數資料的所有操作。

class fraction:
	# Types without importing type - Does not retuire a python install.
	type_float = type(0.1)
	type_int = type(1)
	
	def __init__(self, num, den=1):
		if den == 0:
			raise ValueError, 'Division by zero'
		g = self.gcd(num, den)
		self.num = num / g
		self.den = den / g

	def __str__(self):
		return "%d/%d" % (self.num, self.den)

	def __mul__(self, other):
		if type(other) is fraction.type_int:
			other = fraction(other)
		elif type(other) is fraction.type_float:
			return self.eval() * other
		if not isinstance(other, fraction):
			raise ValueError, 'Unsupported operand type for multiply operation ' + str(type(other))
		return fraction(self.num * other.num, self.den * other.den)

	__rmul__ = __mul__

	def __add__(self, other):
		if type(other) is fraction.type_int:
			other = fraction(other)
		elif type(other) is fraction.type_float:
			return self.eval() + other
		if not isinstance(other, fraction):
			raise ValueError, 'Unsupported operand type for addition operation ' + str(type(other))
		num = self.num * other.den + self.den * other.num
		den = self.den * other.den
		return fraction(num, den)

	def __cmp__(self, other):
		if type(other) is fraction.type_int or type(other) is fraction.type_float:
			return self.eval() - other
		if not isinstance(other, fraction):
			raise ValueError, 'Comparative operation no supported for operand ' * type(other)
		return (self.num * other.den - other.num * self.den)

	def __neg__(self):	
		return fraction(self.num * -1, self.den)

	def __invert__(self):
		return fraction(self.den, self.num)

	def __sub__(self, other):
		return self + -other

	def __rsub__(self, other):
		return other + -self

	def __div__(self, other):
		return fraction(self.num, self.den) * fraction(other.den, other.num)
	
	def __rdiv__(self, other):
		return fraction(self.den, self.num) * fraction(other.num, other.den)
	
	def __pow__(self, other):
		if type(other) is fraction.type_int:
			return fraction(self.num ** other, self.den ** other)
		elif type(other) is fraction.type_float:
			a = self.eval()
			if a > 0:
				return a ** other
			else:
				raise ValueError, 'Negative number raised to fractional power'
		if not isinstance(other, fraction):
			raise ValueError, 'Unsupported operand type for exponential operation ' + str(type(other))
		return self.eval() ** other.eval()

	def gcd(self, m, n):
		if m % n:
			return self.gcd(n, m % n)
		return n
		
	
	def eval(self):
		return float(self.num) / self.den

##### Usage: #####

a = fraction(3, 4)
b = fraction(5, 6)
print a * b
print a - 3
print a ** b

#invalid - raising a negative number to a fractional power
print (-a)**b

獲取一個名稱,其中包含合理的字元

[編輯 | 編輯原始碼]

當您從物件/網格/場景... 名稱建立檔名時,可以使用此函式。Blender 在名稱中支援許多檔案系統可能不支援的字元。

saneFilechars 將這些字元替換為“_”。

def saneFilechars(name):
	for ch in ' /\\~!@#$%^&*()+=[];\':",./<>?\t\r\n':
		name = name.replace(ch, '_')
	return name

顏色指令碼

[編輯 | 編輯原始碼]

處理顏色的指令碼的地方。

RGB 到 HSV

[編輯 | 編輯原始碼]

將紅色/綠色/藍色轉換為色調/飽和度/值

r、g、b 值範圍為 0.0 到 1.0

h = [0,360],s = [0,1],v = [0,1]

如果 s == 0,則 h = -1 (未定義)

色調/飽和度/值模型由 A. R. Smith 於 1978 年建立。它基於諸如色調、陰影和色調(或色系、純度和強度)等直觀的顏色特徵。座標系是圓柱形的,顏色在六角錐體內部定義。色調值 H 的範圍為 0 到 360º。飽和度 S 是強度或純度的程度,範圍為 0 到 1。純度是指向顏色中添加了多少白色,因此 S=1 使顏色最純(沒有白色)。亮度 V 的範圍也在 0 到 1 之間,其中 0 是黑色。

def RGBtoHSV(R,G,B):
	# min, max, delta;
	min_rgb = min( R, G, B )
	max_rgb = max( R, G, B )
	V = max_rgb

	delta = max_rgb - min_rgb
	if not delta:
		H = 0
		S = 0
		V = R # RGB are all the same.
		return H,S,V
		
	elif max_rgb: # != 0
		S = delta / max_rgb
	else:
		R = G = B = 0 # s = 0, v is undefined
		S = 0
		H = 0 # -1
		return H,S,V

	if R == max_rgb:
		H = ( G - B ) / delta # between yellow & magenta
	elif G == max_rgb:
		H = 2 + ( B - R ) / delta # between cyan & yellow
	else:
		H = 4 + ( R - G ) / delta # between magenta & cyan

	H *= 60 # convert to deg
	if H < 0:
		H += 360
	
	return H,S,V


HSV 到 RGB

[編輯 | 編輯原始碼]

將色調/飽和度/值轉換為紅色/綠色/藍色

def HSVtoRGB(H,S,V):
	if not S: # S == 0
		# achromatic (grey)
		# R = G = B = V
		return V,V,V # RGB == VVV
		
	H /= 60;			# sector 0 to 5
	i = int( H ) # round down to int. in C its floor()
	f = H - i			# factorial part of H
	p = V * ( 1 - S )
	q = V * ( 1 - S * f )
	t = V * ( 1 - S * ( 1 - f ) )
	
	if i == 0:
		R,G,B = V,t,p
	elif i == 1:
		R,G,B = q,V,p
	elif i == 2:
		R,G,B = p,V,t
	elif i == 3:
		R,G,B = p,q,V
	elif i == 4:
		R,G,B = t,p,V
	else: # 5
		R,G,B = V,p,q
	return R,G,B

互動式工具

[編輯 | 編輯原始碼]

手繪折線繪製工具

[編輯 | 編輯原始碼]
from Blender import *

def main():
        # New Curve and add to Scene.
        scn = Scene.GetCurrent()
        cu = Curve.New()
        # cu.setResolu(1)
        x=y=z=w=t = 1
        cu.appendNurb([x,y,z,w,t])
        cu[0].type = 0 # Poly line
        ob = Object.New('Curve')
        ob.link(cu)
        scn.link(ob)
        ob.sel = 1 # Make active and selected

        # Initialize progress bar for writing
        Window.DrawProgressBar(0.0, '')

        ticker = 0.0 # Used to cycle the progress bar

        # Pause before drawing
        while not Window.GetMouseButtons() & Window.MButs['L']:
                sys.sleep(10)

                Window.DrawProgressBar(ticker, 'Left Mouse to Draw')
                ticker += 0.01
                if ticker > 0.98: ticker = 0

        oldx=oldy = -100000
        # Mouse Clicked, lets draw
        while Window.GetMouseButtons() & Window.MButs['L']:
                x,y = Window.GetMouseCoords()
                print abs(x-oldx)+abs(y-oldy)
                if (oldx == x and oldy == y) or abs(x-oldx)+abs(y-oldy) < 10: # Mouse must have moved 10 before adding the next point
                        pass
                else:
                        z = 0 # 2D Drawing for now
                        w = 100 #Weight is 1
                        cu.appendPoint(0, [x*0.001,y*0.001,z,w]) # Can add tilt here.
                        cu.update()
                        Window.Redraw(Window.Types.VIEW3D)

                        Window.DrawProgressBar(ticker, 'Drawing...')
                        ticker += 0.01
                        if ticker > 0.98: ticker = 0

                        oldx,oldy = x,y # Store the old mouse location to compare with new.

        # Clear the progress bar
        Window.DrawProgressBar(1.0, '')

main()



渲染函式

[編輯 | 編輯原始碼]

渲染到目錄

[編輯 | 編輯原始碼]
# recursive dir creation.
def _mkdir(newdir):
	import os, sys
	"""works the way a good mkdir should :)
			- already exists, silently complete
			- regular file in the way, raise an exception
			- parent directory(ies) does not exist, make them as well
	"""
	if os.path.isdir(newdir):
		pass
	elif sys.exists(newdir):
		raise OSError("a file with the same name as the desired " \
						      "dir, '%s', already exists." % newdir)
	else:
			head, tail = os.path.split(newdir)
			if head and not os.path.isdir(head):
				_mkdir(head)
			#print "_mkdir %s" % repr(newdir)
			if tail:
				os.mkdir(newdir)

from Blender import *
from Blender.Scene import Render
if sys.sep == '\\':
	path="c:\\tmp\\renderfarm\\render"
else:
	path="/tmp/renderfarm/render"

# Should probably create the paths if not existing.
_mkdir(path)

scene= Scene.GetCurrent()
context = scene.getRenderingContext()
context.setRenderPath(path)
context.setImageType(Scene.Render.PNG)
context.enableExtensions(1)

context.renderAnim()


從所有攝像機渲染

[編輯 | 編輯原始碼]

此指令碼從場景中的所有攝像機渲染動畫,它根據攝像機建立新的名稱,並將場景保留為原始狀態。

確保使用有用的攝像機名稱。

from Blender import Object, Scene

sce= Scene.GetCurrent()
context = sce.getRenderingContext()
output_path_orig= context.getRenderPath()

cams= [ob for ob in sce.getChildren() if ob.getType()=='Camera']

# backup the active cam.
orig_cam= sce.getCurrentCamera()

# loop over all the cameras in this scene, set active and render.
for i, c in enumerate(cams):
	print '\tRendering %i of %i cameras.' % (i, len(cams))
	context.setRenderPath('%s_%s_' % (output_path_orig, c.name)) # use a unique name
	sce.setCurrentCamera(c) # set this camera to be active.
	context.renderAnim()
print 'Done per camera render'	

if orig_cam:
	sce.setCurrentCamera(orig_cam) # restore the original cam

# restore the original path.
context.setRenderPath(output_path_orig)

指令碼連結

[編輯 | 編輯原始碼]

監檢視像

[編輯 | 編輯原始碼]

此指令碼需要用作重新繪製指令碼連結。它檢查當前影像的日期,並嘗試重新載入影像,如果成功,則重新繪製圖像。

import Blender
try:
	Blender.my_image_time
except:
	Blender.my_image_time=0
import os
img= Blender.Image.GetCurrent() # currently displayed picture.
if img: # Image isnt None
	path= Blender.sys.expandpath(img.filename)
	if Blender.sys.exists(path):
		t= os.path.getctime(path)
		if t != Blender.my_image_time:
			img.reload()
			Blender.Window.Redraw(Blender.Window.Types.IMAGE)
		Blender.my_image_time= t # global, persists between running the scripts.

動態文字

[編輯 | 編輯原始碼]

此指令碼需要用作連結到文字物件的幀更改指令碼。更改 Years.append 行以選擇時間軸的年份。

import Blender as B 

Years = [] 

# year = [year, initial frame, duration of frames] 
Years.append([1800, 1, 10])
Years.append([1850, 100, 50]) 
Years.append([1994, 170, 100]) 
Years.append([2008, 300, 50])
Years.append([2050, 400, 50]) 
 
def when (frame,  years): 
     
    iniY = 0 
 
    for y in range(len(years)): 
        if frame > years[y][1]: 
            iniY = y 
 
    iniYear       = years[iniY][0] 
    iniFrame      = years[iniY][1] 
    iniFrameDelay = years[iniY][2] 
 
    finYear  = years[iniY+1][0] 
    finFrame = years[(iniY+1)][1] 
 
    frameRange = finFrame - (iniFrame + iniFrameDelay) 
    yearRange = finYear - iniYear 
     
    normFrame = float(frame - iniFrame - iniFrameDelay) 
    normFrame = normFrame/frameRange 
 
    if normFrame > 0: 
        newYear = str(int(iniYear + (yearRange * normFrame))) 
    else: 
        newYear = iniYear 
     
    return str(newYear) 
 
if B.bylink: 
 
    actualFrame = B.Get("curframe") 
 
    year = B.link 
    dynYear = year.getData()      
     
    oldYear=dynYear.getText() 
    newYear=when (actualFrame,Years) 
 
    if newYear != oldYear: 
        dynYear.setText(newYear) 
        year.makeDisplayList() 
        B.Window.RedrawAll()

外部實用程式

[編輯 | 編輯原始碼]

壓縮所有 Blend 檔案 (僅限 Unix)

[編輯 | 編輯原始碼]

自 Blender 2.41 起,GZip 壓縮支援已整合到 Blender 中。因此,您可以壓縮所有 blend 檔案,它們仍然可以按預期開啟。

此實用程式在您的硬碟上搜索 blend 檔案,如果它們尚未壓縮,則對其進行 gzip 壓縮。

注意:需要 python3。

#!/usr/bin/python3

root_dir = '/mango/pro'

import os
import os.path


def blend_path_list(path):
    for dirpath, dirnames, filenames in os.walk(path):
        for filename in filenames:
            if filename.endswith(".blend"):
                yield os.path.join(dirpath, filename)


def isblend_nogz(path):
    try:
        f = open(path, 'rb')
        is_blend = (f.read(7) == b'BLENDER')
        f.close()
        return is_blend
    except:
        return False


def main():
    print('Searching "%s"...' % root_dir)
    files = list(blend_path_list(root_dir))
    files.sort()
    print('done.')
    #print files
    tot_files = len(files)
    tot_compressed = tot_blends = tot_alredy_compressed = 0
    tot_blend_size = 0
    tot_blend_size_saved = 0
    for f in files:
        if len(f) >= 6:  # .blend is 6 chars
            f_lower = f.lower()
            if (f_lower.endswith(".blend") or
                f_lower[:-1].endswith(".blend") or
                f_lower[:-2].endswith(".blend")):  # .blend10 +

                print(f, "...", end="")
                tot_blends += 1
                # allows for dirs with .blend, will just be false.
                if isblend_nogz(f):
                    print("compressing ...", end="")
                    tot_compressed += 1
                    orig_size = os.path.getsize(f)
                    tot_blend_size += orig_size
                    os.system('gzip --best "%s"' % f)
                    os.system('mv "%s.gz" "%s"' % (f, f)) # rename the gz file to the original.
                    new_size = os.path.getsize(f)
                    tot_blend_size_saved += orig_size - new_size
                    print('saved %.2f%%' % (100 - (100 * (float(new_size) / orig_size))))
                else:
                    print('alredy compressed.')
                    tot_alredy_compressed += 1

    print('\nTotal files:', tot_files)
    print('Total Blend:', tot_blends)
    print('Total Blend Compressed:', tot_compressed)
    print('Total Alredy Compressed:', tot_alredy_compressed)
    print('\nTotal Size in Blends: %sMB' % (((tot_blend_size) / 1024) / 1024))
    print('Total Saved in Blends: %sMB' % (((tot_blend_size_saved) / 1024) / 1024))

if __name__ == '__main__':
        main()
華夏公益教科書