Skip to content

Instantly share code, notes, and snippets.

@dov
Created July 3, 2024 17:35
Show Gist options
  • Save dov/11d43a92db9eaae5e1d55043053474ea to your computer and use it in GitHub Desktop.
Save dov/11d43a92db9eaae5e1d55043053474ea to your computer and use it in GitHub Desktop.
Example of how to highlight object picking in Vulkan Scene Graph
//======================================================================
// hello-picking.cpp - An example of how to select and change the
// color of objects created with the builder in Vulkan Scene Graph.
//
// This file is licensed under the MIT license.
//
// Dov Grobgeld <[email protected]>
// Tue Jul 2 21:59:01 2024
//----------------------------------------------------------------------
#include <vsg/all.h>
#include <iostream>
#include <fmt/core.h>
#include <set>
#include <vsg/utils/Intersector.h>
using namespace std;
class IntersectionHandler : public vsg::Inherit<vsg::Visitor, IntersectionHandler>
{
public:
IntersectionHandler(vsg::ref_ptr<vsg::Group> scene,
vsg::ref_ptr<vsg::Camera> camera,
set<vsg::Node *> objectNodes = {}
) : _scene(scene),
_camera(camera),
_objectNodes(objectNodes)
{
// Set default colors
selectNode();
}
void apply(vsg::ButtonPressEvent& buttonPressEvent) override
{
intersection(buttonPressEvent);
}
void selectNode(const vsg::Node* node = nullptr)
{
for (auto &n : _objectNodes)
{
vsg::vec4 color = node==n ? selected_color : default_color;
setColor((vsg::Node*)n
, color);
}
}
void intersection(vsg::PointerEvent& pointerEvent)
{
auto intersector = vsg::LineSegmentIntersector::create(*_camera, pointerEvent.x, pointerEvent.y);
_scene->accept(*intersector);
fmt::print("Intersected {} objects\n", intersector->intersections.size());
if (intersector->intersections.empty())
return;
// sort the intersections front to back
std::sort(intersector->intersections.begin(),
intersector->intersections.end(),
[](auto& lhs, auto& rhs) { return lhs->ratio < rhs->ratio; });
auto& intersection = intersector->intersections[0];
for (const vsg::Node* node : intersection->nodePath)
{
std::string name;
// std::cout << "className=" << node->className();
node->getValue("name", name);
// check if the node is in the set of selectable objects
if (_objectNodes.find((vsg::Node*)node) != _objectNodes.end())
{
fmt::print("Found selectable object {}\n", name);
selectNode(node);
break;
}
}
}
void setColor(vsg::Node* node,
vsg::vec4 color) {
struct ChangeColor : public vsg::Visitor
{
ChangeColor(vsg::vec4 color) : _color(color) {}
void apply(vsg::Object& object) override
{
object.traverse(*this);
}
void apply(vsg::VertexIndexDraw& vid) override
{
for (auto& buffer : vid.arrays) {
auto data = buffer->data;
if (data->is_compatible(typeid(vsg::vec4Array))) {
// Change the color to green
(*(vsg::vec4*)(data->dataPointer(0)))
= _color;
fmt::print("Changed color to ({},{},{},{})\n",
_color.r, _color.g, _color.b, _color.a
);
data->dirty();
}
}
}
vsg::vec4 _color;
};
ChangeColor cc(color);
node->accept(cc);
}
static constexpr vsg::vec4 default_color = vsg::vec4 {0.5, 0.5, 0.5, 1.0}; // Default color
static constexpr vsg::vec4 selected_color = vsg::vec4 {1.0, 0, 0, 1.0}; // Select color
vsg::ref_ptr<vsg::Node> _scene;
vsg::ref_ptr<vsg::Camera> _camera;
set<vsg::Node*> _objectNodes;
};
// Change the property of the color mode of the list of nodes to be
// dynamic. This is necessary in order to change the color when selecting
// them.
void makeColorDynamic(set<vsg::Node*> nodes)
{
struct ChangeDynamicProperty : public vsg::Visitor
{
ChangeDynamicProperty() {}
void apply(vsg::Object& object) override
{
object.traverse(*this);
}
void apply(vsg::VertexIndexDraw& vid) override
{
for (auto& buffer : vid.arrays) {
auto data = buffer->data;
if (data->is_compatible(typeid(vsg::vec4Array))) {
// Change the property to dynamic
data->properties.dataVariance = vsg::DYNAMIC_DATA;
data->dirty();
}
}
}
vsg::vec4 _color;
};
ChangeDynamicProperty cdp;
for (auto* n : nodes)
n->accept(cdp);
}
int main(int argc, char **argv)
{
auto windowTraits = vsg::WindowTraits::create();
windowTraits->windowTitle = "Hello VSG Select";
auto builder = vsg::Builder::create();
auto scene = vsg::Group::create();
vsg::GeometryInfo geomInfo;
vsg::StateInfo stateInfo;
// create a set of all our selectable objects
set<vsg::Node*> objectNodes;
// Add a box
auto node = builder->createBox(geomInfo, stateInfo);
node->setValue("name", "box");
objectNodes.insert((vsg::Node*)node.get());
scene->addChild(node);
// Add a sphere
geomInfo.position = vsg::vec3(1.5f, 0.0f, 0.0f);
node = builder->createSphere(geomInfo, stateInfo);
objectNodes.insert((vsg::Node*)node.get());
node->setValue("name", "sphere");
scene->addChild(node);
// And a cylinder
geomInfo.position = vsg::vec3(0.75f, 1.0f, 0.0f);
node = builder->createCylinder(geomInfo, stateInfo);
objectNodes.insert((vsg::Node*)node.get());
node->setValue("name", "cylinder");
scene->addChild(node);
// Change the dynamic property of the color nodes
makeColorDynamic(objectNodes);
// compute the bounds of the scene graph to help position the camera
vsg::ComputeBounds computeBounds;
scene->accept(computeBounds);
vsg::dvec3 centre = (computeBounds.bounds.min + computeBounds.bounds.max) * 0.5;
// centre = vsg::dvec3(0,0,0);
// 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);
vsg::ref_ptr<vsg::LookAt> lookAt;
// set up the camera
double radius = 1.0;
lookAt = vsg::LookAt::create(centre + vsg::dvec3(-radius, -radius, -radius)*3.5, centre, vsg::dvec3(0.0, 0.0, 1.0));
double nearFarRatio = 0.001;
auto perspective = vsg::Perspective::create(30.0, static_cast<double>(window->extent2D().width) / static_cast<double>(window->extent2D().height), nearFarRatio * radius, radius * 10.0);
// auto perspective = vsg::Orthographic();
auto camera = vsg::Camera::create(perspective, lookAt, vsg::ViewportState::create(window->extent2D()));
viewer->addEventHandler(vsg::CloseHandler::create(viewer));
viewer->addEventHandler(IntersectionHandler::create(scene, camera, objectNodes));
auto trackball = vsg::Trackball::create(camera);
viewer->addEventHandler(trackball);
// Setup keybindings for looking from different dirs
auto lookAtDiag = vsg::LookAt::create(vsg::dvec3(1.0, 1.0, 1.0)*4.0,
centre,
vsg::dvec3(0.0, 0.0, 1.0));
auto lookAtTop = vsg::LookAt::create(vsg::dvec3(0.0, 0.0, 1.0)*4.0,
centre,
vsg::dvec3(0.0, 1.0, 0.0));
trackball->addKeyViewpoint(vsg::KeySymbol::KEY_f, lookAtDiag, 0.5);
trackball->addKeyViewpoint(vsg::KeySymbol::KEY_7, lookAtTop, 0.5);
auto commandGraph = vsg::createCommandGraphForView(window, camera, scene);
viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph});
viewer->compile();
auto startTime = vsg::clock::now();
double numFramesCompleted = 0.0;
// rendering main loop
while (viewer->advanceToNextFrame())
{
// pass any events into EventHandlers assigned to the Viewer
viewer->handleEvents();
viewer->update();
viewer->recordAndSubmit();
viewer->present();
numFramesCompleted += 1.0;
}
auto duration = std::chrono::duration<double, std::chrono::seconds::period>(vsg::clock::now() - startTime).count();
if (numFramesCompleted > 0.0)
{
std::cout << "Average frame rate = " << (numFramesCompleted / duration) << std::endl;
}
return 0;
exit(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment