From a35d51ee9ea90b0fb335f3a9c681f99d165b769d Mon Sep 17 00:00:00 2001 From: Chao Shen Date: Wed, 25 Jul 2018 14:46:24 +0800 Subject: [PATCH] Add a mouse interaction example. --- CMakeLists.txt | 1 + src/CMakeLists.txt | 4 + src/interaction/CMakeLists.txt | 76 +++++++++ src/interaction/InteractionExample.cpp | 228 +++++++++++++++++++++++++ 4 files changed, 309 insertions(+) create mode 100644 src/interaction/CMakeLists.txt create mode 100644 src/interaction/InteractionExample.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f59300081..2b0641a54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,5 +77,6 @@ option(WITH_VIEWER_EXAMPLE "Build Viewer example (requires ColladaImporter plugi if(CORRADE_TARGET_EMSCRIPTEN) option(WITH_WEBVR_EXAMPLE "Build WebVR example" OFF) endif() +option(WITH_INTERACTION_EXAMPLE "Build mouse interaction example" OFF) add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 627247df6..191936628 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -105,3 +105,7 @@ endif() if(WITH_WEBVR_EXAMPLE) add_subdirectory(webvr) endif() + +if(WITH_INTERACTION_EXAMPLE) + add_subdirectory(interaction) +endif() diff --git a/src/interaction/CMakeLists.txt b/src/interaction/CMakeLists.txt new file mode 100644 index 000000000..68fee183f --- /dev/null +++ b/src/interaction/CMakeLists.txt @@ -0,0 +1,76 @@ +# +# This file is part of Magnum. +# +# Original authors — credit is appreciated but not required: +# +# 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 — +# Vladimír Vondruš +# 2018 — scturtle +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or distribute +# this software, either in source code form or as a compiled binary, for any +# purpose, commercial or non-commercial, and by any means. +# +# In jurisdictions that recognize copyright laws, the author or authors of +# this software dedicate any and all copyright interest in the software to +# the public domain. We make this dedication for the benefit of the public +# at large and to the detriment of our heirs and successors. We intend this +# dedication to be an overt act of relinquishment in perpetuity of all +# present and future rights to this software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +cmake_minimum_required(VERSION 2.8.12) + +# CMake policies. 0025 needs to be before project(), so putting all there. +# Use AppleClang instead of Clang on Apple +if(POLICY CMP0025) + cmake_policy(SET CMP0025 NEW) +endif() +# Don't treat imported targets with :: as files +if(POLICY CMP0028) + cmake_policy(SET CMP0028 NEW) +endif() +# Enable MACOSX_RPATH by default +if(POLICY CMP0042) + cmake_policy(SET CMP0042 NEW) +endif() +# Quoted variables should not be dereferenced +if(POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) +endif() + +project(MagnumInteractionExample) + +# Add module path in case this is project root +if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/../../modules/") +endif() + +find_package(Magnum REQUIRED + GL + MeshTools + SceneGraph + Shaders + Sdl2Application) + +set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) + +add_executable(magnum-interaction InteractionExample.cpp) +target_link_libraries(magnum-interaction PRIVATE + Magnum::Application + Magnum::GL + Magnum::Magnum + Magnum::MeshTools + Magnum::SceneGraph + Magnum::Shaders) + +install(TARGETS magnum-interaction DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}) diff --git a/src/interaction/InteractionExample.cpp b/src/interaction/InteractionExample.cpp new file mode 100644 index 000000000..6e53b333c --- /dev/null +++ b/src/interaction/InteractionExample.cpp @@ -0,0 +1,228 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 — + Vladimír Vondruš + 2018 — scturtle + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Magnum { +namespace Examples { + +using Object3D = + Magnum::SceneGraph::Object; +using Scene3D = + Magnum::SceneGraph::Scene; + +class InteractiveApplication : public Magnum::Platform::Sdl2Application { + public: + explicit InteractiveApplication(const Arguments &arguments, + Magnum::NoCreateT) + : Magnum::Platform::Sdl2Application{arguments, Magnum::NoCreate} {} + explicit InteractiveApplication(const Arguments &arguments, + const Configuration &conf) + : Magnum::Platform::Sdl2Application{arguments, conf} {} + + protected: + float depth_at(int x, int y); + Magnum::Vector3 unproject(float x, float y, float z, + bool cam_coords = false); + void mousePressEvent(MouseEvent &event) override; + void mouseMoveEvent(MouseMoveEvent &event) override; + void mouseScrollEvent(MouseScrollEvent &event) override; + + Object3D *_camera_object; + Magnum::SceneGraph::Camera3D *_camera; + float last_z = 0.8; + Magnum::Vector3 rp, tp; +}; + +void InteractiveApplication::mousePressEvent(MouseEvent &event) { + int x = event.position().x(); + int y = event.position().y(); + float orig_z = depth_at(x, y); + float z = orig_z == 1.f ? last_z : orig_z; + tp = unproject(x, y, z, /*cam_coords=*/true); + if (orig_z != 1.f) + rp = tp, last_z = z; +} + +void InteractiveApplication::mouseMoveEvent(MouseMoveEvent &event) { + Vector2i pos = event.position(); + int x = pos.x(), y = pos.y(); + static Vector2i last_pos = pos; + Vector2i delta = pos - last_pos; + last_pos = pos; + if (event.buttons() & MouseMoveEvent::Button::Left) { + Vector3 p = unproject(x, y, last_z, true); + _camera_object->translateLocal({tp[0] - p[0], tp[1] - p[1], 0}); + tp = p; + redraw(); + } else if (event.buttons() & MouseMoveEvent::Button::Right) { + float rx = -0.01f * delta.y(), ry = -0.01f * delta.x(); + Matrix4 t = Matrix4::translation(rp) * Matrix4::rotationX(Rad(rx)) * + Matrix4::rotationY(Rad(ry)) * Matrix4::translation(-rp); + _camera_object->transformLocal(t); + redraw(); + } +} + +void InteractiveApplication::mouseScrollEvent(MouseScrollEvent &event) { + int x, y; + SDL_GetMouseState(&x, &y); + float orig_z = depth_at(x, y); + float z = orig_z == 1.f ? last_z : orig_z; + Vector3 p = unproject(x, y, z, /*cam_coords=*/true); + if (orig_z != 1.f) + rp = p, last_z = z; + int dir = event.offset().y(); + if (!dir) + return; + Vector3 t = rp * (dir < 0 ? -1 : 1) * 0.1f; + // move towards/backwards that point in cam coords + _camera_object->translateLocal(t); + redraw(); +} + +float InteractiveApplication::depth_at(int x, int y) { + Range2Di view = GL::defaultFramebuffer.viewport(); + y = view.sizeY() - y; + GL::defaultFramebuffer.mapForRead( + GL::DefaultFramebuffer::ReadAttachment::Front); + constexpr int w = 2; + Image2D data = GL::defaultFramebuffer.read( + Range2Di::fromSize({x - w, y - w}, {2 * w + 1, 2 * w + 1}), + {GL::PixelFormat::DepthComponent, GL::PixelType::Float}); + return *std::min_element(data.data(), + data.data() + data.pixelSize()); +} + +Vector3 InteractiveApplication::unproject(float x, float y, float z, + bool cam_coords) { + Range2Di view = GL::defaultFramebuffer.viewport(); + y = view.sizeY() - y; + Vector4 in = {(x - view.min().x()) / view.sizeX() * 2 - 1, + (y - view.min().y()) / view.sizeY() * 2 - 1, z * 2 - 1, 1}; + Vector4 out = _camera->projectionMatrix().inverted() * in; + if (!cam_coords) + out = _camera->cameraMatrix().invertedRigid() * out; + out /= out[3]; + return out.xyz(); +} + +template +class Drawable : public Object3D, SceneGraph::Drawable3D { + public: + Drawable(Shader &&shader, Object3D &parent, + SceneGraph::DrawableGroup3D &drawables) + : Object3D{&parent}, SceneGraph::Drawable3D{*this, &drawables}, + shader(std::move(shader)) {} + + void draw(const Magnum::Matrix4 &transformation, + SceneGraph::Camera3D &camera) { + shader.setTransformationProjectionMatrix(camera.projectionMatrix() * + transformation); + mesh.draw(shader); + } + + Shader shader; + GL::Mesh mesh; + GL::Buffer buffer; +}; + +class InteractionExample : public InteractiveApplication { + public: + explicit InteractionExample(const Arguments &arguments); + + private: + void drawEvent() override; + + Scene3D _scene; + SceneGraph::DrawableGroup3D _drawables; +}; + +InteractionExample::InteractionExample(const Arguments &arguments) + : InteractiveApplication{arguments, NoCreate} { + using namespace Math::Literals; + + create(Configuration{}.setTitle("interaction"), + GLConfiguration{}.setSampleCount(4)); + + GL::Renderer::enable(GL::Renderer::Feature::DepthTest); + GL::Renderer::setClearColor(0x222222_rgbf); + + struct { + Vector3 pos; + Color3 color; + } data[4] = {{{-1.0f, -1.0f, 0.0f}, 0xff0000_rgbf}, + {{1.0f, -1.0f, 0.0f}, 0x00ff00_rgbf}, + {{0.0f, 1.0f, 0.0f}, 0x0000ff_rgbf}}; + + auto tri = new Drawable(Shaders::VertexColor3D{}, + _scene, _drawables); + tri->buffer.setData(data); + tri->mesh.setPrimitive(GL::MeshPrimitive::TriangleStrip) + .setCount(3) + .addVertexBuffer(tri->buffer, 0, Shaders::VertexColor3D::Position{}, + Shaders::VertexColor3D::Color3{}); + + Vector2i view_size = GL::defaultFramebuffer.viewport().size(); + _camera_object = new Object3D{&_scene}; + _camera_object->setTransformation( + Matrix4::lookAt({0, 0, 5}, {0, 0, 0}, {0, 1, 0})); + _camera = new SceneGraph::Camera3D{*_camera_object}; + _camera->setProjectionMatrix(Matrix4::perspectiveProjection( + 45.0_degf, Vector2{view_size}.aspectRatio(), 0.01f, 100.0f)); +} + +void InteractionExample::drawEvent() { + GL::defaultFramebuffer.clear(GL::FramebufferClear::Color | + GL::FramebufferClear::Depth); + _camera->draw(_drawables); + swapBuffers(); +} + +} // namespace Examples +} // namespace Magnum + +MAGNUM_APPLICATION_MAIN(Magnum::Examples::InteractionExample)