diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 51fa7aa05e..8f119dd707 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -1042,6 +1042,16 @@ void setSgctDelegateFunctions() { sgct::SGCTWindow* w = sgct::Engine::instance()->getWindowPtr(0); w->setHorizFieldOfView(hFovDeg); }; + #ifdef WIN32 + sgctDelegate.getNativeWindowHandle = [](size_t windowIndex) -> void* { + sgct::SGCTWindow* w = sgct::Engine::instance()->getWindowPtr(windowIndex); + if(w) { + HWND hWnd = glfwGetWin32Window(w->getWindowHandle()); + return reinterpret_cast(hWnd); + } + return nullptr; + }; + #endif // WIN32 sgctDelegate.frustumMode = []() { using FM = sgct_core::Frustum::FrustumMode; switch (sgct::Engine::instance()->getCurrentFrustumMode()) { diff --git a/data/assets/scene/milkyway/milkyway/volume.asset b/data/assets/scene/milkyway/milkyway/volume.asset index 0d1537d45e..f4ad4a9385 100644 --- a/data/assets/scene/milkyway/milkyway/volume.asset +++ b/data/assets/scene/milkyway/milkyway/volume.asset @@ -10,15 +10,25 @@ local data = asset.syncedResource({ Version = 1 }) +local kiloparsec = 3.086E19 + local MilkyWayVolumeGalaxy = { - Identifier = "Milky Way Volume", + Identifier = "MilkyWayVolume", Parent = transforms.SolarSystemBarycenter.Identifier, + Transform = { + Translation = { + Type = "StaticTranslation", + -- The center of the Milky Way is approximately 8 kiloparsec from the Sun. + -- The x-axis of galactic coordinates points from the sun towards the center + -- of the galaxy. + Position = {8 * kiloparsec, 0, 0} + } + }, Renderable = { Type = "RenderableGalaxy", StepSize = 0.01, AbsorptionMultiply = 200, EmissionMultiply = 250, - Translation = {0.2, 0, 0}, Rotation = {3.1415926, 3.1248, 4.45741}, Volume = { Type = "Volume", @@ -34,9 +44,10 @@ local MilkyWayVolumeGalaxy = { } }, GUI = { - Path = "/Milky Way" + Path = "/Milky Way", + Name = "Milky Way Volume" } } local objects = { MilkyWayVolumeGalaxy } -assetHelper.registerSceneGraphNodesAndExport(asset, objects) \ No newline at end of file +assetHelper.registerSceneGraphNodesAndExport(asset, objects) diff --git a/include/openspace/engine/windowdelegate.h b/include/openspace/engine/windowdelegate.h index 324c838b86..70f6f21d3d 100644 --- a/include/openspace/engine/windowdelegate.h +++ b/include/openspace/engine/windowdelegate.h @@ -118,6 +118,10 @@ struct WindowDelegate { double (*getHorizFieldOfView)() = []() { return 0.0; }; void (*setHorizFieldOfView)(float hFovDeg) = [](float) { }; + + void* (*getNativeWindowHandle)(size_t windowIndex) = [](size_t) -> void* { + return nullptr; + }; using GLProcAddress = void(*)(void); diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 2ce1e6846c..29379351a0 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -42,6 +42,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableboxgrid.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecartesianaxes.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablenodeline.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneimagelocal.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneimageonline.h @@ -89,6 +90,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableboxgrid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecartesianaxes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablenodeline.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneimagelocal.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplaneimageonline.cpp @@ -124,6 +126,8 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/grid_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/line_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/line_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/plane_fs.glsl diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index ba70bd6c2e..3535e48522 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +124,7 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) { fRenderable->registerClass("RenderableBoxGrid"); fRenderable->registerClass("RenderableCartesianAxes"); fRenderable->registerClass("RenderableModel"); + fRenderable->registerClass("RenderableNodeLine"); fRenderable->registerClass("RenderablePlaneImageLocal"); fRenderable->registerClass("RenderablePlaneImageOnline"); fRenderable->registerClass("RenderableSphere"); @@ -190,6 +192,7 @@ std::vector BaseModule::documentations() const { RenderableBoxGrid::Documentation(), RenderableModel::Documentation(), + RenderableNodeLine::Documentation(), RenderablePlane::Documentation(), RenderableSphere::Documentation(), RenderableTrailOrbit::Documentation(), diff --git a/modules/base/rendering/renderablenodeline.cpp b/modules/base/rendering/renderablenodeline.cpp new file mode 100644 index 0000000000..56bbdd53a0 --- /dev/null +++ b/modules/base/rendering/renderablenodeline.cpp @@ -0,0 +1,305 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * 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 + +namespace { + constexpr const char* ProgramName = "NodeLineProgram"; + constexpr const char* Root = "Root"; + + constexpr openspace::properties::Property::PropertyInfo StartNodeInfo = { + "StartNode", + "Start Node", + "The identifier of the node the line starts from. " + }; + + constexpr openspace::properties::Property::PropertyInfo EndNodeInfo = { + "EndNode", + "End Node", + "The identifier of the node the line ends at. " + }; + + constexpr openspace::properties::Property::PropertyInfo LineColorInfo = { + "Color", + "Color", + "This value determines the RGB color for the line." + }; + + constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = { + "LineWidth", + "Line Width", + "This value specifies the line width." + }; +} // namespace + +namespace openspace { + +documentation::Documentation RenderableNodeLine::Documentation() { + using namespace documentation; + return { + "Renderable Node Line", + "base_renderable_renderablenodeline", + { + { + StartNodeInfo.identifier, + new StringVerifier, + Optional::No, + StartNodeInfo.description + }, + { + EndNodeInfo.identifier, + new StringVerifier, + Optional::No, + EndNodeInfo.description + }, + { + LineColorInfo.identifier, + new DoubleVector3Verifier, + Optional::Yes, + LineColorInfo.description + }, + { + LineWidthInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LineWidthInfo.description + } + } + }; +} + +RenderableNodeLine::RenderableNodeLine(const ghoul::Dictionary& dictionary) + : Renderable(dictionary) + , _lineColor(LineColorInfo, glm::vec3(1.f, 1.f, 1.f), glm::vec3(0.f), glm::vec3(1.f)) + , _lineWidth(LineWidthInfo, 2.f, 1.f, 20.f) + , _start(StartNodeInfo, Root) + , _end(EndNodeInfo, Root) +{ + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "RenderableNodeLine" + ); + + _start = dictionary.value(StartNodeInfo.identifier); + _end = dictionary.value(EndNodeInfo.identifier); + + if (dictionary.hasKey(LineColorInfo.identifier)) { + _lineColor = dictionary.value(LineColorInfo.identifier); + } + if (dictionary.hasKey(LineWidthInfo.identifier)) { + _lineWidth = static_cast(dictionary.value(LineWidthInfo.identifier)); + } + + addProperty(_start); + addProperty(_end); + addProperty(_lineColor); + addProperty(_lineWidth); + addProperty(_opacity); +} + +void RenderableNodeLine::initializeGL() { + _program = BaseModule::ProgramObjectManager.request( + ProgramName, + []() -> std::unique_ptr { + return global::renderEngine.buildRenderProgram( + ProgramName, + absPath("${MODULE_BASE}/shaders/line_vs.glsl"), + absPath("${MODULE_BASE}/shaders/line_fs.glsl") + ); + } + ); + + // Generate + glGenVertexArrays(1, &_vaoId); + glGenBuffers(1, &_vBufferId); + + bindGL(); + + glVertexAttribPointer(_locVertex, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr); + glEnableVertexAttribArray(_locVertex); + + unbindGL(); +} + +void RenderableNodeLine::deinitializeGL() { + + glDeleteVertexArrays(1, &_vaoId); + _vaoId = 0; + + glDeleteBuffers(1, &_vBufferId); + _vBufferId = 0; + + BaseModule::ProgramObjectManager.release( + ProgramName, + [](ghoul::opengl::ProgramObject* p) { + global::renderEngine.removeRenderProgram(p); + } + ); + _program = nullptr; +} + +bool RenderableNodeLine::isReady() const { + bool ready = true; + ready &= (_program != nullptr); + return ready; +} + +void RenderableNodeLine::unbindGL() { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void RenderableNodeLine::bindGL() +{ + glBindVertexArray(_vaoId); + glBindBuffer(GL_ARRAY_BUFFER, _vBufferId); +} + +void RenderableNodeLine::updateVertexData() +{ + _vertexArray.clear(); + + // Update the positions of the nodes + _startPos = getCoordinatePosFromAnchorNode(global::renderEngine.scene()->sceneGraphNode(_start)->worldPosition()); + _endPos = getCoordinatePosFromAnchorNode(global::renderEngine.scene()->sceneGraphNode(_end)->worldPosition()); + + _vertexArray.push_back(_startPos.x); + _vertexArray.push_back(_startPos.y); + _vertexArray.push_back(_startPos.z); + + _vertexArray.push_back(_endPos.x); + _vertexArray.push_back(_endPos.y); + _vertexArray.push_back(_endPos.z); + + _vertexArray; + + bindGL(); + glBufferData( + GL_ARRAY_BUFFER, + _vertexArray.size() * sizeof(float), + _vertexArray.data(), + GL_DYNAMIC_DRAW + ); + + //update vertex attributes + glVertexAttribPointer(_locVertex, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr); + + unbindGL(); +} + +void RenderableNodeLine::render(const RenderData& data, RendererTasks&) { + + updateVertexData(); + + _program->activate(); + + glm::dmat4 anchorTranslation(1.0); + // Update anchor node information, used to counter precision problems + if (global::navigationHandler.orbitalNavigator().anchorNode()) { + anchorTranslation = glm::translate(glm::dmat4(1.0), global::navigationHandler.orbitalNavigator().anchorNode()->worldPosition()); + } + + const glm::dmat4 modelTransform = + glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * + glm::dmat4(data.modelTransform.rotation) * + glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)); + + const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * + modelTransform * anchorTranslation; + + _program->setUniform("modelViewTransform", glm::mat4(modelViewTransform)); + _program->setUniform("projectionTransform", data.camera.projectionMatrix()); + _program->setUniform("color", glm::vec4(_lineColor.value(), _opacity)); + + // Save current state: + GLboolean isBlendEnabled = glIsEnabledi(GL_BLEND, 0); + GLboolean isLineSmoothEnabled = glIsEnabled(GL_LINE_SMOOTH); + GLfloat currentLineWidth; + glGetFloatv(GL_LINE_WIDTH, ¤tLineWidth); + + GLenum blendEquationRGB, blendEquationAlpha, blendDestAlpha, + blendDestRGB, blendSrcAlpha, blendSrcRGB; + glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRGB); + glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha); + glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha); + glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRGB); + glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha); + glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRGB); + + // Change GL state: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnablei(GL_BLEND, 0); + glEnable(GL_LINE_SMOOTH); + glLineWidth(_lineWidth); + + // Bind and draw + bindGL(); + glDrawArrays(GL_LINES, 0, 2); + + // Restore GL State + unbindGL(); + _program->deactivate(); + glLineWidth(currentLineWidth); + glBlendEquationSeparate(blendEquationRGB, blendEquationAlpha); + glBlendFuncSeparate(blendSrcRGB, blendDestRGB, blendSrcAlpha, blendDestAlpha); + if (!isBlendEnabled) { + glDisablei(GL_BLEND, 0); + } + if (!isLineSmoothEnabled) { + glDisable(GL_LINE_SMOOTH); + } +} + + +/* Returns a position that is relative to the current + anchor node. This is a method to handle precision + problems that occur when approaching a line end point */ +glm::dvec3 RenderableNodeLine::getCoordinatePosFromAnchorNode(glm::dvec3 worldPos) { + + glm::dvec3 anchorNodePos(0); + + if (global::navigationHandler.orbitalNavigator().anchorNode()) { + anchorNodePos = global::navigationHandler.orbitalNavigator().anchorNode()->worldPosition(); + } + glm::dvec3 diffPos = glm::dvec3(worldPos.x - anchorNodePos.x, worldPos.y - anchorNodePos.y, + worldPos.z - anchorNodePos.z); + + return diffPos; +} + + +} // namespace openspace diff --git a/modules/base/rendering/renderablenodeline.h b/modules/base/rendering/renderablenodeline.h new file mode 100644 index 0000000000..d8866def1b --- /dev/null +++ b/modules/base/rendering/renderablenodeline.h @@ -0,0 +1,86 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * 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. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_BASE___RENDERABLENODELINE___H__ +#define __OPENSPACE_MODULE_BASE___RENDERABLENODELINE___H__ + +#include + +#include +#include +#include +#include +#include + +namespace ghoul::opengl { class ProgramObject; } + +namespace openspace { + +namespace documentation { struct Documentation; } + +class Translation; + +/** + * This is a class for a line that is drawn between two nodes in OpenSpace. + */ +class RenderableNodeLine : public Renderable { +public: + RenderableNodeLine(const ghoul::Dictionary& dictionary); + ~RenderableNodeLine() = default; + + static documentation::Documentation Documentation(); + +private: + void initializeGL() override; + void deinitializeGL() override; + + bool isReady() const override; + void updateVertexData(); + void render(const RenderData& data, RendererTasks& rendererTask) override; + + void unbindGL(); + void bindGL(); + + glm::dvec3 getCoordinatePosFromAnchorNode(glm::dvec3 worldPos); + + ghoul::opengl::ProgramObject* _program; + /// The vertex attribute location for position + /// must correlate to layout location in vertex shader + const GLuint _locVertex = 0; + GLuint _vaoId = 0; + GLuint _vBufferId = 0; + std::vector _vertexArray; + + glm::dvec3 _startPos; + glm::dvec3 _endPos; + + properties::StringProperty _start; + properties::StringProperty _end; + properties::Vec3Property _lineColor; + properties::FloatProperty _lineWidth; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___RENDERABLENODELINE___H__ diff --git a/modules/base/shaders/line_fs.glsl b/modules/base/shaders/line_fs.glsl new file mode 100644 index 0000000000..9479a98897 --- /dev/null +++ b/modules/base/shaders/line_fs.glsl @@ -0,0 +1,42 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * 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 "fragment.glsl" +#include "floatoperations.glsl" + +in float vs_depth; +in vec4 vs_positionViewSpace; + +uniform vec4 color; + +Fragment getFragment() { + Fragment frag; + + frag.color = color; + + frag.depth = vs_depth; + frag.gPosition = vs_positionViewSpace; + frag.gNormal = vec4(0.0, 0.0, 0.0, 1.0); + return frag; +} diff --git a/modules/base/shaders/line_vs.glsl b/modules/base/shaders/line_vs.glsl new file mode 100644 index 0000000000..2e68d502c6 --- /dev/null +++ b/modules/base/shaders/line_vs.glsl @@ -0,0 +1,43 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * 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. * + ****************************************************************************************/ + +#version __CONTEXT__ + +layout(location = 0) in vec3 in_position; + +out float vs_depth; +out vec4 vs_positionViewSpace; + +uniform mat4 modelViewTransform; +uniform mat4 projectionTransform; + +void main() { + vs_positionViewSpace = vec4(modelViewTransform * dvec4(in_position, 1)); + vec4 positionScreenSpace = projectionTransform * vs_positionViewSpace; + vs_depth = positionScreenSpace.w; + gl_Position = positionScreenSpace; + + // Set z to 0 to disable near and far plane, unique handling for perspective in space + gl_Position.z = 0.f; +} diff --git a/modules/galaxy/shaders/points_vs.glsl b/modules/galaxy/shaders/points_vs.glsl index ecfd7b3d15..456d12923b 100644 --- a/modules/galaxy/shaders/points_vs.glsl +++ b/modules/galaxy/shaders/points_vs.glsl @@ -50,8 +50,6 @@ void main() { dpos.xyz *= 8.0; dpos = modelMatrix * dpos; dpos /= PARSEC; - //It lies about 8 kpc from the center on what is known as the Orion Arm of the Milky Way - dpos.x += 8000; vec4 positionScreenSpace = z_normalization(vec4(cameraViewProjectionMatrix * dpos)); vs_color = in_color; diff --git a/modules/touch/CMakeLists.txt b/modules/touch/CMakeLists.txt index 35929a64b4..07190880d5 100644 --- a/modules/touch/CMakeLists.txt +++ b/modules/touch/CMakeLists.txt @@ -29,6 +29,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/tuioear.h ${CMAKE_CURRENT_SOURCE_DIR}/include/touchinteraction.h ${CMAKE_CURRENT_SOURCE_DIR}/include/touchmarker.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/win32_touch.h ) source_group("Header Files" FILES ${HEADER_FILES}) @@ -37,6 +38,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/tuioear.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/touchinteraction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/touchmarker.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/win32_touch.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/touch/include/tuioear.h b/modules/touch/include/tuioear.h index 3e8625c82e..a05296ba89 100644 --- a/modules/touch/include/tuioear.h +++ b/modules/touch/include/tuioear.h @@ -61,9 +61,7 @@ class TuioEar : public TUIO::TuioListener { public: TuioEar(); ~TuioEar() { - _tuioClient->disconnect(); - delete _tuioClient; - delete _oscReceiver; + _tuioClient.disconnect(); } /** @@ -108,8 +106,7 @@ class TuioEar : public TUIO::TuioListener { TUIO::TuioCursor _tapCo = TUIO::TuioCursor(-1, -1, -1.0f, -1.0f); std::mutex _mx; - TUIO::TuioClient *_tuioClient; - TUIO::OscReceiver *_oscReceiver; + TUIO::TuioClient _tuioClient; std::vector _list; diff --git a/modules/touch/include/win32_touch.h b/modules/touch/include/win32_touch.h new file mode 100644 index 0000000000..21a39fbc03 --- /dev/null +++ b/modules/touch/include/win32_touch.h @@ -0,0 +1,41 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * 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. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_TOUCH___WIN32_TOUCH___H__ +#define __OPENSPACE_MODULE_TOUCH___WIN32_TOUCH___H__ + +#ifdef WIN32 + +namespace openspace { + +class Win32TouchHook { +public: + Win32TouchHook(void* nativeWindow); + ~Win32TouchHook(); +}; + +} // namespace openspace + +#endif // WIN32 +#endif // __OPENSPACE_MODULE_TOUCH___WIN32_TOUCH___H__ diff --git a/modules/touch/src/tuioear.cpp b/modules/touch/src/tuioear.cpp index 4ba8c7e24b..4a9969f241 100644 --- a/modules/touch/src/tuioear.cpp +++ b/modules/touch/src/tuioear.cpp @@ -115,10 +115,12 @@ void TuioEar::removeTuioBlob(TuioBlob*) { } void TuioEar::refresh(TuioTime) { } // about every 15ms std::vector TuioEar::getInput() { + std::lock_guard lock(_mx); return _list; } bool TuioEar::tap() { + std::lock_guard lock(_mx); if (_tap) { _tap = false; return !_tap; @@ -129,7 +131,7 @@ bool TuioEar::tap() { } TuioCursor TuioEar::getTap() { - std::lock_guard lock(_mx); + std::lock_guard lock(_mx); return _tapCo; } @@ -157,9 +159,9 @@ void TuioEar::clearInput() { } // Standard UDP IP connection to port 3333 -TuioEar::TuioEar() { - _oscReceiver = new UdpReceiver(3333); - _tuioClient = new TuioClient(_oscReceiver); - _tuioClient->addTuioListener(this); - _tuioClient->connect(); +TuioEar::TuioEar() + : _tuioClient(3333) +{ + _tuioClient.addTuioListener(this); + _tuioClient.connect(); } diff --git a/modules/touch/src/win32_touch.cpp b/modules/touch/src/win32_touch.cpp new file mode 100644 index 0000000000..8d248d3a63 --- /dev/null +++ b/modules/touch/src/win32_touch.cpp @@ -0,0 +1,166 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * 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. * + ****************************************************************************************/ + +#ifdef WIN32 + +#include + +#include +#include + +#include + +#include + +#include +#include + +namespace { + constexpr const char* _loggerCat = "win32_touch"; + HHOOK gTouchHook{ nullptr }; + bool gStarted{ false }; + TUIO::TuioServer* gTuioServer{ nullptr }; + std::unordered_map gCursorMap; +} + +namespace openspace { + +// This hook will only work for Win7+ Digitizers. +// - Once GLFW has native touch support, we can remove this windows-specific code +LRESULT CALLBACK HookCallback(int nCode, WPARAM wParam, LPARAM lParam) { + if (nCode < 0) { + return CallNextHookEx(0, nCode, wParam, lParam); + } + if (nCode == HC_ACTION) { + LPMSG pStruct = reinterpret_cast(lParam); + const UINT message = pStruct->message; + switch (message) { + case WM_POINTERDOWN: + case WM_POINTERUPDATE: + case WM_POINTERUP: + { + POINTER_INFO pointerInfo = {}; + if (GetPointerInfo(GET_POINTERID_WPARAM(pStruct->wParam), &pointerInfo)) { + RECT rect; + GetClientRect(pStruct->hwnd, reinterpret_cast(&rect)); + + POINT p = pointerInfo.ptPixelLocation; + // native touch to screen conversion + ScreenToClient(pStruct->hwnd, reinterpret_cast(&p)); + + float xPos = (float)p.x / (float)(rect.right - rect.left); + float yPos = (float)p.y / (float)(rect.bottom - rect.top); + if (pointerInfo.pointerFlags & POINTER_FLAG_DOWN) { + // Handle new touchpoint + gTuioServer->initFrame(TUIO::TuioTime::getSessionTime()); + gCursorMap[pointerInfo.pointerId] = gTuioServer->addTuioCursor(xPos, yPos); + gTuioServer->commitFrame(); + } + else if (pointerInfo.pointerFlags & POINTER_FLAG_UPDATE) { + // Handle update of touchpoint + TUIO::TuioTime frameTime = TUIO::TuioTime::getSessionTime(); + if (gCursorMap[pointerInfo.pointerId]->getTuioTime() == frameTime) { + break; + } + gTuioServer->initFrame(frameTime); + gTuioServer->updateTuioCursor(gCursorMap[pointerInfo.pointerId], xPos, yPos); + gTuioServer->commitFrame(); + } + else if (pointerInfo.pointerFlags & POINTER_FLAG_UP) { + // Handle removed touchpoint + gTuioServer->initFrame(TUIO::TuioTime::getSessionTime()); + gTuioServer->removeTuioCursor(gCursorMap[pointerInfo.pointerId]); + gTuioServer->commitFrame(); + gCursorMap.erase(pointerInfo.pointerId); + } + } + break; + } + } + } + + // Pass the hook along! + return CallNextHookEx(0, nCode, wParam, lParam); +} + +Win32TouchHook::Win32TouchHook(void* nativeWindow) +{ + HWND hWnd = reinterpret_cast(nativeWindow); + if (hWnd == nullptr) { + LINFO("No windowhandle available for touch input."); + return; + } + + // Test for touch: + int value = GetSystemMetrics(SM_DIGITIZER); + if ((value & NID_READY) == 0) { + // Don't bother setting up touch hooks? + return; + } + // stack ready, drivers installed and digitizer is ready for input + if (value & NID_MULTI_INPUT) { + // Digitizer is multitouch + LINFO("Found Multitouch input digitizer!"); + } + if (value & NID_INTEGRATED_TOUCH) { + // Integrated touch + } + + // This should be needed, but we seem to receive messages even without it, + // probably a Win7+ behaviour + // Also - RegisterTouchWindow enables Windows gestures, which we don't want + // since they produce visual feedback for "press-and-tap" etc. + // RegisterTouchWindow(hWnd, TWF_FINETOUCH | TWF_WANTPALM); + + // TODO: Would be nice to find out if the gesture "press-and-tap" can be disabled + // basically we don't really care for windows gestures for now... + // this disables press and hold (right-click) gesture + const DWORD dwHwndTabletProperty = TABLET_DISABLE_PRESSANDHOLD; + + ATOM atom = ::GlobalAddAtom(MICROSOFT_TABLETPENSERVICE_PROPERTY); + ::SetProp(hWnd, MICROSOFT_TABLETPENSERVICE_PROPERTY, reinterpret_cast(dwHwndTabletProperty)); + ::GlobalDeleteAtom(atom); + + if (!gStarted) { + gStarted = true; + gTuioServer = new TUIO::TuioServer("localhost", 3333); + TUIO::TuioTime::initSession(); + gTouchHook = SetWindowsHookExW(WH_GETMESSAGE, HookCallback, GetModuleHandleW(NULL), GetCurrentThreadId()); + if (!gTouchHook) { + LINFO(fmt::format("Failed to setup WindowsHook for touch input redirection")); + delete gTuioServer; + gStarted = false; + } + } +} + +Win32TouchHook::~Win32TouchHook() { + if (gStarted) { + UnhookWindowsHookEx(gTouchHook); + delete gTuioServer; + } +} + +} // namespace openspace +#endif // WIN32 diff --git a/modules/touch/touchmodule.cpp b/modules/touch/touchmodule.cpp index f0e38dd2c6..a47e2fe4d2 100644 --- a/modules/touch/touchmodule.cpp +++ b/modules/touch/touchmodule.cpp @@ -23,6 +23,7 @@ ****************************************************************************************/ #include +#include #include #include @@ -156,6 +157,14 @@ TouchModule::TouchModule() global::callback::initializeGL.push_back([&]() { LDEBUGC("TouchModule", "Initializing TouchMarker OpenGL"); _markers.initialize(); +#ifdef WIN32 + // We currently only support one window of touch input internally + // so here we grab the first window-handle and use it. + void* nativeWindowHandle = global::windowDelegate.getNativeWindowHandle(0); + if (nativeWindowHandle) { + _win32TouchHook.reset(new Win32TouchHook(nativeWindowHandle)); + } +#endif //WIN32 }); global::callback::deinitializeGL.push_back([&]() { @@ -190,4 +199,8 @@ TouchModule::TouchModule() } +TouchModule::~TouchModule() { + //intentionally left empty +} + } // namespace openspace diff --git a/modules/touch/touchmodule.h b/modules/touch/touchmodule.h index a596944ab2..5300967a36 100644 --- a/modules/touch/touchmodule.h +++ b/modules/touch/touchmodule.h @@ -31,11 +31,15 @@ namespace openspace { + #ifdef WIN32 + class Win32TouchHook; + #endif //WIN32 class TouchModule : public OpenSpaceModule { using Point = std::pair; public: TouchModule(); + ~TouchModule(); private: /** @@ -54,6 +58,9 @@ namespace openspace { // contains an id and the TuioPoint that was processed last frame std::vector _lastProcessed; glm::ivec2 _webPositionCallback = glm::ivec2(0,0); +#ifdef WIN32 + std::unique_ptr _win32TouchHook; +#endif //WIN32 }; } // namespace openspace diff --git a/modules/webbrowser/src/webbrowserapp.cpp b/modules/webbrowser/src/webbrowserapp.cpp index dc49bd8fa9..f5f1aa05ac 100644 --- a/modules/webbrowser/src/webbrowserapp.cpp +++ b/modules/webbrowser/src/webbrowserapp.cpp @@ -43,8 +43,9 @@ void WebBrowserApp::OnContextCreated(CefRefPtr, CefRefPtr, void WebBrowserApp::OnBeforeCommandLineProcessing(const CefString&, CefRefPtr commandLine) { -// command_line->AppendSwitch("disable-gpu"); -// command_line->AppendSwitch("disable-gpu-compositing"); + commandLine->AppendSwitch("disable-gpu"); + commandLine->AppendSwitch("disable-gpu-compositing"); + commandLine->AppendSwitch("enable-begin-frame-scheduling"); commandLine->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required"); } diff --git a/modules/webbrowser/webbrowsermodule.cpp b/modules/webbrowser/webbrowsermodule.cpp index bf11e5eb34..eb86fe070b 100644 --- a/modules/webbrowser/webbrowsermodule.cpp +++ b/modules/webbrowser/webbrowsermodule.cpp @@ -142,16 +142,6 @@ void WebBrowserModule::internalInitialize(const ghoul::Dictionary& dictionary) { _cefHost = std::make_unique(_webHelperLocation); LDEBUG("Starting CEF... done!"); - global::callback::preSync.emplace_back([this]() { - if (_cefHost && !_browsers.empty()) { - _cefHost->doMessageLoopWork(); - - const std::chrono::time_point timeAfter = - std::chrono::high_resolution_clock::now(); - webbrowser::latestCall = timeAfter; - } - }); - if (dictionary.hasValue(UpdateBrowserBetweenRenderablesInfo.identifier)) { _updateBrowserBetweenRenderables = dictionary.value(UpdateBrowserBetweenRenderablesInfo.identifier); @@ -218,14 +208,18 @@ bool WebBrowserModule::isEnabled() const { return _enabled; } -namespace webbrowser { - /** * Logic for the webbrowser performance hotfix, * described in more detail in globalscallbacks.h. */ +namespace webbrowser { -std::chrono::microseconds interval = std::chrono::microseconds(1); + /** +* The time interval to describe how often the CEF message loop needs to +* be pumped to work properly. A value of 10000 us updates CEF a 100 times +* per second which is enough for fluid interaction without wasting resources +*/ +std::chrono::microseconds interval = std::chrono::microseconds(10000); std::chrono::time_point latestCall; CefHost* cefHost = nullptr;