Thomas Larsson - Введение в написание скриптов на Питоне для Блендера 2.5x. Примеры кода
Эта программа также показывает, как вызвать диалог выбора файлов, нажав кнопку на панели. Класс кнопки Load BVH наследуется от двух базовых классов bpy.types.Operator и ImportHelper.
class OBJECT_OT_LoadBvhButton(bpy.types.Operator, ImportHelper):
Класс ImportHelper (возможно, недокументированный) определяет некоторые атрибуты, которые используются для фильтрации файлов, отображающихся в диалоге выбора файлов.
filename_ext = ".bvh"
filter_glob = bpy.props.StringProperty(default="*.bvh", options={'HIDDEN'})
filepath = bpy.props.StringProperty(name="File Path",
maxlen=1024, default="")
Существует аналогичный класс ExportHelper, который ограничивает имеющийся выбор файлов экспорта.
#----------------------------------------------------------
# File simple_bvh_import.py
# Simple bvh importer
#----------------------------------------------------------
bl_info = {
'name': 'Simple BVH importer (.bvh)',
'author': 'Thomas Larsson',
'version': (1, 0, 0),
'blender': (2, 5, 7),
'api': 34786,
'location': "File > Import",
'description': 'Simple import of Biovision bvh',
'category': 'Import-Export'}
import bpy, os, math, mathutils, time
from mathutils import Vector, Matrix
from io_utils import ImportHelper
#
# class CNode:
#
class CNode:
def __init__(self, words, parent):
name = words[1]
for word in words[2:]:
name += ' '+word
self.name = name
self.parent = parent
self.children = []
self.head = Vector((0,0,0))
self.offset = Vector((0,0,0))
if parent:
parent.children.append(self)
self.channels = []
self.matrix = None
self.inverse = None
return
def __repr__(self):
return "CNode %s" % (self.name)
def display(self, pad):
vec = self.offset
if vec.length < Epsilon:
c = '*'
else: c = ' '
print("%s%s%10s (%8.3f %8.3f %8.3f)" %
(c, pad, self.name, vec[0], vec[1], vec[2]))
for child in self.children:
child.display(pad+" ")
return
def build(self, amt, orig, parent):
self.head = orig + self.offset
if not self.children:
return self.head
zero = (self.offset.length < Epsilon)
eb = amt.edit_bones.new(self.name)
if parent:
eb.parent = parent
eb.head = self.head
tails = Vector((0,0,0))
for child in self.children:
tails += child.build(amt, self.head, eb)
n = len(self.children)
eb.tail = tails/n
(trans,quat,scale) = eb.matrix.decompose()
self.matrix = quat.to_matrix()
self.inverse = self.matrix.copy()
self.inverse.invert()
if zero:
return eb.tail
else:
return eb.head
#
# readBvhFile(context, filepath, rot90, scale):
#
Location = 1
Rotation = 2
Hierarchy = 1
Motion = 2
Frames = 3
Deg2Rad = math.pi/180
Epsilon = 1e-5
def readBvhFile(context, filepath, rot90, scale):
fileName = os.path.realpath(os.path.expanduser(filepath))
(shortName, ext) = os.path.splitext(fileName)
if ext.lower() != ".bvh":
raise NameError("Not a bvh file: " + fileName)
print( "Loading BVH file "+ fileName )
time1 = time.clock()
level = 0
nErrors = 0
scn = context.scene
fp = open(fileName, "rU")
print( "Reading skeleton" )
lineNo = 0
for line in fp:
words= line.split()
lineNo += 1
if len(words) == 0:
continue
key = words[0].upper()
if key == 'HIERARCHY':
status = Hierarchy
elif key == 'MOTION':
if level != 0:
raise NameError("Tokenizer out of kilter %d" % level)
amt = bpy.data.armatures.new("BvhAmt")
rig = bpy.data.objects.new("BvhRig", amt)
scn.objects.link(rig)
scn.objects.active = rig
bpy.ops.object.mode_set(mode='EDIT')
root.build(amt, Vector((0,0,0)), None)
#root.display('')
bpy.ops.object.mode_set(mode='OBJECT')
status = Motion
elif status == Hierarchy:
if key == 'ROOT':
node = CNode(words, None)
root = node
nodes = [root]
elif key == 'JOINT':
node = CNode(words, node)
nodes.append(node)
elif key == 'OFFSET':
(x,y,z) = (float(words[1]), float(words[2]), float(words[3]))
if rot90:
node.offset = scale*Vector((x,-z,y))
else:
node.offset = scale*Vector((x,y,z))
elif key == 'END':
node = CNode(words, node)
elif key == 'CHANNELS':
oldmode = None
for word in words[2:]:
if rot90:
(index, mode, sign) = channelZup(word)
else:
(index, mode, sign) = channelYup(word)
if mode != oldmode:
indices = []
node.channels.append((mode, indices))
oldmode = mode
indices.append((index, sign))
elif key == '{':
level += 1
elif key == '}':
level -= 1
node = node.parent
else:
raise NameError("Did not expect %s" % words[0])
elif status == Motion:
if key == 'FRAMES:':
nFrames = int(words[1])
elif key == 'FRAME' and words[1].upper() == 'TIME:':
frameTime = float(words[2])
frameTime = 1
status = Frames
frame = 0
t = 0
bpy.ops.object.mode_set(mode='POSE')
pbones = rig.pose.bones
for pb in pbones:
pb.rotation_mode = 'QUATERNION'
elif status == Frames:
addFrame(words, frame, nodes, pbones, scale)
t += frameTime
frame += 1
fp.close()
time2 = time.clock()
print("Bvh file loaded in %.3f s" % (time2-time1))
return rig
#
# channelYup(word):
# channelZup(word):
#
def channelYup(word):
if word == 'Xrotation':
return ('X', Rotation, +1)
elif word == 'Yrotation':
return ('Y', Rotation, +1)
elif word == 'Zrotation':
return ('Z', Rotation, +1)
elif word == 'Xposition':
return (0, Location, +1)
elif word == 'Yposition':
return (1, Location, +1)
elif word == 'Zposition':
return (2, Location, +1)
def channelZup(word):
if word == 'Xrotation':
return ('X', Rotation, +1)
elif word == 'Yrotation':
return ('Z', Rotation, +1)
elif word == 'Zrotation':
return ('Y', Rotation, -1)
elif word == 'Xposition':
return (0, Location, +1)
elif word == 'Yposition':
return (2, Location, +1)
elif word == 'Zposition':
return (1, Location, -1)
#
# addFrame(words, frame, nodes, pbones, scale):
#
def addFrame(words, frame, nodes, pbones, scale):
m = 0
for node in nodes:
name = node.name
try:
pb = pbones[name]
except:
pb = None
if pb:
for (mode, indices) in node.channels:
if mode == Location:
vec = Vector((0,0,0))
for (index, sign) in indices:
vec[index] = sign*float(words[m])
m += 1
pb.location = (scale * vec - node.head) * node.inverse
for n in range(3):
pb.keyframe_insert('location', index=n, frame=frame, group=name)
elif mode == Rotation:
mats = []
for (axis, sign) in indices:
Откройте для себя мир чтения на siteknig.com - месте, где каждая книга оживает прямо в браузере. Здесь вас уже ждёт произведение Thomas Larsson - Введение в написание скриптов на Питоне для Блендера 2.5x. Примеры кода, относящееся к жанру Программирование. Никаких регистраций, никаких преград - только вы и история, доступная в полном формате. Наш литературный портал создан для тех, кто любит комфорт: хотите читать с телефона - пожалуйста; предпочитаете ноутбук - идеально! Все книги открываются моментально и представлены полностью, без сокращений и скрытых страниц. Каталог жанров поможет вам быстро найти что-то по настроению: увлекательный роман, динамичное фэнтези, глубокую классику или лёгкое чтение перед сном. Мы ежедневно расширяем библиотеку, добавляя новые произведения, чтобы вам всегда было что открыть "на потом". Сегодня на siteknig.com доступно более 200000 книг - и каждая готова стать вашей новой любимой. Просто выбирайте, открывайте и наслаждайтесь чтением там, где вам удобно.


