Личный блог и информация по репозиториям
26 августа 2025г.
Данная статья открывает цикл публикаций об обменных форматах передачи информационных моделей. Первым на очереди – формат представления информационной модели, разработанный НПФ “Топоматик” (компания-разработчик линейки ПО “Топоматик Робур”).
Формат SMDX расшифровывается как “Summary MoDel eXtensible”, информация о чём приведена на справочной страничке http://smdx.info/. Появился где-то в 2022 году.
Формат представляет собой ZIP-архив (свободно распаковываемый и формируемый вручную\программно) с индексным JSON-файлом и вспомогательными ресурсами, расположенными в одной из пяти вложенных папок.
Геометрия хранится в каркасном представлении (mesh), в json-файлах своей разметки – J3D. Информация о материалах представлена в трех видах – текстуры, шейдеры и однородный цвет (модель Blinn–Phong, как на русском не знаю).
Также в составе данных SMDX могут храниться связанные документы любых типов.
Важной черной формата является стремление к оптимизации хранения данных – используются некоторые подходы (о них ниже), снижающие количество хранимых в файле численных данных геометрии. Вместе с тем присутствие в числе данных текстур несколько увеличивает вес данных, а особенности упаковки данных приводят к необходимости разработки специальных инструментов чтения-записи (при программной работе с SMDX).
В формате устроено раздельное хранение геометрии и информации о точках её вставки: например, если на базе фиксированного типоразмера параметрического объекта (aka семейства) или статичной геометрии было создано несколько объектов, то в память сохранится только геометрия одного (как правило, внутренняя геометрия объекта), а все вхождения будут представлять собой матрицу трансформации относительно первого – представленной смещением, поворотами, масштабированием.
Структура данных информационной модели, атрибуты, информация о точках вставки геометрии, самой геометрии, сведения о текстурах и наборе связанных документов хранятся в корневом JSON-файле content.json.
Блок geometry представлен списком из отдельных “геометрических примитивов”, каждый из которых представляет собой каркасную модель (tin-mesh) из одного или нескольких тел.
Отдельная запись в блоке geometry по спецификации обязательно содержит информацию о имени файла геометрии (j3d-файл в подпапке \geometry). При этом может встречаться ситуация, что j3d-файл всё-таки не включен в SMDX.
Дополнительной информацией могут являться:
границы геометрического примитива в формате массива координат (Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
информация об уровнях детализации – при каком расстоянии до объекта при зуммировании следует отрисовывать другой файл геометрии j3d)
Типовой j3d-файл (json-разметка) представляет собой ассоциативную коллекцию вида “имя mesh’a” : информация о геометрии mesh’a. Имена произвольные, главное, чтобы в пределах j3d-файла они были уникальными.
В минимальном варианте информация о mesh’е должна включать:
positions: список координат вершин;
triangles: упорядоченный список индексов вершин, тройки которых образуют грань;
Далее, в зависимости от типа материала:
groups: сопоставление упорядоченного набора индексов граней в triangles конкретным материалам в виде ассоциативной коллекции, где ключом выступает имя материала – файла jmtl в подпапке \materials, а значением – массив из двух чисел – индекс первой грани и индекс последней грани из trianglesимеющих один цвет.ИЛИ
textures: информация, какая текстура у каждой из граней в triangles, если используются текстуры, а не материалы.Дополнительными массивами могут быть smoothing, triangles_flagsсвязанные с индексами из triangles.
Несмотря на то, что информация о материалах не указывается напрямую в индексном файле, стоит оговорить ОСОБЕННОСТЬ 😡 оных описаний материалов. Как выяснилось при программной разработке, jmtl надо создавать не программной серриализацией класса в json, а редактированием их файла шаблона. (может я кривой и у меня не вышло, но делюсь своей болью, пока ещё это смог выявить …)
Материалов в SMDX может быть 2 типа – либо это упомянутый выше Blinn–Phong (тип указан в поле type), запись о котором выглядит примерно так:
{
"type": "Blinn–Phong",
"ambient": [ 0.05333332, 0.09882354, 0.1819608 ],
"diffuse": [ 0, 1, 0 ],
"specular": [ 0.7529412, 0.7529412, 0.7529412 ],
"level": 0.4,
"shininess": 0.2,
"blur": 0,
"transparency": 0,
"illumination": 0,
"shading": "Wire",
"flags": 1,
"wire": 1
}
Либо это материал с типом Effect. Как пространно сказано в документации, для него состав полей определяется используемым шейдером и может быть любым. Я к слову его игнорировал при программной обработке.
Текстуры записываются в папку \textures.
Сведения о том, где размещена геометрия, хранятся в блоке insertions. Типовая запись обязательно имеет поля:
geometry: индекс элемента из блока geometry (см. п. 2.1 выше);
group: индекс из блока groups - информация об объектах – в каком по индексу объекта списка лежит данное вхождение геометрии. Например, объект сцены, представляющий лес, описывается из множества деревьев и грунта;
Также обязательны поля с геометрией. В основном, встречается классическая схема (некоторые данные могут приниматься по умолчанию, если они не указаны):
position: 3 координаты, в точке которой (плюс координат wcs из корня content.json) размещено вхождение геометрии;
scale: 1 число или массив из трех чисел, задающее масштабирование элемента на одинаковое число по всем осям (если задано одним числом) или масштабирование на специальное значение для каждой из оси;
angle: угол поворота в плоскости XOY в радианах;
normal: вектор нормали. Фактически, описывает углы поворота осей в пространстве в дополнение к angle;
Также мне встречалось ещё одно экзотическое описание геометрии – через ассоциативную коллекцию, представляющую position – там что-то с интерполяцией, временем и пр. Я даже не стал разбираться, просто это проигнорировал.
В блоке groups хранится структура информационной модели. Корневые объекты не имеют свойства parent. У всех остальных при наличии родителя свойство parent имеет значение от 0 до числа объектов в groups -1.
Свойство name – это наименование объекта. Свойства заданы в виде списка в properties. Каждое отдельное свойство имеет поле tag – идентификатор свойства. Если у свойства есть поле type, то расшифроку свойства см. в блоке types файла content.json (кстати, они же лежат в файле smdx.json в корневом каталоге папки установки Робура).
Если определения свойства нет в группе types то краткая информация о нём может быть приведена в блоке info у отдельного свойства.
Как явно следует из пункта 2.3, один объект может быть представлен множеством различных вхождений геометрии, а может и вовсе не иметь какого-либо отображения – то есть быть только элементом структуры.
Подводя краткий итог, файл SMDX имеет 2 несомненных плюса:
хранение примитивной геометрии и информации о вхождениях геометрии, что существенно снижает общий вес модели при наличии множества вхождений объектов одного типа;
читабельный формат – использование JSON (с возможностью серриализации\десерриализации программно);
Вместе с тем отмечу некоторые спорные стороны формата и реализации записи/чтения данных в Робуре:
информация о материале задается не просто, для интероперабельности с другими средами и форматами в Робуре должна быть опция выгрузки материалов типа Blinn-Phong;
список граней и связанный с ними список точек в файле геометрии j3d должен быть отсортирован так, чтобы одноцветные грани лежали рядом – это требует дополнительных существенных вычислительных затрат. В противном случае заложенная в формат оптимизация хранения индексов себя не оправдывает;
я столкнулся с невозможностью программной серриализации в JSON класса, ответственного за примитивный материал по Blinn-Phong - получилось только редактировать определения из робуровского файла jmtl;
оторванность объектов (groups) от вхождений (insertions), отсутствие взаимных индексов, что при отладке очень тормозило разработку;
модуль чтения SMDX в Робуре имеет не информативное логгирование ошибок. Ошибки обработки совершенно не указывают на проблему в конкретном месте. Конкретное место пишется только при чтении content.json. Также мне вываливалась ошибка о недостаточной памяти, хотя по Диспетчеру задач он занял едва чуть больше 1 Гб ОЗУ;
не хватает информации о всей спецификации. Имеющееся описание не затрагивает, например, вариант представления вхождения геометрии через какую-то интерполяцию;
ВАЖНО! формат допускает дубли объектов – один и тот же набор вхождений геометрии может быть у разных объектов в структуре модели из-за того, что в SMDX файле может быть несколько корневых элементов со своей дублирующей структурой. Это потенциально может приводить к ошибкам при проверке на коллизии;
ВАЖНО! Объекты в SMDX не имеют внутренних идентификаторов по умолчанию (только писать свои свойства)! Это критическая ошибка приведет к повторной перезаписи объектов SMDX при их передаче в другие СОД;
В целом, у меня получилось реализовать корректную запись SMDX, созданного с нуля и неоднозначное чтение – какие-то моменты от меня ускользают (буду пробовать дальше)