В Блендере есть два различных типа свойств: ID-свойства и RNA-свойства. RNA-свойства расширяют определение структуры данных. Они должны быть объявлены до того, как будут использоваться.
Я потратил некоторое время на выяснение того, как же расшифровывается и что означает аббревиатура RNA для программирования на Питоне в Блендере. Может быть, я был недостаточно настойчив в поисках, но всё, что я нашел — это РНК, Рибонуклеиновая кислота. Разработчики применили химико-биологическую метафору для обозначения реальных структур данных на языке С (DNA, в переводе ДНК) и соответствующих им структур на Питоне (RNA, в переводе РНК). С понятием ID, думаю все и так знакомы, это сокращение слова Идентификатор. - прим. пер.
bpy.types.Object.myRnaInt = bpy.props.IntProperty(
name = "RNA int",
min = -100,
max = 100,
default = 33)
Как только RNA-свойства были объявлены, они будут доступны через точечный синтаксис:
cube.myRnaInt = -99
После декларации RNA-свойства myRnaInt расширяет определение структуры данных Object, каждый объект будет иметь это свойство.
ID-cвойство добавляется к единственному блоку данных, не влияя на другие данные того же самого типа. Ему не нужна какая-либо предварительная декларация, но оно автоматически определяется при присвоении, напр.
cube.data["MyIdInt"] = 4711
ID-свойства могут только быть целыми, вещественными, и строками; другие типы автоматически будут преобразованы. Следовательно, строка
cube.data["MyIdBool"] = True
определяет целое ID-свойство, а не логическое.
Не знаю, как в предыдущих версиях, а в 2.57 вполне можно определять списки — прим. пер.
Свойства сохраняются в blend-файле, но декларации свойств — нет.
Вот скрипт, который создает три меша, назначает различные свойства и печатает их величины в консоли.
#----------------------------------------------------------
# File properties.py
#----------------------------------------------------------
import bpy
from bpy.props import *
# Очистка сцены и создание нескольких объектов
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
bpy.ops.mesh.primitive_cube_add(location=(-3,0,0))
cube = bpy.context.object
bpy.ops.mesh.primitive_cylinder_add(location=(0,0,0))
cyl = bpy.context.object
bpy.ops.mesh.primitive_uv_sphere_add(location=(3,0,0))
sphere = bpy.context.object
# Определение RNA-свойства для каждого объекта
bpy.types.Object.myRnaInt = IntProperty(
name = "RNA int",
min = -100, max = 100,
default = 33)
bpy.types.Object.myRnaFloat = FloatProperty(
name = "RNA float",
default = 12.345,
min = 1, max = 20)
bpy.types.Object.myRnaString = StringProperty(
name = "RNA string",
default = "Ribonucleic acid")
bpy.types.Object.myRnaBool = BoolProperty(
name = "RNA bool")
bpy.types.Object.myRnaEnum = EnumProperty(
items = [('one', 'eins', 'un'),
('two', 'zwei', 'deux'),
('three', 'drei', 'trois')],
name = "RNA enum")
# Присвоение RNA-свойств кубу
cube.myRnaInt = -99
cube.myRnaFloat = -1
cube.myRnaString = "I am an RNA prop"
cube.myRnaBool = True
cube.myRnaEnum = 'three'
# Создание ID-свойств для меша куба присвоением значений.
cube.data["MyIdInt"] = 4711
cube.data["MyIdFloat"] = 666.777
cube.data["MyIdString"] = "I am an ID prop"
cube.data["MyIdBool"] = True
# Печать всех свойств
def printProp(rna, path):
try:
print(' %s%s =' % (rna.name, path), eval("rna"+path))
except:
print(' %s%s does not exist' % (rna.name, path))
for ob in [cube, cyl, sphere]:
print("%s RNA properties" % ob)
printProp(ob, ".myRnaInt")
printProp(ob, ".myRnaFloat")
printProp(ob, ".myRnaString")
printProp(ob, ".myRnaBool")
printProp(ob, ".myRnaEnum")
print("%s ID properties" % ob.data)
printProp(ob.data, '["MyIdInt"]')
printProp(ob.data, '["MyIdFloat"]')
printProp(ob.data, '["MyIdString"]')
printProp(ob.data, '["MyIdBool"]')
Скрипт напечатает следующий результат на консоль:
RNA properties Cube.myRnaInt = -99
Cube.myRnaFloat = 1.0
Cube.myRnaString = I am an RNA prop
Cube.myRnaBool = True
Cube.myRnaEnum = three
ID properties
Cube.001["MyIdInt"] = 4711
Cube.001["MyIdFloat"] = 666.777
Cube.001["MyIdString"] = I am an ID prop
Cube.001["MyIdBool"] = 1
RNA properties
Cylinder.myRnaInt = 33
Cylinder.myRnaFloat = 12.345000267028809
Cylinder.myRnaString = Ribonucleic acid
Cylinder.myRnaBool = False
Cylinder.myRnaEnum = one
ID properties
Cylinder["MyIdInt"] does not exist
Cylinder["MyIdFloat"] does not exist
Cylinder["MyIdString"] does not exist
Cylinder["MyIdBool"] does not exist
RNA properties
Sphere.myRnaInt = 33 Sphere.myRnaFloat = 12.345000267028809
Sphere.myRnaString = Ribonucleic acid
Sphere.myRnaBool = False
Sphere.myRnaEnum = one
ID properties
Sphere["MyIdInt"] does not exist
Sphere["MyIdFloat"] does not exist
Sphere["MyIdString"] does not exist
Sphere["MyIdBool"] does not exist
Все три объекта имеют RNA-свойства, поскольку они являются расширением типа данных Object. RNA-свойствам Куба программой присвоены значения, кроме значения myRnaFloat, которое не может быть меньше чем 1. Цилиндру и сфере никаких свойств присвоено не было, но они все равно имеют RNA-свойства со значением по умолчанию.
Мешу куба программой были заданы ID-свойства. Заметьте, что свойство MyIdBool является целочисленной 1, а не логической True.
Свойства Объекта отображаются в панели пользовательского интерфейса под Properties, и также в контексте объекта. Свойства меша можно найти в контексте меша.
Как мы видели в распечатке, мы можем иметь доступ к RNA-свойствам объекта сферы. Тем не менее, они не появляются в интерфейсе пользователя. Очевидно, только присвоенные значения свойств сохраняются в блоке данных Объекта. Мы можем использовать RNA-свойство, которое не присвоено в скрипте; при этом берется значение по умолчанию. В противовес этому, если мы попытаемся получить доступ к незаданному ID-свойству, будет возбуждена ошибка.
Свойства совместимы со связями файлов. Сохраните blend-файл и привяжите (link) куб в новый файл. Как RNA-, так и ID-свойства появляются в новом файле, но они серые, поскольку они не могут быть доступны в связанном файле.
Если мы проксим (proxify) связанный куб, свойства объекта принадлежат блоку данных прокси-объекта, и могут быть модифицированы в связанном файле. В противовес этому, свойства меша принадлежат блоку данных меша и не могут изменяться.
Как упомянуто выше, свойства сохранены в blend-файлах, но декларации свойств — нет. Закройте и перезапустите Блендер и откройте файл, который мы сохранили выше. Свойства myRnaBool и myRnaEnum окажутся преобразованными в целые. Фактически, они и были сохранены как целые всё время, но отображались как логические и перечисления из-за продекларированных свойств, сохранённых в типе данных Object.
Чтобы подтвердить, что RNA-свойства превратились в ID-свойства, выполните следующий скрипт.
#----------------------------------------------------------
# File print_props.py
#----------------------------------------------------------
import bpy
def printProp(rna, path):
try:
print(' %s%s =' % (rna.name, path), eval("rna"+path))
except:
print(' %s%s does not exist' % (rna.name, path))
ob = bpy.context.object print("%s RNA properties" % ob)
printProp(ob, ".myRnaInt")
printProp(ob, ".myRnaFloat")
printProp(ob, ".myRnaString")
printProp(ob, ".myRnaBool")
printProp(ob, ".myRnaEnum")
print("%s ID properties" % ob)
printProp(ob, '["myRnaInt"]')
printProp(ob, '["myRnaFloat"]')
printProp(ob, '["myRnaString"]')
printProp(ob, '["myRnaBool"]')
printProp(ob, '["myRnaEnum"]')
print("%s ID properties" % ob.data)
printProp(ob.data, '["MyIdInt"]')
printProp(ob.data, '["MyIdFloat"]')
printProp(ob.data, '["MyIdString"]')
printProp(ob.data, '["MyIdBool"]')
Этот скрипт выведет следующий текст на терминале.
RNA properties
Cube.myRnaInt does not exist
Cube.myRnaFloat does not exist
Cube.myRnaString does not exist
Cube.myRnaBool does not exist
Cube.myRnaEnum does not exist
ID properties
Cube["myRnaInt"] = -99
Cube["myRnaFloat"] = 1.0
Cube["myRnaString"] = I am an RNA prop
Cube["myRnaBool"] = 1
Cube["myRnaEnum"] = 2
ID properties
Cube.001["MyIdInt"] = 4711
Cube.001["MyIdFloat"] = 666.777
Cube.001["MyIdString"] = I am an ID prop
Cube.001["MyIdBool"] = 1
Если мы восстановим декларации свойств, ID-свойства преобразуются обратно в RNA-свойства.
Эта программа ожидает, что активный объект — это арматура. Она сохраняет угол вращения каждой editbone как свойство соответствующей кости, и в конце выводит величины свойств на терминале. При выполнении с выбранной арматурой на изображении ниже, результат на терминале выглядит следующим образом.
Head 3.1416
Arm_L 1.5708
Leg_R -2.7646
Leg_L 2.7646
Arm_R -1.5708
Torso 3.1416
Заметьте, что величины свойств выражены в радианах. В интерфейсе углы отображаются в градусах, но при доступе из Питона они выражены в радианах. Тем не менее, свойство Roll - это просто некоторая вещественная переменная, и Блендер не знает, что его предполагается использовать как угол.
Для нахождения свойства в интерфейсе пользователя, нам нужно выбрать кость в режиме позы,и затем переключиться в режим редактирования, как показано на изображении.
Этот код действительно несколько полезен для скрипта, который перенастраивает данные, полученные от захвата движения (motion capture). Для того, чтобы делать это правильно, нам нужно знать углы поворота roll. Тем не менее, их нельзя получить, если арматура связана с другим файлом через прокси. Для того, чтобы получить доступ к углу поворота rig.data.edit_bones[name].roll, арматуру нужно переключить в режим редактирования, который не доступен для связанных объектов. Но если скрипт выполнен в файле, где арматура определена, свойство Roll может быть доступно из связанного файла как rig.pose.bones[name].bone["Roll"].
#----------------------------------------------------------
# File bone_roll.py
#----------------------------------------------------------
import bpy
def createBoneRollProps(rig):
if rig.type != 'ARMATURE':
raise NameError("Object not an armature")
# Объект не является арматурой
bpy.context.scene.objects.active = rig
try:
bpy.ops.object.mode_set(mode='EDIT')
editable = (len(rig.data.edit_bones) > 0)
except:
editable = False
rolls = {}
if editable:
for eb in rig.data.edit_bones:
rolls[eb.name] = eb.roll
bpy.ops.object.mode_set(mode='POSE')
for pb in rig.pose.bones:
pb.bone["Roll"] = rolls[pb.name]
else:
try:
bpy.ops.object.mode_set(mode='POSE')
except:
raise NameError("Armature is not posable. Create proxy")
# У арматуры не доступно позирование. Создайте прокси
for pb in rig.pose.bones:
try:
rolls[pb.name] = pb.bone["Roll"]
except:
raise NameError("Create roll props in asset file")
# Создайте свойство roll в файле актива
return rolls
rolls = createBoneRollProps(bpy.context.object)
for (bname, roll) in rolls.items():
print(" %16s %8.4f" % (bname, roll))