From 51c1c0cdbdfef06a64a09133b59d6bb574d07689 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 22 Apr 2026 14:06:11 +0100 Subject: [PATCH 1/7] Created vsgcustombuilders example to test out providing custom scene graph builders to vsgXchange loaders. --- examples/io/CMakeLists.txt | 4 + examples/io/vsgcustombuilders/CMakeLists.txt | 10 + .../vsgcustombuilders/vsgcustombuilders.cpp | 446 ++++++++++++++++++ 3 files changed, 460 insertions(+) create mode 100644 examples/io/vsgcustombuilders/CMakeLists.txt create mode 100644 examples/io/vsgcustombuilders/vsgcustombuilders.cpp diff --git a/examples/io/CMakeLists.txt b/examples/io/CMakeLists.txt index 02352b0b..3c90443d 100644 --- a/examples/io/CMakeLists.txt +++ b/examples/io/CMakeLists.txt @@ -3,3 +3,7 @@ add_subdirectory(vsgio) add_subdirectory(vsglog) add_subdirectory(vsglog_mt) add_subdirectory(vsgpath) + +if (vsgXchange_FOUND) + add_subdirectory(vsgcustombuilders) +endif() diff --git a/examples/io/vsgcustombuilders/CMakeLists.txt b/examples/io/vsgcustombuilders/CMakeLists.txt new file mode 100644 index 00000000..d6bc2bbc --- /dev/null +++ b/examples/io/vsgcustombuilders/CMakeLists.txt @@ -0,0 +1,10 @@ +set(SOURCES + vsgcustombuilders.cpp +) + +add_executable(vsgcustombuilders ${SOURCES}) + +target_compile_definitions(vsgcustombuilders PRIVATE vsgXchange_FOUND) +target_link_libraries(vsgcustombuilders vsgXchange::vsgXchange) + +install(TARGETS vsgcustombuilders RUNTIME DESTINATION bin) diff --git a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp new file mode 100644 index 00000000..8ad24cd0 --- /dev/null +++ b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp @@ -0,0 +1,446 @@ +#include + +#ifdef vsgXchange_FOUND +# include +#endif + +#include +#include +#include +#include + +vsg::ref_ptr createTextureQuad(vsg::ref_ptr sourceData, vsg::ref_ptr options) +{ + auto builder = vsg::Builder::create(); + builder->options = options; + + vsg::StateInfo state; + state.image = sourceData; + state.lighting = false; + + vsg::GeometryInfo geom; + geom.dx.set(static_cast(sourceData->width()), 0.0f, 0.0f); + geom.dy.set(0.0f, 0.0f, static_cast(sourceData->height())); + geom.dz.set(0.0f, -1.0f, 0.0f); + + return builder->createQuad(geom, state); +} + +void enableGenerateDebugInfo(vsg::ref_ptr options) +{ + auto shaderHints = vsg::ShaderCompileSettings::create(); + shaderHints->generateDebugInfo = true; + + auto& text = options->shaderSets["text"] = vsg::createTextShaderSet(options); + text->defaultShaderHints = shaderHints; + + auto& flat = options->shaderSets["flat"] = vsg::createFlatShadedShaderSet(options); + flat->defaultShaderHints = shaderHints; + + auto& phong = options->shaderSets["phong"] = vsg::createPhongShaderSet(options); + phong->defaultShaderHints = shaderHints; + + auto& pbr = options->shaderSets["pbr"] = vsg::createPhysicsBasedRenderingShaderSet(options); + pbr->defaultShaderHints = shaderHints; +} + +int main(int argc, char** argv) +{ + try + { + // set up defaults and read command line arguments to override them + vsg::CommandLine arguments(&argc, argv); + + // create windowTraits using the any command line arugments to configure settings + auto windowTraits = vsg::WindowTraits::create(arguments); + + // if we want to redirect std::cout and std::cerr to the vsg::Logger call vsg::Logger::redirect_stdout() + if (arguments.read({"--redirect-std", "-r"})) vsg::Logger::instance()->redirect_std(); + + // set up vsg::Options to pass in filepaths, ReaderWriters and other IO related options to use when reading and writing files. + auto options = vsg::Options::create(); + options->sharedObjects = vsg::SharedObjects::create(); + options->fileCache = vsg::getEnv("VSG_FILE_CACHE"); + options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); + +#ifdef vsgXchange_all + // add vsgXchange's support for reading and writing 3rd party file formats + options->add(vsgXchange::all::create()); +#endif + + options->readOptions(arguments); + + if (uint32_t numOperationThreads = 0; arguments.read("--ot", numOperationThreads)) options->operationThreads = vsg::OperationThreads::create(numOperationThreads); + + bool reportAverageFrameRate = arguments.read("--fps"); + bool reportMemoryStats = arguments.read("--rms"); + if (arguments.read({"-t", "--test"})) + { + windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + windowTraits->fullscreen = true; + reportAverageFrameRate = true; + } + if (arguments.read({"--st", "--small-test"})) + { + windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + windowTraits->width = 192, windowTraits->height = 108; + windowTraits->decoration = false; + reportAverageFrameRate = true; + } + + bool multiThreading = arguments.read("--mt"); + auto maxTime = arguments.value(std::numeric_limits::max(), "--max-time"); + + if (arguments.read("--ThreadLogger")) vsg::Logger::instance() = vsg::ThreadLogger::create(); + if (int log_level = 0; arguments.read("--log-level", log_level)) vsg::Logger::instance()->level = vsg::Logger::Level(log_level); + auto numFrames = arguments.value(-1, "-f"); + auto pathFilename = arguments.value("", "-p"); + auto loadLevels = arguments.value(0, "--load-levels"); + auto maxPagedLOD = arguments.value(0, "--maxPagedLOD"); + auto LODScale = arguments.value(1.0, "--LODScale"); + + auto horizonMountainHeight = arguments.value(0.0, "--hmh"); + auto nearFarRatio = arguments.value(0.001, "--nfr"); + if (arguments.read("--rgb")) options->mapRGBtoRGBAHint = false; + + bool depthClamp = arguments.read({"--dc", "--depthClamp"}); + if (depthClamp) + { + std::cout << "Enabled depth clamp." << std::endl; + auto deviceFeatures = windowTraits->deviceFeatures = vsg::DeviceFeatures::create(); + deviceFeatures->get().samplerAnisotropy = VK_TRUE; + deviceFeatures->get().depthClamp = VK_TRUE; + } + + vsg::ref_ptr resourceHints; + if (auto resourceHintsFilename = arguments.value("", "--rh")) + { + resourceHints = vsg::read_cast(resourceHintsFilename, options); + } + + if (auto outputResourceHintsFilename = arguments.value("", "--orh")) + { + if (!resourceHints) resourceHints = vsg::ResourceHints::create(); + vsg::write(resourceHints, outputResourceHintsFilename, options); + return 0; + } + + if (arguments.read({"--shader-debug-info", "--sdi"})) + { + enableGenerateDebugInfo(options); + windowTraits->deviceExtensionNames.push_back(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME); + } + + if (int log_level = 0; arguments.read("--log-level", log_level)) vsg::Logger::instance()->level = vsg::Logger::Level(log_level); + auto logFilename = arguments.value("", "--log"); + + vsg::ref_ptr instrumentation; + if (arguments.read({"--gpu-annotation", "--ga"}) && vsg::isExtensionSupported(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) + { + windowTraits->debugUtils = true; + + auto gpu_instrumentation = vsg::GpuAnnotation::create(); + if (arguments.read("--name")) + gpu_instrumentation->labelType = vsg::GpuAnnotation::SourceLocation_name; + else if (arguments.read("--className")) + gpu_instrumentation->labelType = vsg::GpuAnnotation::Object_className; + else if (arguments.read("--func")) + gpu_instrumentation->labelType = vsg::GpuAnnotation::SourceLocation_function; + + instrumentation = gpu_instrumentation; + } + else if (arguments.read({"--profiler", "--pr"})) + { + // set Profiler options + auto settings = vsg::Profiler::Settings::create(); + arguments.read("--cpu", settings->cpu_instrumentation_level); + arguments.read("--gpu", settings->gpu_instrumentation_level); + arguments.read("--log-size", settings->log_size); + + // create the profiler + instrumentation = vsg::Profiler::create(settings); + } + + vsg::Affinity affinity; + uint32_t cpu = 0; + while (arguments.read("-c", cpu)) + { + affinity.cpus.insert(cpu); + } + + // should animations be automatically played + auto autoPlay = !arguments.read({"--no-auto-play", "--nop"}); + + if (arguments.errors()) return arguments.writeErrorMessages(std::cerr); + + if (argc <= 1) + { + std::cout << "Please specify a 3d model or image file on the command line." << std::endl; + return 1; + } + + auto group = vsg::Group::create(); + + vsg::Path path; + + // read any vsg files + for (int i = 1; i < argc; ++i) + { + vsg::Path filename = arguments[i]; + path = vsg::filePath(filename); + + auto object = vsg::read(filename, options); + if (auto node = object.cast()) + { + group->addChild(node); + } + else if (auto data = object.cast()) + { + if (auto textureGeometry = createTextureQuad(data, options)) + { + group->addChild(textureGeometry); + } + } + else if (object) + { + std::cout << "Unable to view object of type " << object->className() << std::endl; + } + else + { + std::cout << "Unable to load file " << filename << std::endl; + } + } + + if (group->children.empty()) + { + return 1; + } + + vsg::ref_ptr vsg_scene; + if (group->children.size() == 1) + vsg_scene = group->children[0]; + else + vsg_scene = group; + + // create the viewer and assign window(s) to it + auto viewer = vsg::Viewer::create(); + auto window = vsg::Window::create(windowTraits); + if (!window) + { + std::cout << "Could not create window." << std::endl; + return 1; + } + + viewer->addWindow(window); + + auto ellipsoidModel = vsg_scene->getRefObject("EllipsoidModel"); + + vsg::ref_ptr lookAt; + vsg::ref_ptr perspective; + if (ellipsoidModel) + { + // compute the bounds of the scene graph to help position camera + vsg::ComputeBounds computeBounds; + vsg_scene->accept(computeBounds); + + double initialRadius = vsg::length(computeBounds.bounds.max - computeBounds.bounds.min) * 0.5; + double modelToEarthRatio = (initialRadius / ellipsoidModel->radiusEquator()); + + // if the model is small compared to the radius of the earth position camera in local coordinate frame of the model rather than ECEF. + if (modelToEarthRatio < 1.0) + { + vsg::dvec3 lla = ellipsoidModel->convertECEFToLatLongAltitude((computeBounds.bounds.min + computeBounds.bounds.max) * 0.5); + + auto worldToLocal = ellipsoidModel->computeWorldToLocalTransform(lla); + auto localToWorld = ellipsoidModel->computeLocalToWorldTransform(lla); + + // recompute the bounds of the model in the local coordinate frame of the model, rather than ECEF + // to give a tigher bound around the dataset. + computeBounds.matrixStack.clear(); + computeBounds.matrixStack.push_back(worldToLocal); + computeBounds.bounds.reset(); + vsg_scene->accept(computeBounds); + + auto bounds = computeBounds.bounds; + vsg::dvec3 centre = (bounds.min + bounds.max) * 0.5; + double radius = vsg::length(bounds.max - bounds.min) * 0.5; + + lookAt = vsg::LookAt::create(localToWorld * (centre + vsg::dvec3(0.0, 0.0, radius)), localToWorld * centre, vsg::dvec3(0.0, 1.0, 0.0) * worldToLocal); + } + else + { + lookAt = vsg::LookAt::create(vsg::dvec3(initialRadius * 2.0, 0.0, 0.0), vsg::dvec3(0.0, 0.0, 0.0), vsg::dvec3(0.0, 0.0, 1.0)); + } + + perspective = vsg::EllipsoidPerspective::create(lookAt, ellipsoidModel, 30.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), nearFarRatio, horizonMountainHeight); + } + else + { + // compute the bounds of the scene graph to help position camera + vsg::ComputeBounds computeBounds; + vsg_scene->accept(computeBounds); + + vsg::dvec3 centre = (computeBounds.bounds.min + computeBounds.bounds.max) * 0.5; + double radius = vsg::length(computeBounds.bounds.max - computeBounds.bounds.min) * 0.6; + + // set up the camera + lookAt = vsg::LookAt::create(centre + vsg::dvec3(0.0, -radius * 3.5, 0.0), centre, vsg::dvec3(0.0, 0.0, 1.0)); + perspective = vsg::Perspective::create(30.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), nearFarRatio * radius, radius * 10.5); + } + + auto camera = vsg::Camera::create(perspective, lookAt, vsg::ViewportState::create(window->extent2D())); + + // add close handler to respond to the close window button and pressing escape + viewer->addEventHandler(vsg::CloseHandler::create(viewer)); + + auto cameraAnimation = vsg::CameraAnimationHandler::create(camera, pathFilename, options); + viewer->addEventHandler(cameraAnimation); + if (autoPlay && cameraAnimation->animation) + { + cameraAnimation->play(); + + if (reportAverageFrameRate && maxTime == std::numeric_limits::max()) + { + maxTime = cameraAnimation->animation->maxTime(); + } + } + + viewer->addEventHandler(vsg::Trackball::create(camera, ellipsoidModel)); + + // if required preload specific number of PagedLOD levels. + if (loadLevels > 0) + { + vsg::LoadPagedLOD loadPagedLOD(camera, loadLevels); + + auto startTime = vsg::clock::now(); + + vsg_scene->accept(loadPagedLOD); + + auto time = std::chrono::duration(vsg::clock::now() - startTime).count(); + std::cout << "No. of tiles loaded " << loadPagedLOD.numTiles << " in " << time << "ms." << std::endl; + } + + auto view = vsg::View::create(camera); + view->LODScale = LODScale; + view->addChild(vsg::createHeadlight()); + view->addChild(vsg_scene); + + auto renderGraph = vsg::RenderGraph::create(window, view); + auto commandGraph = vsg::CommandGraph::create(window, renderGraph); + viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); + + if (instrumentation) viewer->assignInstrumentation(instrumentation); + + if (multiThreading) + { + viewer->setupThreading(); + + if (affinity) + { + auto cpu_itr = affinity.cpus.begin(); + + // set affinity of main thread + if (cpu_itr != affinity.cpus.end()) + { + std::cout << "vsg::setAffinity() " << *cpu_itr << std::endl; + vsg::setAffinity(vsg::Affinity(*cpu_itr++)); + } + + for (auto& thread : viewer->threads) + { + if (thread.joinable() && cpu_itr != affinity.cpus.end()) + { + std::cout << "vsg::setAffinity(" << thread.get_id() << ") " << *cpu_itr << std::endl; + vsg::setAffinity(thread, vsg::Affinity(*cpu_itr++)); + } + } + } + } + else if (affinity) + { + std::cout << "vsg::setAffinity("; + for (auto cpu_num : affinity.cpus) + { + std::cout << " " << cpu_num; + } + std::cout << " )" << std::endl; + + vsg::setAffinity(affinity); + } + + viewer->compile(resourceHints); + + if (maxPagedLOD > 0) + { + // set targetMaxNumPagedLODWithHighResSubgraphs after Viewer::compile() as it will assign any DatabasePager if required. + for (auto& task : viewer->recordAndSubmitTasks) + { + if (task->databasePager) task->databasePager->targetMaxNumPagedLODWithHighResSubgraphs = maxPagedLOD; + } + } + + if (autoPlay) + { + // find any animation groups in the loaded scene graph and play the first animation in each of the animation groups. + auto animationGroups = vsg::visit(vsg_scene).animationGroups; + for (auto ag : animationGroups) + { + if (!ag->animations.empty()) viewer->animationManager->play(ag->animations.front()); + } + } + + viewer->start_point() = vsg::clock::now(); + + // rendering main loop + while (viewer->advanceToNextFrame() && (numFrames < 0 || (numFrames--) > 0) && (viewer->getFrameStamp()->simulationTime < maxTime)) + { + // pass any events into EventHandlers assigned to the Viewer + viewer->handleEvents(); + + viewer->update(); + + viewer->recordAndSubmit(); + + viewer->present(); + } + + if (reportAverageFrameRate) + { + auto fs = viewer->getFrameStamp(); + double fps = static_cast(fs->frameCount) / std::chrono::duration(vsg::clock::now() - viewer->start_point()).count(); + std::cout << "Average frame rate = " << fps << " fps" << std::endl; + } + + if (reportMemoryStats) + { + if (options->sharedObjects) + { + vsg::LogOutput output; + options->sharedObjects->report(output); + } + } + + if (auto profiler = instrumentation.cast()) + { + instrumentation->finish(); + if (logFilename) + { + std::ofstream fout(logFilename); + profiler->log->report(fout); + } + else + { + profiler->log->report(std::cout); + } + } + } + catch (const vsg::Exception& ve) + { + for (int i = 0; i < argc; ++i) std::cerr << argv[i] << " "; + std::cerr << "\n[Exception] - " << ve.message << " result = " << ve.result << std::endl; + return 1; + } + + // clean up done automatically thanks to ref_ptr<> + return 0; +} From dbf0f885717465de5dc4f8352acf63e778932829 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 22 Apr 2026 17:37:23 +0100 Subject: [PATCH 2/7] Created initial test of custom gltf::Builder class --- CMakeLists.txt | 2 +- .../vsgcustombuilders/vsgcustombuilders.cpp | 72 ++++++------------- 2 files changed, 24 insertions(+), 50 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bfe1bbb3..1e71d474 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ vsg_setup_dir_vars() vsg_setup_build_vars() # find the optional vsgXchange that can be used for reading a range of image and 3d model formats and shader compilation -find_package(vsgXchange 1.1.9 QUIET) +find_package(vsgXchange 1.1.12 QUIET) # find the optional vsgImGui that can be used for GUI elements added into graphics windows. find_package(vsgImGui QUIET) diff --git a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp index 8ad24cd0..6702a719 100644 --- a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp +++ b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp @@ -1,49 +1,41 @@ #include -#ifdef vsgXchange_FOUND -# include -#endif +#include +#include +#include #include #include #include #include -vsg::ref_ptr createTextureQuad(vsg::ref_ptr sourceData, vsg::ref_ptr options) +namespace CustomBuilders { - auto builder = vsg::Builder::create(); - builder->options = options; - - vsg::StateInfo state; - state.image = sourceData; - state.lighting = false; - - vsg::GeometryInfo geom; - geom.dx.set(static_cast(sourceData->width()), 0.0f, 0.0f); - geom.dy.set(0.0f, 0.0f, static_cast(sourceData->height())); - geom.dz.set(0.0f, -1.0f, 0.0f); - return builder->createQuad(geom, state); -} - -void enableGenerateDebugInfo(vsg::ref_ptr options) +class MyGltfBuilder : public vsg::Inherit { - auto shaderHints = vsg::ShaderCompileSettings::create(); - shaderHints->generateDebugInfo = true; - - auto& text = options->shaderSets["text"] = vsg::createTextShaderSet(options); - text->defaultShaderHints = shaderHints; +public: + MyGltfBuilder() + { + vsg::info("MyGltfBuilder() ", this); + } - auto& flat = options->shaderSets["flat"] = vsg::createFlatShadedShaderSet(options); - flat->defaultShaderHints = shaderHints; + MyGltfBuilder(const MyGltfBuilder&, const vsg::CopyOp& = {}) : + Inherit() + { + } - auto& phong = options->shaderSets["phong"] = vsg::createPhongShaderSet(options); - phong->defaultShaderHints = shaderHints; + vsg::ref_ptr clone(const vsg::CopyOp& copyop = {}) const override + { + return MyGltfBuilder::create(*this, copyop); + } +}; - auto& pbr = options->shaderSets["pbr"] = vsg::createPhysicsBasedRenderingShaderSet(options); - pbr->defaultShaderHints = shaderHints; } +EVSG_type_name(CustomBuilders::MyGltfBuilder) + + int main(int argc, char** argv) { try @@ -62,6 +54,7 @@ int main(int argc, char** argv) options->sharedObjects = vsg::SharedObjects::create(); options->fileCache = vsg::getEnv("VSG_FILE_CACHE"); options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); + options->setObject("gltf::Builder", CustomBuilders::MyGltfBuilder::create()); #ifdef vsgXchange_all // add vsgXchange's support for reading and writing 3rd party file formats @@ -118,18 +111,6 @@ int main(int argc, char** argv) resourceHints = vsg::read_cast(resourceHintsFilename, options); } - if (auto outputResourceHintsFilename = arguments.value("", "--orh")) - { - if (!resourceHints) resourceHints = vsg::ResourceHints::create(); - vsg::write(resourceHints, outputResourceHintsFilename, options); - return 0; - } - - if (arguments.read({"--shader-debug-info", "--sdi"})) - { - enableGenerateDebugInfo(options); - windowTraits->deviceExtensionNames.push_back(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME); - } if (int log_level = 0; arguments.read("--log-level", log_level)) vsg::Logger::instance()->level = vsg::Logger::Level(log_level); auto logFilename = arguments.value("", "--log"); @@ -194,13 +175,6 @@ int main(int argc, char** argv) { group->addChild(node); } - else if (auto data = object.cast()) - { - if (auto textureGeometry = createTextureQuad(data, options)) - { - group->addChild(textureGeometry); - } - } else if (object) { std::cout << "Unable to view object of type " << object->className() << std::endl; From dc4afd2ffd6af87578f8249355c2ffa674b16201 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 22 Apr 2026 17:53:43 +0100 Subject: [PATCH 3/7] Added more debug output. --- examples/io/vsgcustombuilders/vsgcustombuilders.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp index 6702a719..7121d5d4 100644 --- a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp +++ b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp @@ -20,9 +20,10 @@ class MyGltfBuilder : public vsg::Inherit clone(const vsg::CopyOp& copyop = {}) const override From 4b32178121bd6b0ea75b9808beb937560b31b162 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 22 Apr 2026 17:59:37 +0100 Subject: [PATCH 4/7] Added tiles for customizing Tiles3D::Builder --- .../vsgcustombuilders/vsgcustombuilders.cpp | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp index 7121d5d4..44126a28 100644 --- a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp +++ b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp @@ -32,10 +32,30 @@ class MyGltfBuilder : public vsg::Inherit +{ +public: + MyTiles3DBuilder() + { + vsg::info("MyTiles3DBuilder() ", this); + } + + MyTiles3DBuilder(const MyTiles3DBuilder& rhs, const vsg::CopyOp& = {}) : + Inherit() + { + vsg::info("MyTiles3DBuilder(const MyTiles3DBuilder& ", &rhs, ", const vsg::CopyOp& = {}) ", this); + } + + vsg::ref_ptr clone(const vsg::CopyOp& copyop = {}) const override + { + return MyTiles3DBuilder::create(*this, copyop); + } +}; + } EVSG_type_name(CustomBuilders::MyGltfBuilder) - +EVSG_type_name(CustomBuilders::MyTiles3DBuilder) int main(int argc, char** argv) { @@ -55,7 +75,9 @@ int main(int argc, char** argv) options->sharedObjects = vsg::SharedObjects::create(); options->fileCache = vsg::getEnv("VSG_FILE_CACHE"); options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); + options->setObject("gltf::Builder", CustomBuilders::MyGltfBuilder::create()); + options->setObject("Tiles3D::Builder", CustomBuilders::MyTiles3DBuilder::create()); #ifdef vsgXchange_all // add vsgXchange's support for reading and writing 3rd party file formats From 05316822d6d48e60d563995f3c10af0f3b896fee Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 23 Apr 2026 15:06:04 +0100 Subject: [PATCH 5/7] Added custom assimp::Builder --- .../vsgcustombuilders/vsgcustombuilders.cpp | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp index 44126a28..566a56b9 100644 --- a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp +++ b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp @@ -1,6 +1,8 @@ #include #include + +#include #include #include @@ -52,10 +54,31 @@ class MyTiles3DBuilder : public vsg::Inherit +{ +public: + MyAssimpBuilder() + { + vsg::info("MyAssimpBuilder() ", this); + } + + MyAssimpBuilder(const MyAssimpBuilder& rhs, const vsg::CopyOp& = {}) : + Inherit() + { + vsg::info("MyAssimpBuilder(const MyAssimpBuilder& ", &rhs, ", const vsg::CopyOp& = {}) ", this); + } + + vsg::ref_ptr clone(const vsg::CopyOp& copyop = {}) const override + { + return MyAssimpBuilder::create(*this, copyop); + } +}; + } EVSG_type_name(CustomBuilders::MyGltfBuilder) EVSG_type_name(CustomBuilders::MyTiles3DBuilder) +EVSG_type_name(CustomBuilders::MyAssimpBuilder) int main(int argc, char** argv) { @@ -78,6 +101,7 @@ int main(int argc, char** argv) options->setObject("gltf::Builder", CustomBuilders::MyGltfBuilder::create()); options->setObject("Tiles3D::Builder", CustomBuilders::MyTiles3DBuilder::create()); + options->setObject("assimp::Builder", CustomBuilders::MyAssimpBuilder::create()); #ifdef vsgXchange_all // add vsgXchange's support for reading and writing 3rd party file formats From 9942649322b77bfecc1b505309d0444123c16ab0 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 23 Apr 2026 15:44:18 +0100 Subject: [PATCH 6/7] Standardized on using vsgXchange::class::prototype_builder for key name of prototype builder. --- examples/io/vsgcustombuilders/vsgcustombuilders.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp index 566a56b9..f01184f2 100644 --- a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp +++ b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp @@ -99,9 +99,9 @@ int main(int argc, char** argv) options->fileCache = vsg::getEnv("VSG_FILE_CACHE"); options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); - options->setObject("gltf::Builder", CustomBuilders::MyGltfBuilder::create()); - options->setObject("Tiles3D::Builder", CustomBuilders::MyTiles3DBuilder::create()); - options->setObject("assimp::Builder", CustomBuilders::MyAssimpBuilder::create()); + options->setObject(vsgXchange::gltf::prototype_builder, CustomBuilders::MyGltfBuilder::create()); + options->setObject(vsgXchange::Tiles3D::prototype_builder, CustomBuilders::MyTiles3DBuilder::create()); + options->setObject(vsgXchange::assimp::prototype_builder, CustomBuilders::MyAssimpBuilder::create()); #ifdef vsgXchange_all // add vsgXchange's support for reading and writing 3rd party file formats From 3e5b4d39ecc105e7bd823ecd8188ee039e24ea94 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 23 Apr 2026 16:49:00 +0100 Subject: [PATCH 7/7] Added overrides of Builder::createSceneGraph(..) methods. --- .../vsgcustombuilders/vsgcustombuilders.cpp | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp index f01184f2..cd4bdbc3 100644 --- a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp +++ b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp @@ -19,19 +19,25 @@ class MyGltfBuilder : public vsg::Inherit clone(const vsg::CopyOp& copyop = {}) const override { return MyGltfBuilder::create(*this, copyop); } + + + vsg::ref_ptr createSceneGraph(vsg::ref_ptr in_model, vsg::ref_ptr in_options) override + { + vsg::info("MyGltfBuilder::createSceneGraph(", in_model, ", ", in_options,") ", this); + return Inherit::createSceneGraph(in_model, in_options); + } + }; class MyTiles3DBuilder : public vsg::Inherit @@ -39,19 +45,23 @@ class MyTiles3DBuilder : public vsg::Inherit clone(const vsg::CopyOp& copyop = {}) const override { return MyTiles3DBuilder::create(*this, copyop); } + + vsg::ref_ptr createSceneGraph(vsg::ref_ptr tileset, vsg::ref_ptr in_options) override + { + vsg::info("MyTiles3DBuilder::createSceneGraph(", tileset, ", ", in_options,") ", this); + return Inherit::createSceneGraph(tileset, in_options); + } }; class MyAssimpBuilder : public vsg::Inherit @@ -59,19 +69,25 @@ class MyAssimpBuilder : public vsg::Inherit clone(const vsg::CopyOp& copyop = {}) const override { return MyAssimpBuilder::create(*this, copyop); } + + vsg::ref_ptr createSceneGraph(const aiScene* in_scene, vsg::ref_ptr in_options, const vsg::Path& ext) override + { + vsg::info("MyAssimpBuilder::createSceneGraph(", in_scene, ", ", in_options, ", ", ext, ") ", this); + return Inherit::createSceneGraph(in_scene, in_options, ext); + } + + }; }