Created
July 12, 2025 18:27
-
-
Save dov/85e967bd0598edccac4a245893721858 to your computer and use it in GitHub Desktop.
VSG example of how to modify the color of an STL file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//====================================================================== | |
// load-stl-and-change-color.cpp - | |
// | |
// Based on vsghelloworld.cpp | |
// | |
// Dov Grobgeld <[email protected]> | |
// Sat Jul 12 04:38:30 2025 | |
//---------------------------------------------------------------------- | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <fmt/core.h> | |
#include <vsgXchange/all.h> | |
#include <vsg/all.h> | |
#include <iostream> | |
using namespace std; | |
using fmt::print; | |
template <typename... Args> | |
static void die(fmt::format_string<Args...> FormatStr, Args &&... args) | |
{ | |
string msg = fmt::format(FormatStr, std::forward<Args>(args)...); | |
if (msg[msg.size()-1] != '\n') | |
msg += "\n"; | |
fmt::print(stderr, "{}", msg); | |
exit(-1); | |
} | |
// Extract a pointer to the materialValue | |
vsg::PbrMaterialValue *extractMaterialValue(vsg::Node* node, bool makeDynamic=true) | |
{ | |
struct ExtractMaterialVisitor : public vsg::Visitor | |
{ | |
ExtractMaterialVisitor() {} | |
void apply(vsg::Object& object) override | |
{ | |
object.traverse(*this); | |
} | |
// Find and return the PbrMaterial value | |
void apply(vsg::DescriptorBuffer& db) override | |
{ | |
print("Found descriptor buffer with {} descriptors\n", db.bufferInfoList.size()); | |
for (auto& bfi : db.bufferInfoList) | |
{ | |
auto data = bfi->data; | |
print("Found bfi of type {}\n", data->type_info().name()); | |
if (data->is_compatible(typeid(vsg::PbrMaterialValue))) { | |
if (makeDynamic) | |
data->properties.dataVariance = vsg::DYNAMIC_DATA; | |
// Store the materialValue so that it can be returned to the user | |
this->materialValue = data->cast<vsg::PbrMaterialValue>(); | |
} | |
} | |
} | |
bool makeDynamic = true; | |
vsg::PbrMaterialValue *materialValue = nullptr; | |
}; | |
ExtractMaterialVisitor emv; | |
emv.makeDynamic = makeDynamic; | |
node->accept(emv); | |
if (!emv.materialValue) | |
throw std::runtime_error("Failed finding the material value!"); | |
return emv.materialValue; | |
} | |
int main(int argc, char** argv) | |
{ | |
// set up defaults and read command line arguments to override them | |
vsg::CommandLine arguments(&argc, argv); | |
// 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(vsgXchange::all::create()); | |
options->fileCache = vsg::getEnv("VSG_FILE_CACHE"); | |
options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); | |
arguments.read(options); | |
if (argc == 1) | |
die("Need filename of model!\n"); | |
vsg::Path filename = arguments[1]; | |
if (arguments.errors()) return arguments.writeErrorMessages(std::cerr); | |
// load the scene graph | |
vsg::ref_ptr<vsg::Node> vsg_scene = vsg::read_cast<vsg::Node>(filename, options); | |
// Extract the materialValue which can be used to change the property | |
auto materialValue = extractMaterialValue(vsg_scene); | |
// This is how the material is set | |
auto material = (vsg::PbrMaterial*)(materialValue->dataPointer(0)); | |
material->diffuseFactor = vsg::vec4{1.0,0.0,0.0,1.0}; | |
materialValue->dirty(); | |
// vsg_scene->setValue("name", filename); | |
// Save the scene to a vsgt file for manual inspection | |
vsg::write(vsg_scene, "/tmp/vsg-scene.vsgt", options); | |
if (!vsg_scene) return 0; | |
// create the viewer and assign window(s) to it | |
auto windowTraits = vsg::WindowTraits::create(); | |
windowTraits->windowTitle = filename; | |
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); | |
// 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; | |
double nearFarRatio = 0.001; | |
// set up the camera | |
auto lookAt = vsg::LookAt::create(centre + vsg::dvec3(0.0, -radius * 3.5, 0.0), centre, vsg::dvec3(0.0, 0.0, 1.0)); | |
vsg::ref_ptr<vsg::ProjectionMatrix> perspective; | |
auto ellipsoidModel = vsg_scene->getRefObject<vsg::EllipsoidModel>("EllipsoidModel"); | |
if (ellipsoidModel) | |
{ | |
double horizonMountainHeight = 0.0; | |
perspective = vsg::EllipsoidPerspective::create(lookAt, ellipsoidModel, 30.0, static_cast<double>(window->extent2D().width) / static_cast<double>(window->extent2D().height), nearFarRatio, horizonMountainHeight); | |
} | |
else | |
{ | |
perspective = vsg::Perspective::create(30.0, static_cast<double>(window->extent2D().width) / static_cast<double>(window->extent2D().height), nearFarRatio * radius, radius * 4.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)); | |
// add trackball to control the Camera | |
viewer->addEventHandler(vsg::Trackball::create(camera, ellipsoidModel)); | |
// add the CommandGraph to render the scene | |
auto commandGraph = vsg::createCommandGraphForView(window, camera, vsg_scene); | |
viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); | |
// compile all Vulkan objects and transfer image, vertex and primitive data to GPU | |
viewer->compile(); | |
// rendering main loop | |
int frameId = 0; | |
int colorId = 0; | |
while (viewer->advanceToNextFrame()) | |
{ | |
// pass any events into EventHandlers assigned to the Viewer | |
viewer->handleEvents(); | |
viewer->update(); | |
viewer->recordAndSubmit(); | |
viewer->present(); | |
// Toggle the material diffuse color once a second | |
if (frameId % 60 == 0) | |
{ | |
if (colorId == 0) | |
material->diffuseFactor = vsg::vec4{1.0,0.0,0.0,1.0}; | |
else | |
material->diffuseFactor = vsg::vec4{0.0,1.0,0.0,1.0}; | |
colorId = (colorId+1)%2; | |
materialValue->dirty(); | |
} | |
frameId++; | |
} | |
// clean up done automatically thanks to ref_ptr<> | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment