Skip to content

Instantly share code, notes, and snippets.

@dov
Created October 11, 2024 08:11
Show Gist options
  • Save dov/90763c1138e0db214430e1d4c2325cff to your computer and use it in GitHub Desktop.
Save dov/90763c1138e0db214430e1d4c2325cff to your computer and use it in GitHub Desktop.
//======================================================================
// select-rectangle.cpp
//
// This program should demonstrate how to select multiple objects
// by drawing a rectangle with the mouse.
//
// This snippet is licensed under the MIT license.
//
// Dov Grobgeld <[email protected]>
// 2024-10-11 Fri
//----------------------------------------------------------------------
#include <vsg/all.h>
#include <iostream>
#include <vsg/app/ProjectionMatrix.h>
#include <fmt/format.h>
using fmt::print;
struct Rectangle
{
vsg::vec2 pos;
vsg::vec2 size;
};
class MouseHandler : public vsg::Inherit<vsg::Visitor, MouseHandler>
{
public:
void apply(vsg::PointerEvent& pointerEvent) override
{
lastPointerEvent = &pointerEvent;
isPressed = pointerEvent.mask != vsg::BUTTON_MASK_OFF;
pointerEvent.handled = true;
}
vsg::ref_ptr<vsg::PointerEvent> lastPointerEvent;
bool isPressed = false;
};
vsg::ref_ptr<vsg::Node> draw_rectangle(vsg::ref_ptr<vsg::Window> window,
const Rectangle& rect)
{
vsg::ref_ptr<vsg::Builder> builder = vsg::Builder::create();
vsg::ref_ptr<vsg::Group> node = vsg::Group::create();
vsg::ref_ptr<vsg::Node> quatNode;
auto w = window->extent2D().width;
auto h = window->extent2D().height;
vsg::GeometryInfo geomInfo;
vsg::StateInfo stateInfo;
float frame_width_in_pixels = 2.0f;
// transparent interior rectangle
stateInfo.blending = true;
geomInfo.color = vsg::vec4 {0.0f, 1.0f, 0.0f, 0.2f};
geomInfo.transform = vsg::translate(rect.pos.x/w, rect.pos.y/h, 0.0f)*vsg::scale(rect.size.x/w, rect.size.y/h, 1.0f)*vsg::translate(0.5f, 0.5f, 0.0f);
quatNode = builder->createQuad(geomInfo, stateInfo);
node->addChild(quatNode);
geomInfo.color = vsg::vec4 {0.0f, 1.0f, 0.0f, 0.5f};
// horizontal lines
for (auto y : {rect.pos.y- frame_width_in_pixels, rect.pos.y + rect.size.y})
{
geomInfo.transform = vsg::translate(rect.pos.x/w, y/h, 0.0f)*vsg::scale(rect.size.x/w, frame_width_in_pixels/h, 1.0f)*vsg::translate(0.5f, 0.50f, 0.0f);
quatNode = builder->createQuad(geomInfo, stateInfo);
node->addChild(quatNode);
}
// vertical lines
for (auto x: {rect.pos.x- frame_width_in_pixels, rect.pos.x + rect.size.x})
{
geomInfo.transform = vsg::translate(x/w, (rect.pos.y-frame_width_in_pixels)/h, 0.0f)*vsg::scale(frame_width_in_pixels/w, (rect.size.y+2*frame_width_in_pixels)/h, 1.0f)*vsg::translate(0.5f, 0.50f, 0.0f);
quatNode = builder->createQuad(geomInfo, stateInfo);
node->addChild(quatNode);
}
return node;
}
int main(int argc, char **argv)
{
auto windowTraits = vsg::WindowTraits::create();
windowTraits->windowTitle = "select-rectangle";
windowTraits->width = 800;
windowTraits->height = 600;
windowTraits->debugLayer = true;
auto builder = vsg::Builder::create();
auto scene = vsg::Group::create();
vsg::ref_ptr<vsg::Node> node;
{
vsg::GeometryInfo geomInfo;
vsg::StateInfo stateInfo;
// Change box color to red
geomInfo.color = vsg::vec4 {0.5f, 0.5f, 0.5f, 1.0f};
geomInfo.transform = vsg::scale(10.0f, 10.0f, 10.0f);
auto ground = builder->createQuad(geomInfo, stateInfo);
scene->addChild(ground);
}
{
vsg::GeometryInfo geomInfo;
vsg::StateInfo stateInfo;
// Change box color to red
geomInfo.color = vsg::vec4 {1.0f, 0.0f, 0.0f, 1.0f};
for (int i = 0; i < 16; i++)
{
geomInfo.transform = vsg::translate(vsg::vec3(0.3f*(i/4), 0.3f*(i%4), 0.3f)) * vsg::scale(0.1f, 0.1f, 0.1f);
node = builder->createBox(geomInfo, stateInfo);
scene->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;
// 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);
// set up the camera
double radius = 1.0;
vsg::ref_ptr<vsg::LookAt> lookAt = vsg::LookAt::create(centre + vsg::dvec3(-2*radius, -radius*2, 0.5*radius)*3.5, centre, vsg::dvec3(0.0, 0.0, 1.0));
double nearFarRatio = 0.0001;
auto perspective = vsg::Perspective::create(30.0, static_cast<double>(window->extent2D().width) / static_cast<double>(window->extent2D().height), nearFarRatio * radius, radius * 100.0);
auto camera = vsg::Camera::create(perspective, lookAt, vsg::ViewportState::create(window->extent2D()));
// Handle the mouse control. This currently always blocks the trackball
auto mouseHandler = MouseHandler::create();
viewer->addEventHandler(mouseHandler);
viewer->addEventHandler(vsg::CloseHandler::create(viewer));
auto trackball = vsg::Trackball::create(camera);
viewer->addEventHandler(trackball);
auto commandGraph = vsg::createCommandGraphForView(window, camera, scene);
auto renderGraph = vsg::RenderGraph::create(window);
auto view = vsg::View::create(camera);
view->addChild(vsg::createHeadlight());
view->addChild(scene);
renderGraph->addChild(view);
// Create an another view that will be use for the selection
auto static_camera = vsg::Camera::create(
vsg::Orthographic::create(0, 1, 0, 1, -10,10), // left, right, bottom, top, 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 view_overlay = vsg::View::create(static_camera);
view_overlay->addChild(vsg::createHeadlight());
vsg::ref_ptr<vsg::Node> quatNode;
// TBD - make this into a function for drawing a rectangle in the overlay
view_overlay->addChild(draw_rectangle(window,
{vsg::vec2(400, 300),
vsg::vec2(220, 80)}));
renderGraph->addChild(view_overlay);
commandGraph->addChild(renderGraph);
viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph});
viewer->compile();
auto startTime = vsg::clock::now();
double numFramesCompleted = 0.0;
// rendering main loop
bool mouseDown = false;
vsg::vec2 mouseDownPos;
while (viewer->advanceToNextFrame())
{
// pass any events into EventHandlers assigned to the Viewer
viewer->handleEvents();
// update the mouse drawn rectangle
if (mouseHandler->isPressed && !mouseDown)
{
mouseDown = true;
mouseDownPos = vsg::vec2(mouseHandler->lastPointerEvent->x, mouseHandler->lastPointerEvent->y);
}
else if (!mouseHandler->isPressed && mouseDown)
mouseDown = false;
if (mouseDown)
{
auto mousePos = vsg::vec2(mouseHandler->lastPointerEvent->x, mouseHandler->lastPointerEvent->y);
auto rect = Rectangle {mouseDownPos, mousePos - mouseDownPos};
rect.pos.y = window->extent2D().height - rect.pos.y;
rect.size.y = -rect.size.y;
if (rect.size.x < 0)
{
rect.pos.x += rect.size.x;
rect.size.x = -rect.size.x;
}
if (rect.size.y < 0)
{
rect.pos.y += rect.size.y;
rect.size.y = -rect.size.y;
}
view_overlay->children.clear();
view_overlay->addChild(draw_rectangle(window, rect));
viewer->compile();
}
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