illumium.org

Главная › Блоги › Блог kayo

Дополнение генерации сот для Blender 2.5 на Python 3

kayo — Вс, 20/11/2011 - 18:31

Актуальная ветка Blender 2.5 предоставляет улучшенные механизмы интеграции со сторонними расширениями и дополнениями на Python 3. Поскольку я имел некоторый опыт скриптинга под Blender 2.4, то мне давно уже хотелось поработать с новым API. Одним из важнейших типов дополнений являются скрипты создания всевозможных трёхмерных форм. В штатной поставке программы можно найти генераторы правильных многогранников разных степеней, всевозможных шестерней, болтов и гаек, и многого другого. В этом посте мы создадим генератор структуры, напоминающей пчелиные соты. Это будет весьма простое дополнение, однако достаточное, чтобы продемонстрировать процесс разработки под Blender.

Введение

Blender ветки 2.5 использует Python 3, поэтому нам придётся писать свои расширения на нём. API программы состоит из нескольких модулей, наиболее важным из которых для нас будет bpy, предоставляющий интерфейсы для работы с самим редактором. Следующие подмодули наиболее интересны для нас:

  • bpy.context — контекст редактора;
  • bpy.ops — операторы редактирования;
  • bpy.props — интерфейс работы со свойствами;
  • bpy.data — работа с датаблоками различных объектов.

Итак, давайте сразу разъясним смысл и назначение этих компонентов. В коде наших дополнений мы можем произвольно обращаться к объектам программы посредством контекста. Каждая операция редактирования, реализуется в Blender путём применения оператора. Оператор может иметь методы, например, метод проверки применимости (poll), или метод исполнения (execute). Для того, чтобы гибко управлять поведением оператора, мы можем объявить в нём некоторые свойства. Основным типом объектов в программе являются так называемые Mesh-ы. Поскольку нам потребуется создавать и изменять формы, мы будем обращаться к данным Mesh-а посредством соответствующего интерфейса.

Концепт дизайн

Самым первым шагом желательно нарисовать то, что мы хотим сделать, это послужит основой для написания кода и избавит от многих непоняток относительно будущей реализации. Наше расширение должно создавать примерно такую плоскую форму:

honeycomb.png

А так она должна тесселироваться в трёхмерные примитивы:

honeycomb_tessel.png

Ниже я нарисовал структуру для того, чтобы лучше представить способ объединения вершин в грани:

honeycomb_tessel_fill.png

По сути мы имеем двумерную матрицу, каждый элемент которой представляет набор номеров вершин соответствующей соты.

Программирование дополнения

Самое время приступить к написанию кода. Для меня отправной точкой в написании дополнения послужило исследование кода шаблонов в каталоге /usr/share/blender/<version>/scripts/templates, а также, других стандартных дополнений. В частности расковырял operator_mesh_add.py и addons/add_mesh_BoltFactory, это помогло получить общее представление о законченной структуре кода, которую должно иметь дополнение. Не лишним, конечно, было чтение документации по API на сайте проекта.

Объявление дополнения

Итак, чтобы Blender понял, что наш код является дополнением для него, мы должны создать словарь с именем bl_info следующего вида:

bl_info = {
    "name": "HoneyComb",
    "author": "Kayo Phoenix <kayo@illumium.org>",
    "version": (0, 1),
    "blender": (2, 5, 7),
    "api": 35853,
    "location": "View3D > Add > Mesh > HoneyComb",
    "description": "Adds HoneyComb Mesh",
    "warning": "",
    "wiki_url": "http://illumium.org",
    "category": "Add Mesh"
}

Поля "blender" и "api" содержат номера минимальных значений версии редактора и интерфейса для языка Python соответственно, с которыми данное расширение совместимо. Если вы не знаете наверняка с какой версией будет работать ваш код, пишите ту, которую используете в момент разработки дополнения.

Поле "location" содержит строку расположения дополнения в системе меню, это лишь подсказка для пользователя. В поле "wiki_url" конечно должен быть актуальный путь до страницы расширения в wiki редактора, но на время разработки вписал туда url этого сайта. Поле "category" предписывает Blender-у куда до́лжно поместить дополнение во вкладке управления дополнениями.

Регистрация дополнения

Следующим шагом нам потребуется реализовать код загрузки и выгрузки нашего дополнения. В Blender это называется регистрацией:

import bpy

