From f45a301d13b24e142ace964e587482ddabd361de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 24 Jun 2015 00:05:51 +0200 Subject: [PATCH] New Object Picking example. --- CMakeLists.txt | 1 + doc/building-examples.dox | 1 + package/archlinux/PKGBUILD | 1 + .../archlinux/magnum-examples-git/PKGBUILD | 1 + package/ci/jenkins-mingw-w64.xml | 1 + package/ci/jenkins.xml | 1 + package/debian/rules | 1 + .../magnum-examples-9999.ebuild | 1 + src/CMakeLists.txt | 4 + src/picking/CMakeLists.txt | 51 +++ src/picking/PhongId.frag | 58 ++++ src/picking/PhongId.vert | 54 +++ src/picking/PickingExample.cpp | 319 ++++++++++++++++++ src/picking/README.md | 16 + src/picking/picking.png | Bin 0 -> 44656 bytes src/picking/resources.conf | 7 + 16 files changed, 517 insertions(+) create mode 100644 src/picking/CMakeLists.txt create mode 100644 src/picking/PhongId.frag create mode 100644 src/picking/PhongId.vert create mode 100644 src/picking/PickingExample.cpp create mode 100644 src/picking/README.md create mode 100644 src/picking/picking.png create mode 100644 src/picking/resources.conf diff --git a/CMakeLists.txt b/CMakeLists.txt index 706993075..e051ef125 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ cmake_dependent_option(WITH_CUBEMAP "Build CubeMap example (requires JpegImporte cmake_dependent_option(WITH_FRAMEBUFFER "Build Framebuffer example (requires TgaImporter plugin)" OFF "NOT MAGNUM_TARGET_GLES" OFF) cmake_dependent_option(WITH_MOTIONBLUR "Build MotionBlur example" OFF "NOT MAGNUM_TARGET_GLES" OFF) option(WITH_OVR "Build OVR example" OFF) +option(WITH_PICKING "Build Picking example" OFF) option(WITH_PRIMITIVES "Build Primitives example" OFF) option(WITH_TEXT "Build Text example (requires FreeTypeFont plugin)" OFF) cmake_dependent_option(WITH_TEXTUREDTRIANGLE "Build TexturedTriangle example (requires TgaImporter plugin)" OFF "NOT MAGNUM_TARGET_GLES" OFF) diff --git a/doc/building-examples.dox b/doc/building-examples.dox index 50fddb07d..91b0276fd 100644 --- a/doc/building-examples.dox +++ b/doc/building-examples.dox @@ -50,6 +50,7 @@ for more information. @ref Trade::TgaImporter "TgaImporter" plugin, not available in OpenGL ES. - `WITH_MOTIONBLUR` -- Build Motion Blur example. Not available on OpenGL ES. +- `WITH_PICKING` -- Build Object Picking example. - `WITH_PRIMITIVES` -- Build @ref examples-primitives "Primitives" example. - `WITH_TEXT` -- Build Text example. Requires @ref Text::FreeTypeFont "FreeTypeFont" plugin. diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 64d0b564f..e0d12b07d 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -25,6 +25,7 @@ build() { -DWITH_FRAMEBUFFER=ON \ -DWITH_MOTIONBLUR=ON \ -DWITH_PRIMITIVES=ON \ + -DWITH_PICKING=ON \ -DWITH_TEXT=ON \ -DWITH_TEXTUREDTRIANGLE=ON \ -DWITH_TRIANGLE=ON \ diff --git a/package/archlinux/magnum-examples-git/PKGBUILD b/package/archlinux/magnum-examples-git/PKGBUILD index 5ace60f7c..618fc06fc 100644 --- a/package/archlinux/magnum-examples-git/PKGBUILD +++ b/package/archlinux/magnum-examples-git/PKGBUILD @@ -30,6 +30,7 @@ build() { -DWITH_FRAMEBUFFER=ON \ -DWITH_MOTIONBLUR=ON \ -DWITH_PRIMITIVES=ON \ + -DWITH_PICKING=ON \ -DWITH_TEXT=ON \ -DWITH_TEXTUREDTRIANGLE=ON \ -DWITH_TRIANGLE=ON \ diff --git a/package/ci/jenkins-mingw-w64.xml b/package/ci/jenkins-mingw-w64.xml index 834e91474..65f6fca20 100644 --- a/package/ci/jenkins-mingw-w64.xml +++ b/package/ci/jenkins-mingw-w64.xml @@ -85,6 +85,7 @@ cmake .. \ -DWITH_CUBEMAP=ON \ -DWITH_FRAMEBUFFER=ON \ -DWITH_MOTIONBLUR=ON \ + -DWITH_PICKING=ON \ -DWITH_PRIMITIVES=ON \ -DWITH_TEXT=ON \ -DWITH_TEXTUREDTRIANGLE=ON \ diff --git a/package/ci/jenkins.xml b/package/ci/jenkins.xml index 0c6434259..013697682 100644 --- a/package/ci/jenkins.xml +++ b/package/ci/jenkins.xml @@ -109,6 +109,7 @@ cmake .. \ -DWITH_CUBEMAP=${desktop_flag} \ -DWITH_FRAMEBUFFER=${desktop_flag} \ -DWITH_MOTIONBLUR=${desktop_flag} \ + -DWITH_PICKING=ON \ -DWITH_PRIMITIVES=ON \ -DWITH_TEXT=${desktop_flag} \ -DWITH_TEXTUREDTRIANGLE=${desktop_flag} \ diff --git a/package/debian/rules b/package/debian/rules index 48795328c..2cbc29c18 100755 --- a/package/debian/rules +++ b/package/debian/rules @@ -9,6 +9,7 @@ override_dh_auto_configure: -DWITH_CUBEMAP=ON \ -DWITH_FRAMEBUFFER=ON \ -DWITH_MOTIONBLUR=ON \ + -DWITH_PICKING=ON \ -DWITH_PRIMITIVES=ON \ -DWITH_TEXT=ON \ -DWITH_TEXTUREDTRIANGLE=ON \ diff --git a/package/gentoo/dev-libs/magnum-examples/magnum-examples-9999.ebuild b/package/gentoo/dev-libs/magnum-examples/magnum-examples-9999.ebuild index d875915db..17aabf69e 100644 --- a/package/gentoo/dev-libs/magnum-examples/magnum-examples-9999.ebuild +++ b/package/gentoo/dev-libs/magnum-examples/magnum-examples-9999.ebuild @@ -33,6 +33,7 @@ src_configure() { -DWITH_CUBEMAP=ON -DWITH_FRAMEBUFFER=ON -DWITH_MOTIONBLUR=ON + -DWITH_PICKING=ON -DWITH_PRIMITIVES=ON -DWITH_TEXT=ON -DWITH_TEXTUREDTRIANGLE=ON diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 84877c3d4..c070d1d48 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,10 @@ if(WITH_OVR) add_subdirectory(ovr) endif() +if(WITH_PICKING) + add_subdirectory(picking) +endif() + if(WITH_PRIMITIVES) add_subdirectory(primitives) endif() diff --git a/src/picking/CMakeLists.txt b/src/picking/CMakeLists.txt new file mode 100644 index 000000000..295ce615d --- /dev/null +++ b/src/picking/CMakeLists.txt @@ -0,0 +1,51 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015 +# Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# 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 OR COPYRIGHT HOLDERS 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.9) +project(MagnumPickingExample) + +find_package(Magnum REQUIRED + MeshTools + Primitives + SceneGraph + Sdl2Application) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CORRADE_CXX_FLAGS}") +include_directories(${MAGNUM_INCLUDE_DIRS} ${MAGNUM_APPLICATION_INCLUDE_DIRS}) + +corrade_add_resource(PickingData resources.conf) + +add_executable(magnum-picking + PickingExample.cpp + ${PickingData}) +target_link_libraries(magnum-picking + ${MAGNUM_LIBRARIES} + ${MAGNUM_MESHTOOLS_LIBRARIES} + ${MAGNUM_PRIMITIVES_LIBRARIES} + ${MAGNUM_SCENEGRAPH_LIBRARIES} + ${MAGNUM_APPLICATION_LIBRARIES}) + +install(TARGETS magnum-picking DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}) +install(FILES README.md DESTINATION ${MAGNUM_DATA_INSTALL_DIR}/examples RENAME README-picking.md) diff --git a/src/picking/PhongId.frag b/src/picking/PhongId.frag new file mode 100644 index 000000000..ae5ddc315 --- /dev/null +++ b/src/picking/PhongId.frag @@ -0,0 +1,58 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +uniform lowp vec3 ambientColor; +uniform lowp vec3 color; +uniform lowp int objectId; + +in mediump vec3 transformedNormal; +in highp vec3 lightDirection; +in highp vec3 cameraDirection; + +layout(location = 0) out lowp vec4 fragmentColor; +layout(location = 1) out lowp int fragmentObjectId; + +void main() { + mediump vec3 normalizedTransformedNormal = normalize(transformedNormal); + highp vec3 normalizedLightDirection = normalize(lightDirection); + + /* Add ambient color */ + fragmentColor.rgb = ambientColor; + + /* Add diffuse color */ + lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection)); + fragmentColor.rgb += color*intensity; + + /* Add specular color, if needed */ + if(intensity > 0.001) { + highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); + mediump float specularity = pow(max(0.0, dot(normalize(cameraDirection), reflection)), 80.0); + fragmentColor.rgb += vec3(1.0)*specularity; + } + + /* Force alpha to 1 */ + fragmentColor.a = 1.0; + fragmentObjectId = objectId; +} diff --git a/src/picking/PhongId.vert b/src/picking/PhongId.vert new file mode 100644 index 000000000..0ed4a805b --- /dev/null +++ b/src/picking/PhongId.vert @@ -0,0 +1,54 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +uniform highp mat4 transformationMatrix; +uniform highp mat4 projectionMatrix; +uniform mediump mat3 normalMatrix; +uniform highp vec3 light; + +layout(location = 0) in highp vec4 position; +layout(location = 1) in mediump vec3 normal; + +out mediump vec3 transformedNormal; +out highp vec3 lightDirection; +out highp vec3 cameraDirection; + +void main() { + /* Transformed vertex position */ + highp vec4 transformedPosition4 = transformationMatrix*position; + highp vec3 transformedPosition = transformedPosition4.xyz/transformedPosition4.w; + + /* Transformed normal vector */ + transformedNormal = normalMatrix*normal; + + /* Direction to the light */ + lightDirection = normalize(light - transformedPosition); + + /* Direction to the camera */ + cameraDirection = -transformedPosition; + + /* Transform the position */ + gl_Position = projectionMatrix*transformedPosition4; +} diff --git a/src/picking/PickingExample.cpp b/src/picking/PickingExample.cpp new file mode 100644 index 000000000..46e05eb81 --- /dev/null +++ b/src/picking/PickingExample.cpp @@ -0,0 +1,319 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Magnum; + +typedef SceneGraph::Object Object3D; +typedef SceneGraph::Scene Scene3D; + +class PhongIdShader: public AbstractShaderProgram { + public: + typedef Attribute<0, Vector3> Position; + typedef Attribute<1, Vector3> Normal; + + enum: UnsignedInt { + ColorOutput = 0, + ObjectIdOutput = 1 + }; + + explicit PhongIdShader(); + + PhongIdShader& setObjectId(Int id) { + setUniform(_objectIdUniform, id); + return *this; + } + + PhongIdShader& setLightPosition(const Vector3& position) { + setUniform(_lightPositionUniform, position); + return *this; + } + + PhongIdShader& setAmbientColor(const Color3& color) { + setUniform(_ambientColorUniform, color); + return *this; + } + + PhongIdShader& setColor(const Color3& color) { + setUniform(_colorUniform, color); + return *this; + } + + PhongIdShader& setTransformationMatrix(const Matrix4& matrix) { + setUniform(_transformationMatrixUniform, matrix); + return *this; + } + + PhongIdShader& setNormalMatrix(const Matrix3x3& matrix) { + setUniform(_normalMatrixUniform, matrix); + return *this; + } + + PhongIdShader& setProjectionMatrix(const Matrix4& matrix) { + setUniform(_projectionMatrixUniform, matrix); + return *this; + } + + private: + Int _objectIdUniform, + _lightPositionUniform, + _ambientColorUniform, + _colorUniform, + _transformationMatrixUniform, + _normalMatrixUniform, + _projectionMatrixUniform; +}; + +PhongIdShader::PhongIdShader() { + Utility::Resource rs("picking-data"); + + Shader vert{Version::GL330, Shader::Type::Vertex}, + frag{Version::GL330, Shader::Type::Fragment}; + vert.addSource(rs.get("PhongId.vert")); + frag.addSource(rs.get("PhongId.frag")); + CORRADE_INTERNAL_ASSERT(Shader::compile({vert, frag})); + attachShaders({vert, frag}); + CORRADE_INTERNAL_ASSERT(link()); + + _objectIdUniform = uniformLocation("objectId"); + _lightPositionUniform = uniformLocation("light"); + _ambientColorUniform = uniformLocation("ambientColor"); + _colorUniform = uniformLocation("color"); + _transformationMatrixUniform = uniformLocation("transformationMatrix"); + _projectionMatrixUniform = uniformLocation("projectionMatrix"); + _normalMatrixUniform = uniformLocation("normalMatrix"); +} + +class PickableObject: public Object3D, SceneGraph::Drawable3D { + public: + explicit PickableObject(UnsignedByte id, PhongIdShader& shader, const Color3& color, Mesh& mesh, Object3D& parent, SceneGraph::DrawableGroup3D& drawables): Object3D{&parent}, SceneGraph::Drawable3D{*this, &drawables}, _id{id}, _selected{false}, _shader{shader}, _color{color}, _mesh{mesh} {} + + void setSelected(bool selected) { _selected = selected; } + + private: + virtual void draw(const Matrix4& transformationMatrix, SceneGraph::Camera3D& camera) { + _shader.setTransformationMatrix(transformationMatrix) + .setNormalMatrix(transformationMatrix.rotationScaling()) + .setProjectionMatrix(camera.projectionMatrix()) + .setAmbientColor(_selected ? _color*0.3f : Color3{}) + .setColor(_color*(_selected ? 2.0f : 1.0f)) + /* relative to the camera */ + .setLightPosition({13.0f, 2.0f, 5.0f}) + .setObjectId(_id); + _mesh.draw(_shader); + } + + UnsignedByte _id; + bool _selected; + PhongIdShader& _shader; + Color3 _color; + Mesh& _mesh; +}; + +class PickingExample: public Platform::Application { + public: + explicit PickingExample(const Arguments& arguments); + + private: + void drawEvent() override; + void mousePressEvent(MouseEvent& event) override; + void mouseMoveEvent(MouseMoveEvent& event) override; + void mouseReleaseEvent(MouseEvent& event) override; + + Scene3D _scene; + Object3D* _cameraObject; + SceneGraph::Camera3D* _camera; + SceneGraph::DrawableGroup3D _drawables; + + PhongIdShader _shader; + Buffer _cubeVertices, _cubeIndices, + _sphereVertices, _sphereIndices, + _planeVertices; + Mesh _cube, _plane, _sphere; + + enum { ObjectCount = 6 }; + PickableObject* _objects[ObjectCount]; + + Framebuffer _framebuffer; + Renderbuffer _color, _objectId, _depth; + + Vector2i _previousMousePosition, _mousePressPosition; +}; + +PickingExample::PickingExample(const Arguments& arguments): Platform::Application{arguments, Configuration{}.setTitle("Magnum object picking example")}, _framebuffer{defaultFramebuffer.viewport()} { + MAGNUM_ASSERT_VERSION_SUPPORTED(Version::GL330); + + /* Global renderer configuration */ + Renderer::enable(Renderer::Feature::DepthTest); + + /* Configure framebuffer (using R8UI for object ID which means 255 objects max) */ + _color.setStorage(RenderbufferFormat::RGBA8, defaultFramebuffer.viewport().size()); + _objectId.setStorage(RenderbufferFormat::R8UI, defaultFramebuffer.viewport().size()); + _depth.setStorage(RenderbufferFormat::DepthComponent24, defaultFramebuffer.viewport().size()); + _framebuffer.attachRenderbuffer(Framebuffer::ColorAttachment{0}, _color) + .attachRenderbuffer(Framebuffer::ColorAttachment{1}, _objectId) + .attachRenderbuffer(Framebuffer::BufferAttachment::Depth, _depth) + .mapForDraw({{PhongIdShader::ColorOutput, Framebuffer::ColorAttachment{0}}, + {PhongIdShader::ObjectIdOutput, Framebuffer::ColorAttachment{1}}}); + CORRADE_INTERNAL_ASSERT(_framebuffer.checkStatus(FramebufferTarget::Draw) == Framebuffer::Status::Complete); + + /* Set up meshes */ + { + Trade::MeshData3D data = Primitives::Cube::solid(); + _cubeVertices.setData(MeshTools::interleave(data.positions(0), data.normals(0)), BufferUsage::StaticDraw); + _cubeIndices.setData(MeshTools::compressIndicesAs(data.indices()), BufferUsage::StaticDraw); + _cube.setCount(data.indices().size()) + .setPrimitive(data.primitive()) + .addVertexBuffer(_cubeVertices, 0, PhongIdShader::Position{}, PhongIdShader::Normal{}) + .setIndexBuffer(_cubeIndices, 0, Mesh::IndexType::UnsignedShort); + } { + Trade::MeshData3D data = Primitives::UVSphere::solid(16, 32); + _sphereVertices.setData(MeshTools::interleave(data.positions(0), data.normals(0)), BufferUsage::StaticDraw); + _sphereIndices.setData(MeshTools::compressIndicesAs(data.indices()), BufferUsage::StaticDraw); + _sphere.setCount(data.indices().size()) + .setPrimitive(data.primitive()) + .addVertexBuffer(_sphereVertices, 0, PhongIdShader::Position{}, PhongIdShader::Normal{}) + .setIndexBuffer(_sphereIndices, 0, Mesh::IndexType::UnsignedShort); + } { + Trade::MeshData3D data = Primitives::Plane::solid(); + _planeVertices.setData(MeshTools::interleave(data.positions(0), data.normals(0)), BufferUsage::StaticDraw); + _plane.setCount(data.positions(0).size()) + .setPrimitive(data.primitive()) + .addVertexBuffer(_planeVertices, 0, PhongIdShader::Position{}, PhongIdShader::Normal{}); + } + + /* Set up objects */ + (*(_objects[0] = new PickableObject{1, _shader, Color3::fromHSV(25.0_degf, 0.9f, 0.9f), _cube, _scene, _drawables})) + .rotate(34.0_degf, Vector3(1.0f).normalized()) + .translate({1.0f, 0.3f, -1.2f}); + (*(_objects[1] = new PickableObject{2, _shader, Color3::fromHSV(54.0_degf, 0.9f, 0.9f), _sphere, _scene, _drawables})) + .translate({-1.2f, -0.3f, -0.2f}); + (*(_objects[2] = new PickableObject{3, _shader, Color3::fromHSV(105.0_degf, 0.9f, 0.9f), _plane, _scene, _drawables})) + .rotate(254.0_degf, Vector3(1.0f).normalized()) + .scale(Vector3(0.45f)) + .translate({0.5f, 1.3f, 1.5f}); + (*(_objects[3] = new PickableObject{4, _shader, Color3::fromHSV(162.0_degf, 0.9f, 0.9f), _sphere, _scene, _drawables})) + .translate({-0.2f, -1.7f, -2.7f}); + (*(_objects[4] = new PickableObject{5, _shader, Color3::fromHSV(210.0_degf, 0.9f, 0.9f), _sphere, _scene, _drawables})) + .translate({0.7f, 0.6f, 2.2f}) + .scale(Vector3(0.75f)); + (*(_objects[5] = new PickableObject{6, _shader, Color3::fromHSV(280.0_degf, 0.9f, 0.9f), _cube, _scene, _drawables})) + .rotate(-92.0_degf, Vector3(1.0f).normalized()) + .scale(Vector3(0.25f)) + .translate({-0.5f, -0.3f, 1.8f}); + + /* Configure camera */ + _cameraObject = new Object3D{&_scene}; + _cameraObject->translate(Vector3::zAxis(8.0f)); + _camera = new SceneGraph::Camera3D{*_cameraObject}; + _camera->setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend) + .setProjectionMatrix(Matrix4::perspectiveProjection(35.0_degf, 4.0f/3.0f, 0.001f, 100.0f)) + .setViewport(defaultFramebuffer.viewport().size()); +} + +void PickingExample::drawEvent() { + /* Draw to custom framebuffer */ + _framebuffer.clear(FramebufferClear::Color|FramebufferClear::Depth) + .bind(); + _camera->draw(_drawables); + + /* Blit color to window framebuffer */ + _framebuffer.mapForRead(Framebuffer::ColorAttachment{0}); + AbstractFramebuffer::blit(_framebuffer, defaultFramebuffer, + {{}, _framebuffer.viewport().size()}, FramebufferBlit::Color); + + swapBuffers(); +} + +void PickingExample::mousePressEvent(MouseEvent& event) { + if(event.button() != MouseEvent::Button::Left) return; + + _previousMousePosition = _mousePressPosition = event.position(); + event.setAccepted(); +} + +void PickingExample::mouseMoveEvent(MouseMoveEvent& event) { + if(!(event.buttons() & MouseMoveEvent::Button::Left)) return; + + const Vector2 delta = 3.0f* + Vector2{event.position() - _previousMousePosition}/ + Vector2{defaultFramebuffer.viewport().size()}; + + (*_cameraObject) + .rotate(Rad{-delta.y()}, _cameraObject->transformation().right().normalized()) + .rotateY(Rad{-delta.x()}); + + _previousMousePosition = event.position(); + event.setAccepted(); + redraw(); +} + +void PickingExample::mouseReleaseEvent(MouseEvent& event) { + if(event.button() != MouseEvent::Button::Left || _mousePressPosition != event.position()) return; + + /* Read object ID at given click position (framebuffer has Y up while windowing system Y down) */ + _framebuffer.mapForRead(Framebuffer::ColorAttachment{1}); + Image2D data = _framebuffer.read( + Range2Di::fromSize({event.position().x(), _framebuffer.viewport().sizeY() - event.position().y() - 1}, {1, 1}), + {ColorFormat::RedInteger, ColorType::UnsignedByte}); + + /* Highlight object under mouse and deselect all other */ + for(auto* o: _objects) o->setSelected(false); + UnsignedByte id = data.data()[0]; + if(id > 0 && id < ObjectCount + 1) + _objects[id - 1]->setSelected(true); + + event.setAccepted(); + redraw(); +} + +MAGNUM_APPLICATION_MAIN(PickingExample) diff --git a/src/picking/README.md b/src/picking/README.md new file mode 100644 index 000000000..626f188ff --- /dev/null +++ b/src/picking/README.md @@ -0,0 +1,16 @@ +This example demonstrates usage multiple framebuffer attachment to implement +object picking. One attachment is used for color output, the other contains +object IDs. + +![Object picking](picking.png) + +Key shortcuts +------------- + +Use **mouse drag** to rotate the scene, **mouse click** to highlight particular +object. + +Platform requirements +--------------------- + +* OpenGL 3.3+ diff --git a/src/picking/picking.png b/src/picking/picking.png new file mode 100644 index 0000000000000000000000000000000000000000..84d99622d41b790dd34cd8c880968701e8cca0ae GIT binary patch literal 44656 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 vC<1xj&lFO3X!_l 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 Bux