Генерация 3д объекта - как правило, многоэтапный процесс (например в булевых операциях сначала поиск графа пересечений, нахождение геометрии кривых пересечения и построение топологии результирующего тела). Закономерно возникает сложность с его отладкой. Положим при генерации что-то пошло не так и имеем наполовину готовый объект, который не может быть визуализирован разрабатываемой CAD системой. Что делать? Как локализовать место и момент ошибки? Анализировать глазами тысячи xyz координат промежуточных результатов и вспомогательных объектов на момент выдачи исключения? Или хуже, если отклонения желаемого результата от фактического незначительные, тогда и все числа внешне будут корректны. Работая С++ программистом в области 3Д моделирования и построения различных CAD/САПР систем, я регулярно сталкивался с проблемой визуализации вспомогательных/промежуточных сущностей.
Сформировал себе универсальный инструментарий DumpSTL, позволяющий с минимальными усилиями, в любом C++ проекте дампить в .stl файлы любые внутренние объекты в проекте.
Почему именно .stl? Так уж исторически сложилось. Много использовал чпу фрезера и 3д принтера, где основным и простейшим форматом моделей является .stl.
Суть использования сводится к однократной адаптации инструмента под структуры данных конкретного проекта, затем:
1) подключить один DumpSTL.h;
2) вызвать к необходимым данным метод DUMP::save(...)
;
3) получить на выходе множество файлов с 3д моделями, которые можно открыть в любом 3д редакторе.
Примеры использования
Пример 1: По ходу генерации 3д модели музыкального инструмента - флейты Пана
используются аналитические функции для проецирования точек по определенным правилам, rоторые могут быть визуализированы поверхностями для лучшего понимания областей определения и итоговой геометрии.
Пример 2: Визуализация формы математических функций – сплайнов, по которым натягиваются некая внутренняя поверхность и формируется геометрия входного отверстия (это не просто круг и цилиндр, постарался показать синим и красным гипертрофированный изгиб).
Получаемые при этом графики функций сплайнов в плоскости XY:
Пример 3: Обозначение ориентации кривых в пространстве, что бы понять где начало, а где конец.
Адаптация под проект / геометрическое ядро
Покажу на примере библиотеки MeshLib.
1) Указать путь к папке, куда будут дампиться .stl файлы в глобальной переменной.std::string_view folderSTL = "C:\\Repos\\STL\\"
2) Подключить include в DumpSTL.h с необходимыми структурами, в данном примере это положим MR::Mesh
(полная сетка 3д тело), MR::FaceBitSet
(подмножество треугольников сетки).
3) Написать функции преобразования в DUMP::Model3D
для каждого такого пользовательского типа (аргумент может быть не один):
а) Model3D convert(const MR::Mesh&, const MR::FaceBitSet& );
б) Model3D convert(const MR::Mesh& );
4) По необходимости написать преобразования пользовательских типов в множество точек const std::vector<Point3f>& points
, которые могут быть использованы для дампа с особыми модификациями:
a) Model3D direction(const std::vector<Point3f>& points)
– порождает по упорядоченному множеству точек ориентированную цепочку конусов (пирамидок);
б) Model3D line(const std::vector<Point3f>& points)
– порождает по упорядоченному множеству точек отрезки;
в) Model3D sphere(const std::vector<Point3f>& points)
– порождает по множеству точек низкополигональные сферы.
Пример реализации convert методов
DUMP::Point3f convert( const MR::Vector3f& point )
{
return { point.x, point.y, point.z };
}
DUMP::Model3D convert( const MR::Mesh& mesh, const MR::FaceBitSet& faces)
{
DUMP::Model3D res;
for ( auto f : faces )
{
MR::Vector3f a, b, c;
if ( mesh.topology.hasFace( f ) )
{
mesh.getTriPoints( f, a, b, c );
res.addTriangle( convert(a), convert(b), convert(c));
}
}
return res;
}
DUMP::Model3D convert( const MR::Mesh& mesh )
{
return convert( mesh, mesh.topology.getValidFaces() );
}
Как пользоваться
Применять методы ниже к стуктурам данным проекта и получать генерируемые 3д модели:
1) void save(std::string_view fileName, Args... args)
– сохраняем один файл, где fileName
– только название файла (нужная папка в folderSTL
), args
– передаем напрямую нужные нам объектыsave(“mesh”, my_mesh)
, save(“mesh_part”, my_mesh, my_faceBitSet)
2) void saveInc(std::string_view fileName, Args... args)
– то же самое что и save
, с той лишь разницей, что сохраняет всегда в новый файл. Если файл с указанным именем существует, приписывает цифру-счетчик еще не существующего файла. Так например если процесс итерационный, и на 135 итерации все взрывается, то есть возможность увидеть эволюцию 3д объекта во всех 135 итерациях, и увидеть, что было непосредственно перед падением приложения.
3) Использовать модификаторы direction
/line
/sphere
: save(“mesh”, direction(mesh))
, save(“mesh_part”, direction(my_mesh, my_faceBitSet))
4) Создовать напрямую объекты Model3D
, используя напрямую его API addTriangle
/addPoint
/addEdge
/addCone
/addQuad
/addSphere
и дампить его так же save(“model3D”, my_Model3D)
Hello world
Пример использования и соответственно запускаемый "hello world" можно найти в соседнем проекте в репозитории example. При запуске из коробки должны получаться следующие примитивы:
И вот такой набор файлов с 3д моделями: dumpStlExample_cube.stl, dumpStlExample_directionChain_0.stl, dumpStlExample_directionChain_1.stl, dumpStlExample_directionChain_2.stl, dumpStlExample_lineChain.stl, dumpStlExample_points.stl, dumpStlExample_spheres.stl, dumpStlExample_tetraedr.stl
Как устроена библиотека
1) Представляет из себя один подключаемый header only DumpSTL.h репозиторий
а) В нем описаны тривиальные структуры Point3
, Triangle
, Model3D
с сопутствующими методами
б) Функции для пользователя save
, saveInc
(incremental)
в) Модификаторы: direction
, sphere
, line
2) Проект под студию с примером использования и известным ожидаемым результатом example.sln
3) Скрипт clear_stl.bat для автоматической чистки папки от накопившихся .stl файлов (их могут быть сотни)
4) Скрипт run_blender.bat + blenderScrypt.py для автоматического открытия в 3д редакторе blender всех файлов в папке по маске *.stl и фотографирования каждого с 4ех ракурсов и сохранением фото в эту же папку с припиской _left.jpg, _right.jpg, _top.jpg, _bottom.jpg. Может быть полезно при полуавтоматическом прогоне тестов и относительно быстрой проверки глазами, что ничего не попортилось путем быстрого пролистывания скриншотов.