def register():
    bpy.utils.register_module(__name__)

def unregister():
    bpy.utils.unregister_module(__name__)
    
if __name__ == "__main__":
    register()

Функция bpy.utils.register_module автоматически регистрирует все найденные классы в модуле, являющиеся потомками специальных классов API.

Создание оператора редактирования

Одним из таких классов может выступать оператор, который мы как раз сейчас и объявим:

class add_mesh_honeycomb(bpy.types.Operator):
    bl_idname = "mesh.honeycomb_add"
    bl_label = "Add HoneyComb"
    
    ##### POLL #####
    @classmethod
    def poll(cls, context):
        return context.scene is not None
    
    ##### EXECUTE #####
    def execute(self, context):
        return {'FINISHED'}

Мы определяем параметр bl_idname так, чтобы оператор был доступен в Blender как bpy.ops.mesh.honeycomb_add. Значение bl_label это человекопонятное название нашего оператора.

Метод poll объявлен в стиле Python 3 как метод непосредственно класса, а не объектов этого класса, используется он для того, чтобы определить возможно ли применение нашего оператора в данном конкретном контексте context. Здесь мы просто проверяем нахождение в контексте сцены.

Обычный метод объектов класса execute вызывается для выполнения оператора. Конечно же, между двоеточием и return-ом должно что-то быть, пока же наш оператор ничего не делает.

Добавление оператора в меню

Мы создали оператор, но ещё не предоставили возможность вызывать его из интерфейса пользователя через меню, так давайте сделаем это, исправив наш код инициализации так:

def add_mesh_honeycomb_button(self, context):
    self.layout.operator(add_mesh_honeycomb.bl_idname,
                         text = bl_info['name'], icon="PLUGIN")

def register():
    bpy.utils.register_module(__name__)
    
    bpy.types.INFO_MT_mesh_add.append(add_mesh_honeycomb_button)

def unregister():
    bpy.utils.unregister_module(__name__)
    
    bpy.types.INFO_MT_mesh_add.remove(add_mesh_honeycomb_button)
    
if __name__ == "__main__":
    register()

Собственно на этом регистрация дополнения успешно заканчивается, осталось реализовать метод выполнения оператора.

Использование свойств

Чтобы предоставить возможность гибко настраивать работу оператора, нам потребуется добавить к нему некоторый набор свойств, представленных в bpy.props типов. Типы свойств поддерживают различные параметры, например, название и описание, ограничения значения, минимальный шаг и другие в зависимости от типа. Также есть возможность использовать свой метод update, вызываемый, когда значение параметра было изменено. Итак, расширим класс нашего оператора следующим образом:

from bpy.props import *

def edge_max(diam):
    return diam * sin(pi / 3)

class add_mesh_honeycomb(bpy.types.Operator):
    …
    
    rows = IntProperty(
        name = 'Num of rows', default = 2,
        min = 1, max = 100,
        description='Number of comb\'s rows')
    
    cols = IntProperty(
        name = 'Num of cols', default = 2,
        min = 1, max = 100,
        description='Number of comb\'s columns')
    
    def fix_edge(self, context):
        m = edge_max(self.diam)
        if self.edge > m: self.edge = m
    
    diam = FloatProperty(
        name = 'Comb Diameter', default = 1.0,
        min = 0.0, update = fix_edge,
        description='Diameter of the comb')
    
    edge = FloatProperty(
        name = 'Edge Width', default = 0.1,
        min = 0.0, update = fix_edge,
        description='Width of the comb edge')
    
    # generic transform props
    view_align = BoolProperty(
        name="Align to View",
        default=False)
    location = FloatVectorProperty(
        name="Location",
        subtype='TRANSLATION')
    rotation = FloatVectorProperty(
        name="Rotation",
        subtype='EULER')

Поскольку максимальная ширина границы зависит от диаметра, то при изменении её и диаметра вызывается метод fix_edge, который фиксирует это значение, если оно превышает порог. В составе объекта свойства Blender будут представлены обычными свойствами соответствующих типов, например, FloatProperty станет float-ом, IntProperty — int-ом и т.д.

Реализация оператора

Я решил создать класс honeycomb_geometry для генерации данных mesh-а, который будет просто получать значения параметров и генерировать вершины и грани. Класс получился весьма не маленьким, поэтому не буду его здесь приводить как он есть, покажу лишь объявление:

