Created
June 14, 2024 15:58
-
-
Save dov/8a03c01d610203dbca23be1b6d394013 to your computer and use it in GitHub Desktop.
First effort of a orienter gizmo for 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-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