Skip to content

Instantly share code, notes, and snippets.

@dov
Created June 14, 2024 15:58
Show Gist options
  • Save dov/8a03c01d610203dbca23be1b6d394013 to your computer and use it in GitHub Desktop.
Save dov/8a03c01d610203dbca23be1b6d394013 to your computer and use it in GitHub Desktop.
First effort of a orienter gizmo for Vulkan Scene Graph
//======================================================================
// hello-box.cpp - Show a box with vsg viewer
//
// Extended to add keyboard interaction
//
// Dov Grobgeld <[email protected]>
// Sat Mar 16 23:25:06 2024
//----------------------------------------------------------------------
#include <vsg/all.h>
#include <iostream>
#include <vsg/app/ProjectionMatrix.h>
#include <spnav.h>
#include <fmt/core.h>
using fmt::print;
// Inherit from trackball but block 3D operations.
class OrienterTrackball : public vsg::Inherit<vsg::Trackball, OrienterTrackball>
{
public:
explicit OrienterTrackball(vsg::ref_ptr<vsg::Camera> camera,
vsg::ref_ptr<vsg::Camera> modelCamera,
vsg::ref_ptr<vsg::Trackball> modelTrackball_)
: vsg::Inherit<vsg::Trackball, OrienterTrackball>(camera),
modelTrackball(modelTrackball_)
{
// Setup the orienter camera based on the model camera
auto lookAt = vsg::LookAt::create();
lookAt->set(modelCamera->viewMatrix->transform());
setViewpoint(lookAt);
}
// Giv like image manipulation
void apply(vsg::KeyPressEvent& keyPress) override
{
vsg::Trackball::apply(keyPress);
keyPress.handled = false;
modelTrackball->apply(keyPress);
}
void apply(vsg::KeyReleaseEvent& keyRelease) override
{
vsg::Trackball::apply(keyRelease);
keyRelease.handled = false;
modelTrackball->apply(keyRelease);
}
void apply(vsg::FocusInEvent& focusIn) override
{
modelTrackball->apply(focusIn);
}
void apply(vsg::FocusOutEvent& focusOut) override
{
modelTrackball->apply(focusOut);
}
void apply(vsg::ButtonPressEvent& buttonPress) override
{
vsg::Trackball::apply(buttonPress);
buttonPress.handled = false;
modelTrackball->apply(buttonPress);
}
void apply(vsg::ButtonReleaseEvent& buttonRelease) override
{
vsg::Trackball::apply(buttonRelease);
buttonRelease.handled = false;
modelTrackball->apply(buttonRelease);
}
void apply(vsg::MoveEvent& moveEvent) override
{
vsg::Trackball::apply(moveEvent);
moveEvent.handled = false;
modelTrackball->apply(moveEvent);
}
void apply(vsg::ScrollWheelEvent& scrollWheel) override
{
modelTrackball->apply(scrollWheel);
}
void apply(vsg::TouchDownEvent& touchDown) override
{
modelTrackball->apply(touchDown);
}
void apply(vsg::TouchUpEvent& touchUp) override
{
modelTrackball->apply(touchUp);
}
void apply(vsg::TouchMoveEvent& touchMove) override
{
modelTrackball->apply(touchMove);
}
void apply(vsg::FrameEvent& frame) override
{
modelTrackball->apply(frame);
}
void rotate(double angle, const vsg::dvec3& axis) override
{
vsg::Trackball::rotate(angle, axis);
modelTrackball->rotate(angle, axis);
}
void zoom(double ratio) override
{
modelTrackball->zoom(ratio);
}
void pan(const vsg::dvec2& delta) override
{
modelTrackball->pan(delta);
}
bool withinRenderArea(const vsg::PointerEvent& pointerEvent) const
{
return modelTrackball->withinRenderArea(pointerEvent);
}
bool eventRelevant(const vsg::WindowEvent& event) const {
return modelTrackball->eventRelevant(event);
}
vsg::ref_ptr<vsg::Trackball> modelTrackball;
};
// Create an arrow with the back at pos and pointing in the direction of dir
// Place a cone at the end of the arrow with the color color
vsg::ref_ptr<vsg::Node> create_arrow(vsg::vec3 pos, vsg::vec3 dir, vsg::vec4 color)
{
vsg::ref_ptr<vsg::Group> arrow = vsg::Group::create();
vsg::Builder builder;
vsg::GeometryInfo geomInfo;
vsg::StateInfo stateInfo;
geomInfo.color = vsg::vec4{1,1,1,1};
geomInfo.position = pos;
geomInfo.transform = vsg::translate(0.0f, 0.0f, 0.5f);
// If we don't point in the z-direction, then rotate the arrow
if (vsg::length(vsg::cross(vsg::vec3{0,0,1}, dir)) > 0.0001)
{
vsg::vec3 axis = vsg::cross(vsg::vec3{0,0,1}, dir);
float angle = acos(vsg::dot(vsg::vec3{0,0,1}, dir));
geomInfo.transform = vsg::rotate(angle, axis) * geomInfo.transform;
}
auto axis_transform = geomInfo.transform;
geomInfo.transform = geomInfo.transform * vsg::scale(0.1f, 0.1f, 1.0f);
// Rotate geomInfo from pos in the direction of dir
auto node = builder.createCylinder(geomInfo, stateInfo);
arrow->addChild(node);
// The cone
geomInfo.color = color;
// This would have been cleaner with a pre_translate transform
geomInfo.transform = vsg::scale(0.3f, 0.3f, 0.3f) * axis_transform * vsg::translate(0.0f, 0.0f, 1.0f/0.3f);
node = builder.createCone(geomInfo, stateInfo);
arrow->addChild(node);
return arrow;
}
// Create three arrows of the coordinate axes
vsg::ref_ptr<vsg::Node> create_gizmo()
{
vsg::ref_ptr<vsg::Group> gizmo = vsg::Group::create();
gizmo->addChild(create_arrow(vsg::vec3{0,0,0}, vsg::vec3{1,0,0}, vsg::vec4{1,0,0,1}));
gizmo->addChild(create_arrow(vsg::vec3{0,0,0}, vsg::vec3{0,1,0}, vsg::vec4{0,1,0,1}));
gizmo->addChild(create_arrow(vsg::vec3{0,0,0}, vsg::vec3{0,0,1}, vsg::vec4{0,0,1,1}));
vsg::Builder builder;
vsg::GeometryInfo geomInfo;
vsg::StateInfo stateInfo;
geomInfo.color = vsg::vec4{1,1,1,1};
geomInfo.transform = vsg::scale(0.1f, 0.1f, 0.1f);
auto sphere = builder.createSphere(geomInfo, stateInfo);
gizmo->addChild(sphere);
return gizmo;
}
int main(int argc, char **argv)
{
bool swapyz = true;
if(spnav_open() == -1)
{
std::cerr << "failed to connect to the space navigator daemon" << std::endl;
return -1;
}
auto windowTraits = vsg::WindowTraits::create();
windowTraits->windowTitle = "Gizmo Example";
auto builder = vsg::Builder::create();
auto scene = vsg::Group::create();
auto sceneOrienter = vsg::Group::create();
vsg::GeometryInfo geomInfo;
vsg::StateInfo stateInfo;
// Change box color to red
geomInfo.color = vsg::vec4 {1.0f, 0.0f, 0.0f, 1.0f};
auto node = builder->createBox(geomInfo, stateInfo);
scene->addChild(node);
node = create_gizmo();
sceneOrienter->addChild(node);
// 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));
// 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));
//auto commandGraph = vsg::createCommandGraphForView(window, camera, scene);
auto commandGraph = vsg::CommandGraph::create(window);
auto renderGraph = vsg::RenderGraph::create(window);
// create the normal 3D view of the scene
auto view = vsg::View::create(camera);
view->addChild(vsg::createHeadlight());
view->addChild(scene);
renderGraph->addChild(view);
// To get another overlay need to add another view(?) to rendergraph
double aspectRatio = static_cast<double>(window->extent2D().width) / static_cast<double>(window->extent2D().height);
auto orienterCamera = vsg::Camera::create(
vsg::Orthographic::create(-5,5, // left, right
-5/aspectRatio,5/aspectRatio, // bottom, top
-1000,1000), // near, far
vsg::LookAt::create(vsg::dvec3(0.0, 0.0, 1.0),
vsg::dvec3(0.0, 0.0, 0.0),
vsg::dvec3(0.0, 1.0, 0.0)),
vsg::ViewportState::create(window->extent2D()));
auto modelTrackball = vsg::Trackball::create(camera);
auto orienterTrackball = OrienterTrackball::create(orienterCamera, camera, modelTrackball);
viewer->addEventHandler(orienterTrackball); // Blocks events to trackball!
modelTrackball->addKeyViewpoint(vsg::KeySymbol::KEY_f, lookAtDiag, 0.5);
modelTrackball->addKeyViewpoint(vsg::KeySymbol::KEY_7, lookAtTop, 0.5);
// auto view_overlay = vsg::View::create(static_camera);
auto view_overlay = vsg::View::create(orienterCamera);
view_overlay->addChild(vsg::createHeadlight());
view_overlay->addChild(sceneOrienter);
renderGraph->addChild(view_overlay);
commandGraph->addChild(renderGraph);
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;
double dx = 0, dy = 0, dz = 0, xrot = 0, yrot = 0, zrot = 0;
spnav_event event;
if ( spnav_poll_event(&event) > 0)
{
if (event.type == SPNAV_EVENT_MOTION)
{
dx = 0.0005* event.motion.x;
dy = 0.0005* event.motion.y;
dz = 0.0005* event.motion.z;
xrot = -0.0005* event.motion.rx;
yrot = -0.0005* event.motion.ry;
zrot = -0.0005* event.motion.rz;
}
if (swapyz)
{
std::swap(dy, dz);
dy = -dy;
std::swap(yrot, zrot);
}
// Don't accumulate events
spnav_remove_events(SPNAV_EVENT_MOTION);
}
orienterTrackball->pan({dx,dy});
orienterTrackball->zoom(dz);
orienterTrackball->rotate(xrot, vsg::dvec3(1,0,0));
orienterTrackball->rotate(yrot, vsg::dvec3(0,1,0));
orienterTrackball->rotate(zrot, vsg::dvec3(0,0,1));
}
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