Чтобы удобно редактировать VEX-код в Visual Studio Code вам нужно будет поставить два расширения. Идите в пункт меню Extensions, сделайте поиск по фразе VEX и установите расширения VEX (Houdini Vex Support for VSCode), а также Houdini Vex Help.
После этого vfl-файлы будут показываться с правильным синтаксисом. Кроме того, когда курсор находится над ключевым словом, то при нажатии клавиши F2 будет открываться браузер хелпа Гудини со статьей по этому ключевому слову.
Чтобы открывать Векс-код из врангла в более удобном редакторе, щелкните правой кнопкой мыши по полю ввода кода и выберите Expression — Edit in external editor. Если вы до этого не устанавливали внешний редактор, нажмите Yes в появившемся окошке, после чего откроется окно выбора исполняемого файла вашего любимого редактора.
Код из врангла сохраняется во временный файл, который открывается в редакторе. Когда вы сохраняете его, код автоматически апдейтится во врангле. Чтобы убрать эту связь, можно выбрать пункт меню Expression — Remove file watcher.
Если вы хотите привязать другой внешний редактор, удалите или отредактируйте файл ExternalEditor.cfg, который лежит в папке /User/Documents/HoudiniXX.X/
Надо отметить, что внешний редактор можно использовать не только для Врангла, но и для ноды Python и вообще любых других полей ввода параметров. При этом, когда вы сохраняете код из ноды Python, он сохранится с расширением .py, и ваш редактор сможет автоматически переключить синтаксис языка программирования.
Можно добавить горячую клавишу на открытие внешнего редактора, но она будет действовать только в том случае, если курсор мыши находится над полем Vexpression, расположенны прямо над окном ввода кода.
Если у вас есть линейный и скучный рост какого-то параметра от нуля до одного, анимацию можно улучшить с помощью разных методов интерполяции.
Вот вам шпаргалка.
Struct — это структура данных, которая позволяет использовать инкапсуляцию, пусть и в ограниченном виде.
Struct необходимо создавать во внешнем файле, а не в самом Врангле. Создайте внешний файл так, как это описано в этом вопросе: http://hipnc.club/ufaqs/custom-functions/
Struct может содержать как поля, так и методы (если пользоваться терминологией ООП).
// Упрощенный пример использования структуры для создания двумерного массива
struct IntArray
{
int _xSize;
int _array[];
void Create(int xSize, ySize)
{
_xSize=xSize;
resize(_array, xSize*ySize);
}
int Value(int x, y)
{
return _array[y*_xSize + x];
}
void Set(int x, y, value)
{
_array[y*_xSize + x] = value;
}
}
После этого во Врангле делаем #include с именем вашего файла. Обращаться к структуре можно следующим образом:
#include "arrays.h"
IntArray arr;
arr->Create(10,10);
arr->Set(3,5,1000);
printf("Array value: %d", arr->Value(3,5));
printf("Array X size: %d", arr._xSize);
К «полям» мы обращаемся через знак «точка», а к «методам» через «->».
Также у структур есть конструктор, проще говоря их можно инициализировать следующим образом:
MyStruct var = MyStruct(10, 20, "String");
То есть мы используем название структуры в качестве функции и перечисляем в аргументах значения для всех полей.
Обратите внимание, что в Вексе нельзя создать массив массивов, даже если вы закрыли свой массив в структуре. То есть если планируется сделать массив структур, то внутри структуры не может быть массивов. Как вариант обхода этого ограничения: создать в структуре функцию, которая возвращает массив, а сам массив уже хранить в теле этой функции, тогда ошибки не будет.
Создайте файл с расширением .h, например myvex.h. Этот файл необходимо поместить в папку, которая указана в переменной окружения HOUDINI_VEX_PATH или же в папку ДОКУМЕНТЫ/HoudiniXX.XX/vex/include.
После этого вы сможете обращаться к указанным в этом файле функциям, добавив в начало кода во Врангле строку
#include "myvex.h"
Если же вам нужно передать проект кому-то другому и у вас нет уверенности, что на другой машине будут такие же файлы, можно в include использовать относительный путь:
#include "$HIP/myvex.h"
Также можно встроить функцию прямо во врангл:
- Разлочить врангл (Allow Editing of contents на ноде Врангл)
- Заходим внутрь два раза, до ноды Snippet
- В ноде Snippet можно в параметр Include files записать прямой путь до вашего файла. К сожалению, путь должен быть абсолютным.
- Также в ноде Snippet можно скопировать полностью код из вашего h-файла в параметр Outer Code (или можно запромоутить его на верхний уровень ассета).
- После этого команду #include можно убрать из кода
Допустим, вы не знаете точно названия атрибутов на геометрии. Как получить список их имен, чтобы выбрать нужный?
Для этого существуют следующие интринсики на уровне детали: pointattributes, primitiveattributes, vertexattributes и detailattributes. Их можно прочитать в строковые массивы и уже дальше работать с ними, как с обычными строками. Например, можно сделать поиск по какому-то условию через re_find, найти имя нужного атрибута и записать в него что-то через функции setpointattrib, setprimattrib и так далее.
Чтение из интринсиков выглядит так:
string pointattribs[] = detailintrinsic(0,"pointattributes");
string primattribs[] = detailintrinsic(0,"primitiveattributes");
string vertexattribs[] = detailintrinsic(0,"vertexattributes");
string detailattribs[] = detailintrinsic(0,"detailattributes");
Поскольку функции point(), prim(), detail() и им подобные могут возвращать разные типы данных, то просто написать f@attr = point(1, «P», @ptnum).x; не получится, Врангл выдаст ошибку Ambiguous call to function…
Чтобы подобная записать сработала, необходимо сказать Вексу, что мы читаем именно вектор. Это можно сделать двумя путями: создать векторную переменную и читать значение позиции в нее или скастовать функцию в векторный тип.
// В этом примере мы сначала записываем позицию в векторную переменную и читаем значение компоненты из нее
vector pos = point(1, "P", @ptnum);
f@height = pos.y;
// В этом примере мы используем приведение типа функции
f@height = vector(point(1, "P", @ptnum));
Приведение типа функции или по-английски Cast выполняется записью вида ТИП_ДАННЫХ(выражение). Этот тип приведения не использует никакого реального преобразования, он просто говорит Вексу, какой из перегруженных вариантов функции использовать.
В ноде Carve есть режим Extract — Extract Point(s). В нем от кривой останется одна или две точки (при включенном параметре Second U).
Если нужно решение в вексе, то здесь понадобится функция Primuv.
// На первый вход врангла подается одна точка, на второй - кривая.
// Параметром anim можно перемещать точку по этой кривой.
// Ноль означает номер примитива. Если примитивов больше одного, нужно будет подставить вместо него правильные номера.
@P = primuv(1, "P", 0, ch("anim"));
Если речь про целое число, то существует функция itoa(number), которая из числа типа 125 возвращает строку «125». Эта функция часто используется для работы с атрибутом name, мультипармами или инстансами.
// в атрибут instance запишется строка box0, box1, box2 и так далее в зависимости от числа в атрибуте id.
s@instance = "/box"+ itoa(i@id);
// в атрибут text запишется строковое значение из строкового параметра мультипарма (text0, text1, text2 и так далее).
s@text = chs("text" + itoa(num));
Если же нам нужно перевести в строку флоат или как-то отформатировать результат, то необходимо использовать функцию sprintf(). В ней задается произвольная строка, в которой встречаются служебные последовательности символов типа %g, %d, а после строки через запятую перечисляются значения, которые подставятся вместо этих символов. Также эта функция поддерживает дополнительные опции форматирования, такие как выравнивание, padzero и т.д.
// В атрибут filename запишется значение типа image_0001.jpg, где 0001 - это номер кадра
// Запись вида %04d сделает длину числа равной 4 и забьет свободные места нулями
s@filename = sprintf("image_%04d.jpg",@Frame);
// В атрибут floatstring запишется значение атрибута pscale
s@floatstring = sprintf("%g", f@pscale);
Описание всех возможных вариантов форматирования можно почитать здесь.
Чтобы функция возвращала массив, нужно при ее объявлении использовать слово function, в противном случае выпадет ошибка.
function int[] foo()
{
int array[] = {1,2,3};
return array;
}
Для проверки вхождения точки-примитива-вертекса в группу есть функции inpointgroup(), inprimgroup(), invertexgroup().
Но удобнее использовать виртуальный атрибут @group_название
В этом атрибуте будет 0, если элемент не принадлежит группе, и 1, если принадлежит.
Таким образом проверку на вхождение в группу start можно осуществлять так:
if @group_start==1 ...
или
if (@group_start) ...
Очевидный способ — использовать функции point(), prim(), vertex(), detail(), где первым аргументом указать номер входа.
Есть более простой синтаксис, который выглядит так:
@opinput1_P, i@opinput2_index, v@opinput3_dir
Указание типа происходит по стандартным правилам. После знака @ ставим opinput номер входа и через нижнее подчеркивание название атрибута. Нумерация начинается от нуля.
При этом берется атрибут с элемента с таким же номером, который отрабатывается Вранглом в данный момент. Если Врангл запущен по точкам и отрабатывается точка номер 10, то и с другого входа атрибут будет прочитан с точки номер 10.
Но также у ноды Wrangle есть параметр Attribute to match. В случае его наличия, будет проверяться не номер элемента, а значение в этом атрибуте, например id. Значит, что атрибут будет читаться не с текущего номера элемента, а с элемента, у которого id совпадает.
В Вексе можно прочитать и записать атрибут на геометрии в синтаксисе тип@название. Если тип не указан, а атрибут не находится в списке «известных», то он получит тип Float.
Тип | Синтаксис |
---|---|
float | f@имя |
vector2 | u@имя |
vector | v@имя |
vector4 | p@имя |
int | i@имя |
matrix2 | 2@имя |
matrix3 | 3@имя |
matrix | 4@имя |
string | s@имя |
dict | d@имя |
Существуют атрибуты с известным типом, который автоматически распознается вексом. В их случае тип указывать необязательно.
Тип | Атрибуты |
---|---|
vector | P, accel, Cd, N, scale, force, rest, torque, up, uv, v center, dPdx, dPdy, dPdz в Volume wrangle |
vector4 | backtrack, orient, rot |
int | id, nextid, pstate переменные индексирования: elemnum, ptnum, primnum, vtxnum, numelem, numpt, numprim, numvtx group_* ix, iy, iz, resx, resy, resz в Volume wrangle |
string | name, instance |
Таким образом можно не указывать тип атрибута, если он типа float, а так же для известных атрибутов. Во всех остальных случаев при первом упоминании атрибута в коде этот тип указывать обязательно. После того, как в коде уже был указан тип атрибута, дальше к нему можно обращаться без типа.
Такое количество нюансов справедливо пугает новичков, поэтому вот вам главное правило: если сомневаешься — лучше указывать тип всегда, хуже от этого не станет, ошибку это не вызовет, зато сэкономит время на отладку.
Часть функций в Вексе читают информацию с входящей геометрии. Например атрибуты, группы, габариты и так далее. В хелпе у таких функций обычно присутствует аргумент Geometry.
В этот аргумент записывается или номер входа Врангла, с которого забирается значение (включая отрицательные числа для Spare inputs), или же путь до нужной ноды в формате «op:…».
В том случае, если функция меняет геометрию, то есть создает новые атрибуты, группы, удаляет или создает точки и так далее, то у нее есть аргумент Geohandle.
Это НЕ номер входа, это идентификатор геометрии, на которой происходят изменения. Этот аргумент был добавлен для возможного наращивания функционала в будущем, например для изменения геометрии где-то в другом месте нетворка. Но на данный момент он принимает на вход только текущую геометрию, то есть число ноль.
Дословный перевод хелпа
Geohandle — это идентификатор геометрии, на которой происходит запись. На данный момент единственное возможное значение — ноль или geoself(), то есть текущая геометрия в ноде. Этот аргумент может быть использован в будущем, чтобы дать возможность записи в другие геометрии.
По хорошему нужно писать в этот аргумент функцию geoself(), но это функция-пустышка, которая всегда возвращает ноль. Поэтому записывать в geohandle ноль или geoself() — исключительно вопрос философии разработки и читаемости кода. Главное — не путать этот аргумент с номером входа Врангла.
Короткий ответ — нет, но на практике это может зависеть от конкретных нод.
Векс — язык быстрый, но все равно высокоуровневый. Большинство стандартных нод скомпилированы и будут выполняться быстрее. Другое дело, что ноды меняются, обрастают новым функционалом и не всегда работают оптимально. Проблема в том, что это можно проверить только опытным путём, сравнивая время исполнения ноды и аналогичного кода во Врангле через Performance Monitor.
На мой взгляд лучше искать возможности для оптимизации в другом месте.
В Sublime выбрать пункт меню Preferences/Package Control. Набрать «Install package». В появившемся окне найти и установить VEX Syntax. Теперь синтаксис Векса появится в списке в меню View/Syntax.
Самое простое — включить выход нужной ноды в один из входов Врангла и подставить в функции номер этого входа. Если входов не хватает, вы всегда можете добавить сколько угодно Spare input через меню шестеренки и Add spare input, подключить в них нужные ноды и обращаться к ним по номеру со знаком минус (-1, -2 и т.д.).
Второй вариант: использовать синтаксис op:/путь/до/ноды
Например, если нужно прочитать число точек на ноде с названием sphere1, которая лежит в этом же нетворке, то синтаксис будет таким:
npoints("op:../sphere1");
То есть, относительный путь работает так же, как и в Hscript. Абсолютный путь будет таким:
npoints("op:/obj/geo1/sphere1");
В вексе для этого есть две функции:
atof("строка") - преобразует строку во флоат
atoi("строка") - преобразует строку в целое число
При этом в строке не должно быть других символов, кроме цифр, десятичной точки и знака «е» для записи типа 1.25e+5
Например atof(«12.5») вернет число 12.5
Для целых чисел есть функция opdigits(), которая возвращает последнюю последовательность целых чисел из строки. Обычно она используется, чтобы парсить пути до нод, но и для обычных строк работает.
opdigits("gfhfhg123jshfh") вернет 123
Если же в строке есть другие символы, кроме цифр, и нужно вытащить из нее флоат, то задача сводится к удалению лишних символов из строки. Это делается с помощью регулярных выражений (regular expression, regexp).
Функции для работы с регулярными выражениями в вексе начинаются с приставки «re_».
s@in = "gfhdg12.5jahd";
@out=atof(re_replace(r"[^.0-9]+", "", @in));
В @out окажется число 12.5
Во второй строке мы используем функцию re_replace, которая ищет в атрибуте @in подстроку по регулярному выражению [^.0-9]+, то есть все, кроме точки и цифр 0-9. Буква r перед кавычками важна.
Дальше мы заменяем найденное на пустую строку «». На выходе у нас остаются только цифры и точка, к которым мы уже применяем atof.
А вот если нужно распарсить строку, где может быть несколько чисел с буквами и символами между ними, то используем такую конструкцию:
s@in="123hjh765.4hfhsd12jsh";
string array[] = re_split(r"[^.0-9]+", @in);
Эта функция создаст массив строк, а в качестве разделителя использует все то же выражение. Единственное, если исходная строка начинается не с цифры, то в начале массива создастся пустой элемент, надо это учитывать. В данном примере на выходе будет массив из строк “123″, “765.4″, “12″. Дальше уже можно по этому массиву идти циклом и использовать atof().