Created
July 3, 2024 17:35
-
-
Save dov/11d43a92db9eaae5e1d55043053474ea to your computer and use it in GitHub Desktop.
Example of how to highlight object picking in Vulkan Scene Graph
This file contains 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
//====================================================================== | |
// 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