Created
October 11, 2024 08:11
-
-
Save dov/90763c1138e0db214430e1d4c2325cff to your computer and use it in GitHub Desktop.
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
//====================================================================== | |
// 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