ObjectData плагин создает на сцене объект. Это может быть c4d.PolygonObject
или c4d.SplineObject
.
Так же ObjectData плагин может создавать модификатор, например полигонов или системы частиц.
В официальном репозитории от Maxon - есть примеры плагинов: Py-RoundedTube, Py-DoubleCircle, Py-SpherifyModifier и Py-Gravitation
Итак, первым делом создадим в папке plugins
директорию с названием своего плагина - пусть это будет SimpleGenerator
. Папка plugins
находится примерно по такому пути
C:\Users\UserName\AppData\Roaming\MAXON\Maxon CINEMA 4D Studio R20.030\plugins
. Имя пользователя UserName
и версию CINEMA в пути - меняете на свои. В папке SimpleGenerator
создаем папку res
, в которую положим файл иконки нашего плагина. Обычно это файл формата .tif
размером 32х32 пикселя.
Дальше, нам нужно создать каталоги и ресурсные файлы плагина. В нашем случае это должно выглядеть так:
SimpleGenerator/
simplegenerator.pyp
res/
c4d_symbols.h
description/
Osimplegenerator.h
Osimplegenerator.res
strings_en-US/
c4d_strings.str
description/
Osimplegenerator.str
SimpleGenerator.tif
Можно создать все ресурсные файлы вручную в текстовом редакторе. О том как это сделать можно посмотреть, например здесь.
А можно воспользоваться утилитой написанной nrosenstein
- "Cinema 4D Prototype Converter". Подробнее, как установить и использовать - смотреть тут
Правда, для ее использования понадобится Cinema 4D версии R20. Скачиваем пакет, создаем в папке plugins
папку converter
и распаковываем в нее содержимое пакета. Запускаем Cinema 4D R20 и проверяем тут в меню > Скрипт:
Теперь создаем Null-объект и назовем его SimpleGenerator
:
Если мы хотим, чтобы у созданного объекта можно было менять параметры (в том числе анимировать их) - нам нужно добавить их через опцию "Данные пользователя">"Добавить данные пользователя...". Мы создадим эти данные на нашем Null-объекте:
Сделаем две группы "Diametrs" и "Segments". В каждой группе добавим по 2 поля для данных "Lines" и "Spheres". В первой группе поля будут иметь вещественный тип, а во второй - целый. Выделяем наш Null-объект и запускаем Prototype Converter:
Заполняем поля Prototype Converter:
Sourse
- здесь указываем с какого объекта считывать пользовательские данные (в нашем случае - null-object SimpleGenerator)Plugin Name
- название нашего плагинаPlugin ID
- уникальный ID нашего плагинаResource Name
- имя файла ресурсов для нашего плагинаSymbol Prefix
- префикс для переменных в коде нашего плагинаIcon
- путь до иконки.Plugin Directory
- указываем путь к папке плагина
Больше ничего менять не нужно. На моем скрине я ввел все данные соответсвенно своим настройкам. Жмем кнопочку Create
и переходим к папке своего плагина. В ней находим файл simplegenerator.pyp
и открываем его в редакторе:
# Copyright (C) <year> <author>
# TODO: Remove redundant imports
# TODO: Update Copyright information
# TODO: Add a README file
# TODO: Keep in mind that the variables `doc` and `op` are no longer globally available
import c4d
import os
#
# Note: Check out the C4DDev project which contains tools that can parse
# resource files and generate Python code with all the symbols, similar to
# what you can see below.
#
# See https://github.com/NiklasRosenstein/c4ddev
class res(object):
SIMPLEGENERATOR_DIAMETRS_GROUP = 1000
SIMPLEGENERATOR_DIAMETRS_LINES = 1001
SIMPLEGENERATOR_DIAMETRS_SPHERES = 1002
SIMPLEGENERATOR_SEGMENTS_GROUP = 1003
SIMPLEGENERATOR_SEGMENTS_LINES = 1004
SIMPLEGENERATOR_SEGMENTS_SPHERES = 1005
res = res()
def load_bitmap(path):
path = os.path.join(os.path.dirname(__file__), path)
bmp = c4d.bitmaps.BaseBitmap()
if bmp.InitWith(path)[0] != c4d.IMAGERESULT_OK:
bmp = None
return bmp
class SimpleGeneratorData(c4d.plugins.ObjectData):
PLUGIN_ID = 1777777
PLUGIN_NAME = 'SimpleGenerator'
PLUGIN_INFO = 0
PLUGIN_DESC = 'Osimplegenerator'
PLUGIN_ICON = load_bitmap('res/icons/SimpleGenerator.tif')
PLUGIN_DISKLEVEL = 0
@classmethod
def Register(cls):
return c4d.plugins.RegisterObjectPlugin(
cls.PLUGIN_ID, cls.PLUGIN_NAME, cls, cls.PLUGIN_DESC, cls.PLUGIN_INFO,
cls.PLUGIN_ICON, cls.PLUGIN_DISKLEVEL)
def Init(self, node):
self.InitAttr(node, float, [res.SIMPLEGENERATOR_DIAMETRS_LINES])
self.InitAttr(node, float, [res.SIMPLEGENERATOR_DIAMETRS_SPHERES])
self.InitAttr(node, int, [res.SIMPLEGENERATOR_SEGMENTS_LINES])
self.InitAttr(node, int, [res.SIMPLEGENERATOR_SEGMENTS_SPHERES])
node[res.SIMPLEGENERATOR_DIAMETRS_LINES] = 14.0
node[res.SIMPLEGENERATOR_DIAMETRS_SPHERES] = 16.0
node[res.SIMPLEGENERATOR_SEGMENTS_LINES] = 10
node[res.SIMPLEGENERATOR_SEGMENTS_SPHERES] = 18
return True
if __name__ == '__main__':
SimpleGeneratorData.Register()
Это уже рабочий плагин. Он загружается и запускается без ошибок. Но пока ничего не создает. Чтобы это исправить изменяем в этой строке 0 на c4d.OBJECT_GENERATOR
:
PLUGIN_INFO = c4d.OBJECT_GENERATOR
осталось добавить метод, который будет создавать и возвращать объект:
def GetVirtualObjects(self, op, hh):
sphere = c4d.BaseObject(c4d.Osphere)
sphere[c4d.PRIM_SPHERE_RAD] = 55
sphere[c4d.PRIM_SPHERE_SUB] = 12
sphere[c4d.PRIM_SPHERE_PERFECT] = True
return sphere
и конечно это:
def __init__ (self):
self.SetOptimizeCache(True)
Разумеется, оба метода должны принадлежать классу SimpleGeneratorData
, который является основным для нашего плагина и наследуется от c4d.plugins.ObjectData
.
Вот теперь наш плагин при запуске создает на сцене сферу! Правда, у сферы все параметры фиксированы, а созданные нами никак не влияют на неё... Сейчас мы это исправим!
def GetVirtualObjects(self, op, hh):
sphere = c4d.BaseObject(c4d.Osphere)
sphere[c4d.PRIM_SPHERE_RAD] = op[c4d.SIMPLEGENERATOR_DIAMETRS_SPHERES]
sphere[c4d.PRIM_SPHERE_SUB] = op[c4d.SIMPLEGENERATOR_SEGMENTS_SPHERES]
sphere[c4d.PRIM_SPHERE_PERFECT] = True
return sphere
Изменение полей Spheres
приводит к изменениям диаметра сферы и количества её сегментов! Но на этом еще не всё. Мы ведь зачем-то создали и параметры Lines
, значит мы хотели создать по крайней мере два объекта. Но метод GetVirtualObjects
всегда должен возвращать только один объект! Тут мы можем либо создать Null-объект, подчинить ему все остальные объекты и вернуть этот Null. Либо сделать один из имеющихся объектов корневым и остальные подчинить ему в нужной иерархии (и вернуть корневой объект). Мы поступим первым способом:
def GetVirtualObjects(self, op, hh):
null = c4d.BaseObject(c4d.Onull)
linispline = c4d.SplineObject(2, c4d.SPLINETYPE_LINEAR)
# Создаем первую точку сплайна
point = c4d.Vector(0., 0., 0.)
# Добавляем точку в сплайн
linispline.SetPoint(0, point)
# Создаем вторую точку сплайна
point2 = c4d.Vector(0., 30., 0.)
# Добавляем точку в сплайн
linispline.SetPoint(1, point2)
# Создаем круглый сплайн
circle = c4d.BaseObject(c4d.Osplinenside)
circle[c4d.PRIM_NSIDE_RADIUS] = op[c4d.SIMPLEGENERATOR_DIAMETRS_LINES]
circle[c4d.PRIM_NSIDE_SIDES] = op[c4d.SIMPLEGENERATOR_SEGMENTS_LINES]
# Создание объекта Osweep
sweep = c4d.BaseObject(c4d.Osweep)
sweep[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Y] = op[c4d.SIMPLEGENERATOR_DIAMETRS_SPHERES]
linispline.InsertUnder(sweep)
circle.InsertUnder(sweep)
circle.Message(c4d.MSG_UPDATE)
sphere = c4d.BaseObject(c4d.Osphere)
sphere[c4d.PRIM_SPHERE_RAD] = op[c4d.SIMPLEGENERATOR_DIAMETRS_SPHERES]
sphere[c4d.PRIM_SPHERE_SUB] = op[c4d.SIMPLEGENERATOR_SEGMENTS_SPHERES]
sphere[c4d.PRIM_SPHERE_PERFECT] = True
sphere.InsertUnder(null)
sweep.InsertUnder(null)
return null
Конечно, можно было использовать примитив-объект Cylinder
вместо того чтобы создавать сплайн и свип к нему. А можно было построить объект из отдельных полигонов и еще кучей способов на которые хватит вашей фантазии!
В итоге мы получили такой объект, созданный нашим плагином:
Теперь, вы можете создавать и свои объекты, назначать им параметры и свою логику. Сконвертируйте полученный объект в полигональный клавишей C
- вы увидите что он состоит из тех объектов, которые мы создавали в методе GetVirtualObjects
PS. Созданный таким образом плагин - прекрасно работает в Cinema 4d R23 !