![]() |
3DCoat
3D-COAT 4.9.xx
3DCoat is the one application that has all the tools you need to take your 3D idea from a block of digital clay all the way to a production ready, fully textured organic or hard surface model.
|
Для регистрации членов класса с целью последующего показа класса в UI или сериализации необходимо, чтобы класс был наследником BaseClass. Регистрацию делай так
Или в секции SAVE ... ENDSAVE так (архаичный способ, для нового класса так не надо)
Или так (тоже по древнему, но лучше, чем тулить всё в хидер)
Используем, если тип переменной variable_name не является наследником BaseClass.
Соответствие типов данных c++ и class_type глянь в этой табличке.
| c++ type | class_type |
|---|---|
| float | _float |
| int | _int |
| bool | _bool |
| DWORD | _DWORD |
| DWORD | _color (когда нужно интерпретировать DWORD как цвет) |
| short | _short |
| BYTE | _byte |
| char | _char |
Используй в случае, если тип переменной является наследником BaseClass.
Если хотишь, чтобы переменные типа int и float появлялись в UI как слайдеры (ползунки).
Можешь регистрировать функции и члены класса. В UI они появятся как кнопки, при нажатии на кнопку стартует заданная тобой функция.
В команде выше функцию объявляешь так
При регистрации можно модифицировать идентификатор в UI, который соответствует переменной. При использовании предыдущих команд этот идентификатор такой же, как имя переменной.
С помощью специальных директив можно управлять различными свойствами объекта в UI. Директивы располагаются как префикс перед идентификатором. Если тип переменной variable_name не является наследником BaseClass, используй команду
Например
В этом случае имя переменной в UI ты не увидишь, только её значение. Однако, есть проблема: если несколько переменных вдруг окажутся зарегистрированы с пустым именем, то при сохранении данных класса в виде XML получишь два пустых тега: данные о значении переменной потеряешь. Поэтому лучше сильно не умничать, а регистрировать так
В этом случае имя переменной также не увидишь в UI, но в XML у неё будет ID $integer_variable. Это пример, зачем нужны приставки в определении переменных.
Для регистрации вызова функций.
Используем для функций, если необходимо динамически формировать идентификатор UI.
При этом name_for_UI_with_quotes будет char*.
Набор приставок перед идентификаторами. Приставки используй в том же порядке, как они приведены в таблице.
| Приставка | Что делает |
|---|---|
| {0xHEXADECMAIL_VALUE} | Цвет подложки для этого поля в UI |
| {fc0xHEXADECMAIL_VALUE} | Цвет шрифта для поля |
| [scale=floating_value] | Дополнительный масштабный коэффициент для редактируемого поля |
integer_value <td> integer_value - идентификатор группы для чекбоксов. В этом случае чекбокс показывается как radio-button. Чекбоксы одной группы должны использовать одинаковые integer_value. Например <tr><td>^ <td> REG_MEMBER_EX( _bool, Choice1,1Choice1 ); | |
| ^ | REG_MEMBER_EX( _bool, Choice2, <tt>1Choice2 ); <tr><td>^ <td> REG_MEMBER_EX( _bool, Choice3,1Choice3 ); |
| ^ | В этом случае мы получим 3 radio buttons и только один из низ может быть выделен. |
| % | Применять масштаб сцены к этому контролу (обычно это поле float или float-slider) |
| {hotkey hotkey_id} | hotkey будет назначен хоткееем по умолчанию для кнопки. Например |
| ^ | {hotkey CTRL ALT SHIFT A} |
| ^ | {hotkey SHIFT NUM_PLUS} |
| ^ | Список всех идентификаторов |
| ^ | 0 1 2 3 4 5 6 7 8 9 |
| ^ | A B C D E F G H I J K L M N O P Q R S T U V W X Y Z |
| ^ | ENTER ESC NUM0 NUM1 NUM2 NUM3 NUM4 NUM5 NUM6 NUM7 NUM8 NUM9 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 PGDN PGUP HOME END INS NUM_PLUS NUM_MINUS NUM* NUM/ < > ? |
| ^ | F11 F12 ~ SPACE BACK PAUSE DELETE [ ] ; ' , . / |
| ^ | Left Right Up Down |
| ^ | Lwin Rwin App Lmemu Rmenu Tab Back - + |
| ^ | LMB RMB MMB CTRL ALT SHIFT X1 X2 |
| # | Не показывать подсказку (HINT) |
| ~ | Функция недоступна в Demo-режиме |
| * | Принудительное выравнивание влево |
| $ | Не показывать имя поля, только значение |
| ! | Показывать только значение, read only |
| [^] | Иконка Move_area_16.tga |
| ^ | Read only |
| {icon filename_path} | Показать иконку |
Если в UI необходим выпадающий список, то регистрируем переменную типа int так
Или
где ENUM_ID - енумератор (набор значений для выпадающего списка).
Обычно, енумератор создаётся так
Этот код лучше всего размещать внутри секции SAVE ... ENDSAVE.
Пример
При сохранении или же показе данных экземпляра класса, структура представления может динамически меняться. Для этого раньше использовали маскирование элементов с помощью битовых масок. Ты ведь помнишь, что теперь классы мы регистрируем динамически и маски нам не нужны, да? Нет? См. выше.
Например
Обрати внимание на функцию GetClassMask(): она возвращает битовую маску элементов, которые разрешено показывать. SAVE_SECTION определяет битовую маску для всех последующих элементов до следующей директивы SAVE_SECTION. Если операция побитового AND для GetClassMask и SAVE_SECTION возвращает ненулевой результат, то элемент показывается в UI или будет принимать участие в сериализации.
Элементы в UI можно размещать в несколько столбиков, контролировать, будет ли ширина поля фиксированной, или же подбираться автоматически. За это отвечает директива
Внутри COLUMNS размещаются (без кавычек ") относительная или абсолютная ширина последующих элементов. Например:
Вариант команды COLUMNS
Ещё вариант
Все три поля в данном случае выводятся без названия, только значения. Ширина контрола, соответствующего bool_field будет 16 пкс, а ширина поля IntField1 будет вдвое больше ширины IntField2.
В качестве дополнительных элементов форматирования удобно использовать
Существует несколько модификаторов при регистрации членов класса. Все модификаторы пишутся *перед* командой регистрации элемента.
Указывает на то, что данных элемент не участвует в сериализации класса, то есть не сохраняется и не считывается из XML-структуры.
Элемент может участвовать в сериализации, но будет невидим в UI.
Модификаторы NOSAVE и INVISIBLE полезны для согласования как класс увидит художник и как класс сохранится в XML.
При регистрации статических переменных класса или же глобальных переменных не членов класса, обязательно указывай, что они являются статическими.
Если класс является наследником BaseClass, то регистрируем класс так
Или так
В 3DCoat существует несколько полезных классов, для которых уже написан сравнительно удобный редактор. Вот табличка этих удобств.
| Класс / Тип | Как регистрировать | Что творит |
|---|---|---|
| _str | REG_AUTO/REG_AUTO_EX | Редактирование строки, однострочный редактор. |
| _int | REG_MEMBER( _TextureID, ... ) | Выбор текстуры, в результате в переменной типа int будет либо -1 , либо идентификатор текстуры. |
| One2DCurve | REG_AUTO | Редактирование кривых. |
| ClassArray<One2DCurve> | REG_AUTO | Редактирование группы кривых. |
| _str | REG_AUTO_EX(path,save:*.ext1;*.ext2;) | Строка используется как путь к файлу. В зависимости от использования load или save при выбое пути открывается файловый диалог для загрузки или же сохранения файлов. |
| ^ | REG_AUTO_EX(path,load:*.ext1;*.ext2;) | ^ |
| _bool | REG_MEMBER(_bool,Visible); | Если элемент класса зарегистрирован как _bool и называется Visible, то он будет отображаться в виде иконки с глазиком. |
В некоторых случаях необходимо создать собственный редактор класса, **отличный** от стандартного. В этом случае смело перегружай функцию
При создании интерфейса эта функция вызывается для каждого зарегистрированного элемента класса. Ты можешь идентифицировать этот член по его имени в FieldName. Контролы можешь строить внутри W. До вызова OnCreateControl() в W уже создано стандартное представление данного элемента. Его размер и расположение записаны в BaseWidget::Rect, но ты всё можешь поменять.
Посмотри примеры реализации собственного редактора в этих классах
Вызывается на каждом кадре при редактировании класса.
Вызывается после изменения пользователем элемента класса. В настоящий момент возвращаемое значение не играет роли.
Вызывается непосредственно перед изменением пользователем элемента класса. Полезна для Undo.
Используются для реализации Drag & Drop. Для углублённого понимания смотри примеры в коде Кота.
Каждый наследник BaseClass может быть сохранён в поток данных или же считан из него. Примеры сохранения и считывания