class honeycomb_geometry():
    # Инициализация
    def __init__(self, rows, cols, D, E):
        # rows — число строк,
        # cols — число столбцов,
        # D — диаметр ячейки,
        # E — толщина стенки
    
    # Генерирование формы
    def generate(self):
        # verts — список тьюплов вершин,
        # faces — список тьюплов граней
        return verts, faces

Каждая вершина в возвращаемом списке вершин представлена тремя координатами x, y, z. Каждая грань в списке граней — набор из трёх или четырёх индексов вершин, которые эту грань образуют.

Теперь допишем выполнение оператора:

class add_mesh_honeycomb(bpy.types.Operator):
    bl_options = {"REGISTER", "UNDO"}
    …
    ##### EXECUTE #####
    def execute(self, context):
        # создаём новый датаблок
        mesh = bpy.data.meshes.new(bl_info['name'])
        
        # инициализируем экземпляр генератора
        comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
        # генерируем геометрию
        verts, faces = comb.generate()
        
        # загружаем вершины и грани
        mesh.from_pydata(vertices = verts, edges = [], faces = faces)
        # обновляем mesh
        mesh.update()
        
        # связываем датаблок с объектом
        object_utils.object_data_add(context, mesh, operator=self)
        
        return {'FINISHED'}

Здесь я также добавил bl_options, без опций REGISTER и UNDO, пользователю не будет доступна панель свойств оператора. Собственно, это всё, что нам потребуется сделать, Blender сам позаботится о создании соответствующей панели свойств генератора сот, когда пользователь добавит соответствующий объект.

Тестирование дополнения

Чтобы подключить наше дополнение, проще всего сделать его доступным в одном из стандартных путей, например /usr/share/blender/<version>/scripts/addons или ~/.blender/<version>/scripts/addons. Я просто сделал в последнем каталоге символическую ссылку, ведущую к моему скрипту.

Теперь, если вы всё правильно сделали, в настройках Blender во вкладке Add-Ons в соответствующей категории вы можете найти и включить своё дополнение:

honeycomb_blender_addon.png

Теперь в меню и в панели быстрого запуска должен появиться новый оператор создания сот.

Хотелось бы особо отметить, что Blender сам не будет перезагружать ваше дополнение, поэтому в процессе разработки после каждого исправления, чтобы изменения стали актуальны в редакторе, необходимо выполнить перезагрузку скриптов, например, нажав F8 в окне программы.

Собственно, пробуем наше дополнение, и вот примерно так выглядит процесс:

honeycomb_blender_creating.png

А такие штуки можно с этим делать дальше:

honeycomb_blender_extruded.png

Надеюсь, статья получилась полезная и интересная, если это не так, прошу только ногами не бить ^_~

Ну и конечно же, как обычно, прикладываю получившийся скрипт.

UPD: Также залил небольшой скринкаст на vimeo.

ВложениеРазмер
add_mesh_honeycomb.py_.xz2.56 КБ
  • addon
  • blender
  • python
  • Бортовой журнал Иллюмиума

Эта пять. Очень спасибо за

Анонимус (не проверено) — Сб, 01/09/2012 - 02:08
Эта пять. Очень спасибо за разъяснения, подготовительная часть тоже великолепна.Будем учиться :)
  • ответить

Отправить комментарий

Содержимое этого поля является приватным и не будет отображаться публично.
  • Доступные HTML теги: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Syntax highlight code surrounded by the {syntaxhighlighter SPEC}...{/syntaxhighlighter} tags, where SPEC is a Syntaxhighlighter options string or "class="OPTIONS" title="the title".

Подробнее о форматировании

CAPTCHA
Этот вопрос задается для того, чтобы выяснить, являетесь ли Вы человеком или представляете из себя автоматическую спам-рассылку.
   ___    _   _                 __        __   ___  
/ _ \ | | | | ___ __ _ \ \ / / / _ \
| | | | | | | | / _ \ / _` | \ \ /\ / / | (_) |
| |_| | | |_| | | __/ | (_| | \ V V / \__, |
\__\_\ \___/ \___| \__, | \_/\_/ /_/
|_|
Введите код, изображенный в стиле ASCII-арт.
RSS-материал

Навигация

  • Подшивки
  • Фотоальбомы

«Иллюмиум» на якоре.

Работает на Drupal, система с открытым исходным кодом.

(L) 2010, Illumium.Org. All rights reversed ^_~