Add -DSLIC3R_CAD compile-time gate for the Design/CAD tab

Gate the entire parametric Design/CAD subsystem behind a single CMake
option so the fork can be built with or without it. With SLIC3R_CAD OFF
the build is behaviour-neutral against upstream OrcaSlicer; this is the
"parallel build" for upstream integration discussion.

Gated surface:
- option(SLIC3R_CAD) + add_definitions(-DSLIC3R_CAD)
- deps/OCCT/OCCT.cmake: BUILD_MODULE_ModelingAlgorithms=${SLIC3R_CAD}
  (OFF matches upstream OCCT exactly; ON adds only TKFillet + TKOffset)
- TKFillet/TKOffset link, add_subdirectory(slvs) + libslvs
- CAD kernel + GUI sources, GLGizmoPrimitive (needs GeometryEngine),
  CAD Catch2 tests
- 11 upstream C++ hook sites (GLCanvas3D, MainFrame, GLGizmosManager)
- TabPosition and gizmo EType enums switched to implicit numbering so
  OFF reproduces upstream indices exactly

Verified on behemoth both ways: OFF and ON link snapmaker-orca +
libslic3r_tests (exit 0); ON shows the Design tab and passes 8 [design]
+ 1 [Deviation] tests, OFF omits them and shows upstream's tab layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q
This commit is contained in:
Tommaso Bianchi
2026-07-10 18:12:53 +02:00
co-authored by Claude Opus 4.8
parent 546cef5f42
commit c618965a4c
11 changed files with 131 additions and 44 deletions
+5
View File
@@ -119,6 +119,7 @@ endif()
option(SLIC3R_STATIC "Compile OrcaSlicer with static libraries (Boost, TBB)" ${SLIC3R_STATIC_INITIAL})
option(SLIC3R_GUI "Compile OrcaSlicer with GUI components (OpenGL, wxWidgets)" 1)
option(SLIC3R_CAD "Compile OrcaSlicer with the parametric Design/CAD tab (needs OCCT ModelingAlgorithms)" 1)
option(SLIC3R_FHS "Assume OrcaSlicer is to be installed in a FHS directory structure" 0)
option(SLIC3R_PROFILE "Compile OrcaSlicer with an invasive Shiny profiler" 0)
option(SLIC3R_PCH "Use precompiled headers" 1)
@@ -190,6 +191,10 @@ if (SLIC3R_GUI)
add_definitions(-DSLIC3R_GUI)
endif ()
if (SLIC3R_CAD)
add_definitions(-DSLIC3R_CAD)
endif ()
if(SLIC3R_DESKTOP_INTEGRATION)
add_definitions(-DSLIC3R_DESKTOP_INTEGRATION)
endif ()
+7 -1
View File
@@ -4,6 +4,12 @@ else()
set(library_build_type "Static")
endif()
# The parametric Design/CAD tab is the only consumer of OCCT's ModelingAlgorithms
# module (fillet/offset/loft). With it OFF the deps prefix matches upstream exactly;
# with it ON the delta is TKFillet + TKOffset (3.77 MiB, Windows only -- OCCT links
# statically on macOS/Linux). Must match the SLIC3R_CAD passed to the main project.
option(SLIC3R_CAD "Build OCCT's ModelingAlgorithms module (required by the Design/CAD tab)" ON)
if (IN_GIT_REPO)
set(OCCT_DIRECTORY_FLAG --directory ${BINARY_DIR_REL}/dep_OCCT-prefix/src/dep_OCCT)
endif ()
@@ -28,7 +34,7 @@ orcaslicer_add_cmake_project(OCCT
#-DBUILD_MODULE_DataExchange=OFF
-DBUILD_MODULE_Draw=OFF
-DBUILD_MODULE_FoundationClasses=OFF
-DBUILD_MODULE_ModelingAlgorithms=ON
-DBUILD_MODULE_ModelingAlgorithms=${SLIC3R_CAD}
-DBUILD_MODULE_ModelingData=OFF
-DBUILD_MODULE_Visualization=OFF
)
+35 -20
View File
@@ -21,7 +21,9 @@ endif()
option(BUILD_SHARED_LIBS "Build shared libs" OFF)
# Vendored SolveSpace constraint solver (2D sketch solver backbone).
add_subdirectory(slvs)
if (SLIC3R_CAD)
add_subdirectory(slvs)
endif ()
set(lisbslic3r_sources
AABBMesh.cpp
@@ -278,8 +280,6 @@ set(lisbslic3r_sources
Geometry/VoronoiUtils.cpp
Geometry/VoronoiUtils.hpp
Geometry/VoronoiVisualUtils.hpp
GeometryEngine.cpp
GeometryEngine.hpp
Int128.hpp
KDTreeIndirect.hpp
Layer.cpp
@@ -390,20 +390,6 @@ set(lisbslic3r_sources
SLA/JobController.hpp
SLA/Pad.cpp
SLA/Pad.hpp
SketchEngine.cpp
SketchEngine.hpp
SketchConstraints.cpp
SketchConstraints.hpp
SketchSolver.cpp
SketchSolver.hpp
SketchInference.cpp
SketchInference.hpp
SketchImport.cpp
SketchImport.hpp
CadDocument.cpp
CadDocument.hpp
ThreadStandards.cpp
ThreadStandards.hpp
SLAPrint.cpp
SLAPrint.hpp
SLAPrintSteps.cpp
@@ -495,6 +481,29 @@ set(lisbslic3r_sources
FlushVolPredictor.cpp
)
# Parametric Design/CAD kernel. Needs OCCT's ModelingAlgorithms module and the
# vendored SolveSpace solver; both are pulled in only when SLIC3R_CAD is ON.
if (SLIC3R_CAD)
list(APPEND lisbslic3r_sources
GeometryEngine.cpp
GeometryEngine.hpp
SketchEngine.cpp
SketchEngine.hpp
SketchConstraints.cpp
SketchConstraints.hpp
SketchSolver.cpp
SketchSolver.hpp
SketchInference.cpp
SketchInference.hpp
SketchImport.cpp
SketchImport.hpp
CadDocument.cpp
CadDocument.hpp
ThreadStandards.cpp
ThreadStandards.hpp
)
endif ()
if (APPLE)
list(APPEND lisbslic3r_sources
MacUtils.mm
@@ -565,8 +574,6 @@ find_package(JPEG REQUIRED)
find_package(draco REQUIRED)
set(OCCT_LIBS
TKFillet
TKOffset
TKBool
TKXDESTEP
TKSTEP
@@ -595,6 +602,11 @@ set(OCCT_LIBS
TKMath
TKernel
)
# The CAD kernel is the only consumer of OCCT's ModelingAlgorithms module. Upstream's
# DataExchange already pulls TKBool in transitively, so the true delta is these two.
if (SLIC3R_CAD)
list(APPEND OCCT_LIBS TKFillet TKOffset)
endif ()
# Publish the link list so the Windows packaging step can assert it ships a DLL for
# every toolkit we link, instead of shipping whatever the deps prefix happens to hold.
set(OCCT_LIBS "${OCCT_LIBS}" CACHE INTERNAL "OCCT toolkits linked by libslic3r")
@@ -616,7 +628,6 @@ target_link_libraries(libslic3r
clipper
Clipper2
draco::draco
libslvs
glu-libtess
JPEG::JPEG
libslic3r_cgal
@@ -650,6 +661,10 @@ if (TARGET OpenVDB::openvdb)
target_link_libraries(libslic3r PRIVATE OpenVDB::openvdb)
endif()
if (SLIC3R_CAD)
target_link_libraries(libslic3r PUBLIC libslvs)
endif ()
if(WIN32)
target_link_libraries(libslic3r PRIVATE Psapi.lib bcrypt.lib)
endif()
+21 -13
View File
@@ -107,15 +107,6 @@ set(SLIC3R_GUI_SOURCES
GUI/Downloader.hpp
GUI/DownloadProgressDialog.cpp
GUI/DownloadProgressDialog.hpp
GUI/DesignPanel.cpp
GUI/DesignPanel.hpp
GUI/McpControl.cpp
GUI/McpControl.hpp
GUI/DesignCanvas.cpp
GUI/DesignCanvas.hpp
GUI/DesignSketchTool.cpp
GUI/SketchInlineEditor.cpp
GUI/SketchInlineEditor.hpp
GUI/DragCanvas.cpp
GUI/DragCanvas.hpp
GUI/EditGCodeDialog.cpp
@@ -165,8 +156,6 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoMove.hpp
GUI/Gizmos/GLGizmoPainterBase.cpp
GUI/Gizmos/GLGizmoPainterBase.hpp
GUI/Gizmos/GLGizmoPrimitive.cpp
GUI/Gizmos/GLGizmoPrimitive.hpp
GUI/Gizmos/GLGizmoRotate.cpp
GUI/Gizmos/GLGizmoRotate.hpp
GUI/Gizmos/GLGizmoScale.cpp
@@ -177,8 +166,6 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoSeam.hpp
GUI/Gizmos/GLGizmoSimplify.cpp
GUI/Gizmos/GLGizmoSimplify.hpp
GUI/Gizmos/GLGizmoSketch.cpp
GUI/Gizmos/GLGizmoSketch.hpp
#GUI/Gizmos/GLGizmoSlaSupports.cpp
#GUI/Gizmos/GLGizmoSlaSupports.hpp
GUI/Gizmos/GLGizmosManager.cpp
@@ -690,6 +677,27 @@ set(SLIC3R_GUI_SOURCES
Utils/FileTransferUtils.hpp
)
# Design/CAD tab: parametric sketch UI, its gizmo, and the MCP control socket.
# All of it sits behind SLIC3R_CAD and links the CAD kernel in libslic3r.
if (SLIC3R_CAD)
list(APPEND SLIC3R_GUI_SOURCES
GUI/DesignPanel.cpp
GUI/DesignPanel.hpp
GUI/DesignCanvas.cpp
GUI/DesignCanvas.hpp
GUI/DesignSketchTool.cpp
GUI/SketchInlineEditor.cpp
GUI/SketchInlineEditor.hpp
GUI/McpControl.cpp
GUI/McpControl.hpp
GUI/Gizmos/GLGizmoSketch.cpp
GUI/Gizmos/GLGizmoSketch.hpp
# Needs GeometryEngine (make_primitive / apply_fillet / tessellate).
GUI/Gizmos/GLGizmoPrimitive.cpp
GUI/Gizmos/GLGizmoPrimitive.hpp
)
endif ()
add_subdirectory(GUI/DeviceCore)
add_subdirectory(GUI/DeviceTab)
+16
View File
@@ -1,6 +1,8 @@
#include "libslic3r/libslic3r.h"
#include "GLCanvas3D.hpp"
#ifdef SLIC3R_CAD
#include "DesignSketchTool.hpp" // SnapOrca Design: interactive 2D sketch tool
#endif
#include <igl/unproject.h>
@@ -2136,8 +2138,10 @@ void GLCanvas3D::render(bool only_init)
// SnapOrca Design: interactive 2D sketch overlay, drawn over the scene but
// beneath the UI overlays (toolbars, labels).
#ifdef SLIC3R_CAD
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display())
m_design_sketch_tool->render(*this);
#endif
// draw overlays
_render_overlays();
@@ -3297,6 +3301,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
// SnapOrca Design: Delete/Backspace removes the selected sketch entities while a
// sketch tool is active and the canvas has focus (dialog text fields are separate
// wx controls, so this never eats their editing keys).
#ifdef SLIC3R_CAD
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->is_active()
&& (keyCode == WXK_DELETE || keyCode == WXK_BACK)
&& !m_design_sketch_tool->selection().empty()) {
@@ -3305,9 +3310,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
render();
return;
}
#endif
// Esc exits the active sketch tool (Onshape-like, layered: abort in-progress entity ->
// drop to Select -> exit the session back to Feature mode).
#ifdef SLIC3R_CAD
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->is_active()
&& keyCode == WXK_ESCAPE) {
m_design_sketch_tool->request_exit();
@@ -3315,11 +3322,13 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
render();
return;
}
#endif
// SnapOrca Design: Ctrl+Z / Ctrl+Shift+Z (and Ctrl+Y) undo/redo the Design feature
// history. Scoped by m_design_sketch_tool — only the Design canvas owns one — so the
// main 3D editor's undo/redo (the CanvasView3D-gated cases further below) is untouched.
// Handled here, before the generic Ctrl block, so it takes precedence and early-returns.
#ifdef SLIC3R_CAD
if (m_design_sketch_tool != nullptr && (evt.GetModifiers() & ctrlMask) != 0) {
const bool is_z = (keyCode == 'z' || keyCode == 'Z' || keyCode == WXK_CONTROL_Z);
const bool is_y = (keyCode == 'y' || keyCode == 'Y' || keyCode == WXK_CONTROL_Y);
@@ -3331,10 +3340,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
return;
}
}
#endif
// SnapOrca Design: F = Place on Face (Prepare's lay-flat), when the Design viewport is up
// and a body face is selected. The tool forwards to DesignPanel::place_on_face; it returns
// false (no face picked) so F falls through to the default handler below.
#ifdef SLIC3R_CAD
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display()
&& (keyCode == 'f' || keyCode == 'F') && (evt.GetModifiers() & ctrlMask) == 0) {
if (m_design_sketch_tool->request_place_on_face()) {
@@ -3343,6 +3354,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
return;
}
}
#endif
bool is_in_painting_mode = false;
GLGizmoPainterBase *current_gizmo_painter = dynamic_cast<GLGizmoPainterBase *>(get_gizmos_manager().get_current());
@@ -3718,6 +3730,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
{
// SnapOrca Design: Delete/Backspace removes selected sketch entities. GTK delivers
// these as KEY_DOWN rather than CHAR, so handle it here too.
#ifdef SLIC3R_CAD
if (evt.GetEventType() == wxEVT_KEY_DOWN
&& m_design_sketch_tool != nullptr && m_design_sketch_tool->is_active()
&& (evt.GetKeyCode() == WXK_DELETE || evt.GetKeyCode() == WXK_BACK)
@@ -3727,6 +3740,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
render();
return;
}
#endif
static GLCanvas3D const * thiz = nullptr;
static TranslationProcessor translationProcessor(nullptr, nullptr);
@@ -4263,6 +4277,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// can click to select. It runs after ImGui (so dialogs still work) but before
// camera/toolbar/gizmo handling; on_mouse returns false for events it doesn't consume
// (drag/orbit/wheel) so the camera keeps working over the display-only plate.
#ifdef SLIC3R_CAD
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display()) {
if (evt.LeftDown() && m_canvas != nullptr)
m_canvas->SetFocus(); // grab keyboard focus so Delete/keys reach this canvas
@@ -4272,6 +4287,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
return;
}
}
#endif
#ifdef __WXMSW__
bool on_enter_workaround = false;
+6
View File
@@ -57,7 +57,9 @@ namespace GUI {
class Bed3D;
class PartPlateList;
#ifdef SLIC3R_CAD
class DesignSketchTool; // SnapOrca Design: interactive 2D sketch tool
#endif
#if ENABLE_RETINA_GL
class RetinaHelper;
@@ -548,7 +550,9 @@ private:
// SnapOrca Design: render the world-axis triad at the bed centre (= modeling origin) instead of
// the bed corner. Default false preserves the main editor's corner triad.
bool m_axes_at_bed_center{false};
#ifdef SLIC3R_CAD
DesignSketchTool* m_design_sketch_tool{nullptr};
#endif
//BBS: add canvas type for assemble view usage
ECanvasType m_canvas_type;
@@ -889,8 +893,10 @@ public:
void enable_collapse_toolbar(bool enable);
void enable_plate_chrome(bool enable);
void set_axes_at_bed_center(bool b) { m_axes_at_bed_center = b; }
#ifdef SLIC3R_CAD
void set_design_sketch_tool(DesignSketchTool* tool) { m_design_sketch_tool = tool; }
DesignSketchTool* get_design_sketch_tool() const { return m_design_sketch_tool; }
#endif
void enable_dynamic_background(bool enable) { m_dynamic_background_enabled = enable; }
void enable_labels(bool enable) { m_labels.enable(enable); }
void enable_slope(bool enable) { m_slope.enable(enable); }
@@ -27,8 +27,10 @@
#include "slic3r/GUI/Gizmos/GLGizmoSVG.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoAssembly.hpp"
#ifdef SLIC3R_CAD
#include "slic3r/GUI/Gizmos/GLGizmoPrimitive.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSketch.hpp"
#endif
#include "libslic3r/format.hpp"
#include "libslic3r/Model.hpp"
@@ -178,12 +180,14 @@ void GLGizmosManager::switch_gizmos_icon_filename()
case (EType::BrimEars):
gizmo->set_icon_filename(m_is_dark ? "toolbar_brimears_dark.svg" : "toolbar_brimears.svg");
break;
#ifdef SLIC3R_CAD
case (EType::Primitive):
gizmo->set_icon_filename(m_is_dark ? "toolbar_modifier_cube_dark.svg" : "toolbar_modifier_cube.svg");
break;
case (EType::Sketch):
gizmo->set_icon_filename(m_is_dark ? "toolbar_sketch_dark.svg" : "toolbar_sketch.svg");
break;
#endif
}
}
@@ -227,8 +231,12 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoAssembly(m_parent, m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg", EType::Assembly));
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify));
m_gizmos.emplace_back(new GLGizmoBrimEars(m_parent, m_is_dark ? "toolbar_brimears_dark.svg" : "toolbar_brimears.svg", EType::BrimEars));
#ifdef SLIC3R_CAD
// Registered last: Primitive and Sketch are the final entries before Undefined, so
// omitting them leaves every preceding m_gizmos index (indexed by EType) untouched.
m_gizmos.emplace_back(new GLGizmoPrimitive(m_parent, m_is_dark ? "toolbar_modifier_cube_dark.svg" : "toolbar_modifier_cube.svg", static_cast<unsigned int>(Primitive)));
m_gizmos.emplace_back(new GLGizmoSketch(m_parent, m_is_dark ? "toolbar_sketch_dark.svg" : "toolbar_sketch.svg", static_cast<unsigned int>(Sketch)));
#endif
//m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++));
//m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++));
//m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", sprite_id++));
@@ -90,8 +90,12 @@ public:
Assembly,
Simplify,
BrimEars,
#ifdef SLIC3R_CAD
// Both need the CAD kernel (GeometryEngine); keep them last so that with
// SLIC3R_CAD off the enum matches upstream's numbering exactly.
Primitive,
Sketch,
#endif
//SlaSupports,
// BBS
//FaceRecognition,
+8
View File
@@ -37,8 +37,10 @@
#include "I18N.hpp"
#include "GLCanvas3D.hpp"
#include "Plater.hpp"
#ifdef SLIC3R_CAD
#include "DesignPanel.hpp"
#include "McpControl.hpp"
#endif
#include "WebViewDialog.hpp"
#include "../Utils/Process.hpp"
#include "format.hpp"
@@ -1016,8 +1018,10 @@ void MainFrame::update_layout()
{
case ESettingsLayout::Old:
{
#ifdef SLIC3R_CAD
m_design_panel->Reparent(m_tabpanel);
m_tabpanel->InsertPage(tpDesign, m_design_panel, _L("Design"), std::string("tab_design_active"), std::string("tab_design_active"), false);
#endif
m_plater->Reparent(m_tabpanel);
m_tabpanel->InsertPage(tp3DEditor, m_plater, _L("Prepare"), std::string("tab_3d_active"), std::string("tab_3d_active"), false);
m_tabpanel->InsertPage(tpPreview, m_plater, _L("Preview"), std::string("tab_preview_active"), std::string("tab_preview_active"), false);
@@ -1274,12 +1278,14 @@ void MainFrame::init_tabpanel() {
}
//else if (panel == m_param_panel)
// m_param_panel->OnActivate();
#ifdef SLIC3R_CAD
else if (panel == m_design_panel) {
// Re-sync the Design bed to the active printer: the panel is built before the
// printer profile is fully applied, so its bed must refresh on activation or the
// grid (true bed) spills past the stale default bed quad.
m_design_panel->on_tab_shown();
}
#endif
else if (panel == m_monitor) {
//monitor
}
@@ -1329,8 +1335,10 @@ void MainFrame::init_tabpanel() {
// Register the plater with the app BEFORE constructing DesignPanel: its
// DesignCanvas reads wxGetApp().plater()->config() at construction time.
wxGetApp().plater_ = m_plater;
#ifdef SLIC3R_CAD
m_design_panel = new DesignPanel(this);
start_mcp_control_if_enabled(); // opens the MCP socket iff SNAPORCA_MCP is set
#endif
m_plater->SetBackgroundColour(*wxWHITE);
m_plater->Hide();
+17 -9
View File
@@ -50,7 +50,9 @@ namespace GUI
class Tab;
class PrintHostQueueDialog;
class Plater;
#ifdef SLIC3R_CAD
class DesignPanel;
#endif
class MainFrame;
class ParamsDialog;
#ifdef __WXGTK__
@@ -216,18 +218,22 @@ public:
bool get_mac_full_screen() { return m_mac_fullscreen; }
#endif
//BBS GUI refactor
// Implicitly numbered: with SLIC3R_CAD off, tpDesign vanishes and every later tab
// falls back to the index it has upstream. Do not reintroduce explicit values.
enum TabPosition
{
tpHome = 0,
tpDesign = 1,
tp3DEditor = 2,
tpPreview = 3,
tpMonitor = 4,
tpMultiDevice = 5,
tpProject = 6,
tpCalibration = 7,
tpAuxiliary = 8,
toDebugTool = 9,
#ifdef SLIC3R_CAD
tpDesign,
#endif
tp3DEditor,
tpPreview,
tpMonitor,
tpMultiDevice,
tpProject,
tpCalibration,
tpAuxiliary,
toDebugTool,
};
//BBS: add slice&&print status update logic
@@ -377,7 +383,9 @@ public:
BBLTopbar* m_topbar{ nullptr };
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
Plater* m_plater { nullptr };
#ifdef SLIC3R_CAD
DesignPanel* m_design_panel { nullptr };
#endif
//BBS: GUI refactor
MonitorPanel* m_monitor{ nullptr };
+4 -1
View File
@@ -3,7 +3,6 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests
${_TEST_NAME}_tests.cpp
test_3mf.cpp
test_caddocument.cpp
test_aabbindirect.cpp
test_appconfig.cpp
test_arachne_walls.cpp
@@ -31,6 +30,10 @@ add_executable(${_TEST_NAME}_tests
../libnest2d/printer_parts.cpp
)
if (SLIC3R_CAD)
target_sources(${_TEST_NAME}_tests PRIVATE test_caddocument.cpp)
endif ()
if (TARGET OpenVDB::openvdb)
target_sources(${_TEST_NAME}_tests PRIVATE test_hollowing.cpp)
endif()