diff --git a/data/assets/default.scene b/data/assets/default.scene index c6d3e2523c..4cdd560715 100644 --- a/data/assets/default.scene +++ b/data/assets/default.scene @@ -9,9 +9,9 @@ asset.onInitialize(function () openspace.globebrowsing.goToGeo("Earth", 58.5877, 16.1924, 20000000) - openspace.markInterestingNodes({ "Earth", "Mars", "Moon", "Sun" }) + openspace.markInterestingNodes({ "Saturn", "Mars", "Moon", "Sun", "Saturn" }) end) asset.onDeinitialize(function () - openspace.removeInterestingNodes({ "Earth", "Mars", "Moon", "Sun" }) + openspace.removeInterestingNodes({ "Earth", "Mars", "Moon", "Sun", "Saturn" }) end) diff --git a/data/assets/scene/solarsystem/planets/saturn/saturn.asset b/data/assets/scene/solarsystem/planets/saturn/saturn.asset index 77b45972f2..49748b1bf7 100644 --- a/data/assets/scene/solarsystem/planets/saturn/saturn.asset +++ b/data/assets/scene/solarsystem/planets/saturn/saturn.asset @@ -34,6 +34,15 @@ local Saturn = { Enabled = true } } + }, + Rings = { + Texture = textures .. "/saturn_rings.png", + Size = 140445000, + Offset = { 74500 / 140445.100671159, 1.0 }, -- min / max extend + Shadows = { + Enabled = true, + DistanceFraction = 50.0 + } } }, Tag = { "planet_solarSystem", "planet_giants" }, @@ -42,21 +51,22 @@ local Saturn = { } } -local SaturnRings = { - Identifier = "SaturnRings", - Parent = Saturn.Identifier, - Renderable = { - Type = "RenderableRings", - Texture = textures .. "/saturn_rings.png", - Size = 140445000, - Offset = { 74500 / 140445.100671159, 1.0 } -- min / max extend - }, - GUI = { - Name = "Saturn Rings", - Path = "/Solar System/Planets/Saturn" - } -} +-- local SaturnRings = { +-- Identifier = "SaturnRings", +-- Parent = Saturn.Identifier, +-- Renderable = { +-- Type = "RenderableRings", +-- Texture = textures .. "/saturn_rings.png", +-- Size = 140445000, +-- Offset = { 74500 / 140445.100671159, 1.0 } -- min / max extend +-- }, +-- GUI = { +-- Name = "Saturn Rings", +-- Path = "/Solar System/Planets/Saturn" +-- } +-- } -assetHelper.registerSceneGraphNodesAndExport(asset, { Saturn, SaturnRings }) +--assetHelper.registerSceneGraphNodesAndExport(asset, { Saturn, SaturnRings }) +assetHelper.registerSceneGraphNodesAndExport(asset, { Saturn }) diff --git a/modules/globebrowsing/CMakeLists.txt b/modules/globebrowsing/CMakeLists.txt index 7d46e40e1a..7f8550c9e7 100644 --- a/modules/globebrowsing/CMakeLists.txt +++ b/modules/globebrowsing/CMakeLists.txt @@ -51,6 +51,8 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/rawtile.h ${CMAKE_CURRENT_SOURCE_DIR}/src/rawtiledatareader.h ${CMAKE_CURRENT_SOURCE_DIR}/src/renderableglobe.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/ringscomponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/shadowcomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/skirtedgrid.h ${CMAKE_CURRENT_SOURCE_DIR}/src/tileindex.h ${CMAKE_CURRENT_SOURCE_DIR}/src/tileloadjob.h @@ -80,6 +82,8 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/rawtile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/rawtiledatareader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/renderableglobe.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ringscomponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/shadowcomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/skirtedgrid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/tileindex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/tileloadjob.cpp @@ -94,6 +98,10 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/globalrenderer_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/localrenderer_vs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/renderer_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/rings_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/rings_fs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/rings_geom_vs.glsl + ${CMAKE_CURRENT_SOURCE_DIR}/shaders/rings_geom_fs.glsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/texturetilemapping.hglsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/tile.hglsl ${CMAKE_CURRENT_SOURCE_DIR}/shaders/tileheight.hglsl diff --git a/modules/globebrowsing/shaders/rings_fs.glsl b/modules/globebrowsing/shaders/rings_fs.glsl new file mode 100644 index 0000000000..dfaab354a4 --- /dev/null +++ b/modules/globebrowsing/shaders/rings_fs.glsl @@ -0,0 +1,129 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * 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 "PowerScaling/powerScaling_fs.hglsl" +#include "fragment.glsl" + +in vec2 vs_st; +in float vs_screenSpaceDepth; +in vec4 vs_positionViewSpace; +in flat vec4 shadowCoords; + +uniform sampler2D shadowPositionTexture; + +uniform sampler2DShadow shadowMap; +uniform sampler1D texture1; +uniform vec2 textureOffset; +uniform float transparency; + +uniform bool hasSunPosition; +uniform vec3 sunPosition; +uniform float _nightFactor; + +// temp +in vec4 fragPosInLightSpace; + +Fragment getFragment() { + // Moving the origin to the center + vec2 st = (vs_st - vec2(0.5)) * 2.0; + + // The length of the texture coordinates vector is our distance from the center + float radius = length(st); + + // We only want to consider ring-like objects so we need to discard everything else + if (radius > 1.0) + discard; + + // Remapping the texture coordinates + // Radius \in [0,1], texCoord \in [textureOffset.x, textureOffset.y] + // textureOffset.x -> 0 + // textureOffset.y -> 1 + float texCoord = (radius - textureOffset.x) / (textureOffset.y - textureOffset.x); + if (texCoord < 0.f || texCoord > 1.f) { + discard; + } + + vec4 diffuse = texture(texture1, texCoord); + float colorValue = length(diffuse.rgb); + // times 3 as length of vec3(1.0, 1.0, 1.0) will return 3 and we want + // to normalize the transparency value to [0,1] + if (colorValue < 3.0 * transparency) { + diffuse.a = pow(colorValue / (3.0 * transparency), 1); + } + + // shadow == 1.0 means it is not in shadow + float shadow = 1.0; + if ( shadowCoords.z >= 0 ) { + shadow = textureProj(shadowMap, shadowCoords); + } + + // shadow = 1.0; + // vec4 depthInTexture = vec4(0.0, 0.0, 0.0, 1.0); + // //if (shadowCoords.z >= 0) { + // if (true) { + // vec4 byHandCoords = shadowCoords / shadowCoords.w; + // // Distance of the current pixel from the light source + // depthInTexture = texture(shadowPositionTexture, byHandCoords.xy); + // //depthInTexture = texture(shadowPositionTexture, vec2(0.5, 0.5)); + // // if (depthInTexture.x < byHandCoords.z) { + // // shadow = 0.0; + // // } + // if (length(fragPosInLightSpace) > depthInTexture.x) { + // shadow = 0.2; + // } + // //shadow = length(fragPosInLightSpace); + // } + + // The normal for the one plane depends on whether we are dealing + // with a front facing or back facing fragment + vec3 normal; + // The plane is oriented on the xz plane + // WARNING: This might not be the case for Uranus + if (gl_FrontFacing) { + normal = vec3(-1.0, 0.0, 0.0); + } + else { + normal = vec3(1.0, 0.0, 0.0); + } + + // Reduce the color of the fragment by the user factor + // if we are facing away from the Sun + if (dot(sunPosition, normal) < 0) { + diffuse.xyz *= _nightFactor; + } + + Fragment frag; + //frag.color = depthInTexture; + frag.color = (0.55 * diffuse * shadow) + diffuse * 0.45; + //frag.color = vec4(shadow * vec3(1.0, 1.0, 1.0), 1.0); + //frag.depth = vs_position.w; + frag.depth = vs_screenSpaceDepth; + if (diffuse.a < 1.0) + frag.gPosition = vec4(1e30, 1e30, 1e30, 1.0); + else + frag.gPosition = vs_positionViewSpace; + frag.gNormal = vec4(normal, 1.0); + + return frag; +} diff --git a/modules/globebrowsing/shaders/rings_geom_fs.glsl b/modules/globebrowsing/shaders/rings_geom_fs.glsl new file mode 100644 index 0000000000..0c167298b9 --- /dev/null +++ b/modules/globebrowsing/shaders/rings_geom_fs.glsl @@ -0,0 +1,75 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * 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 "PowerScaling/powerScaling_fs.hglsl" +#include "fragment.glsl" + +layout (location = 3) out vec4 renderedPosition; + +in vec2 vs_st; +in float vs_screenSpaceDepth; +in vec4 vs_positionViewSpace; + +uniform vec2 textureOffset; + +Fragment getFragment() { + // Moving the origin to the center + vec2 st = (vs_st - vec2(0.5)) * 2.0; + + // The length of the texture coordinates vector is our distance from the center + float radius = length(st); + + // We only want to consider ring-like objects so we need to discard everything else + if (radius > 1.0) + discard; + + // Remapping the texture coordinates + // Radius \in [0,1], texCoord \in [textureOffset.x, textureOffset.y] + // textureOffset.x -> 0 + // textureOffset.y -> 1 + float texCoord = (radius - textureOffset.x) / (textureOffset.y - textureOffset.x); + if (texCoord < 0.f || texCoord > 1.f) { + discard; + } + + // The normal for the one plane depends on whether we are dealing + // with a front facing or back facing fragment + //vec3 normal; + // The plane is oriented on the xz plane + // WARNING: This might not be the case for Uranus + // if (gl_FrontFacing) { + // normal = vec3(-1.0, 0.0, 0.0); + // } + // else { + // normal = vec3(1.0, 0.0, 0.0); + // } + + Fragment frag; + frag.color = vec4(1.0);; + frag.depth = vs_screenSpaceDepth; + renderedPosition = vec4(vec3(length(vs_positionViewSpace.xyz)), 1.0);//vec4(vs_positionViewSpace.xyz, 1.0); + //renderedPosition = vec4(vec3(vs_screenSpaceDepth.w)/10.0, 1.0); + + return frag; +} diff --git a/modules/globebrowsing/shaders/rings_geom_vs.glsl b/modules/globebrowsing/shaders/rings_geom_vs.glsl new file mode 100644 index 0000000000..1b287f29d3 --- /dev/null +++ b/modules/globebrowsing/shaders/rings_geom_vs.glsl @@ -0,0 +1,51 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * 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__ + +#include "PowerScaling/powerScalingMath.hglsl" + +layout(location = 0) in vec2 in_position; +layout(location = 1) in vec2 in_st; + +out vec2 vs_st; +out float vs_screenSpaceDepth; +out vec4 vs_positionViewSpace; + +uniform dmat4 modelViewMatrix; +uniform dmat4 projectionMatrix; + +void main() { + vs_st = in_st; + + dvec4 positionViewSpace = modelViewMatrix * dvec4(in_position, 0.0, 1.0); + vec4 positionClipSpace = vec4(projectionMatrix * positionViewSpace); + vec4 positionClipSpaceZNorm = z_normalization(positionClipSpace); + + vs_screenSpaceDepth = positionClipSpaceZNorm.w; + vs_positionViewSpace = vec4(positionViewSpace); + + //gl_Position = positionClipSpaceZNorm; + gl_Position = positionClipSpace; +} \ No newline at end of file diff --git a/modules/globebrowsing/shaders/rings_vs.glsl b/modules/globebrowsing/shaders/rings_vs.glsl new file mode 100644 index 0000000000..729e8615a1 --- /dev/null +++ b/modules/globebrowsing/shaders/rings_vs.glsl @@ -0,0 +1,67 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * 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__ + +#include "PowerScaling/powerScaling_vs.hglsl" + +layout(location = 0) in vec2 in_position; +layout(location = 1) in vec2 in_st; + +out vec2 vs_st; +out float vs_screenSpaceDepth; +out vec4 vs_positionViewSpace; +out vec4 shadowCoords; + +// temp +out vec4 fragPosInLightSpace; +uniform dmat4 objectToLightSpaceMatrix; + +uniform dmat4 modelViewMatrix; +uniform dmat4 projectionMatrix; + +// ShadowMatrix is the matrix defined by: +// textureCoordsMatrix * projectionMatrix * combinedViewMatrix * modelMatrix +// where textureCoordsMatrix is just a scale and bias computation: [-1,1] to [0,1] +uniform dmat4 shadowMatrix; + + +void main() { + vs_st = in_st; + + dvec4 positionViewSpace = modelViewMatrix * dvec4(in_position, 0.0, 1.0); + vec4 positionClipSpace = vec4(projectionMatrix * positionViewSpace); + vec4 positionClipSpaceZNorm = z_normalization(positionClipSpace); + + shadowCoords = vec4(shadowMatrix * dvec4(in_position, 0.0, 1.0)); + // temp + fragPosInLightSpace = vec4(objectToLightSpaceMatrix * + dvec4(in_position.xy, 0.0, 1.0)); + + vs_screenSpaceDepth = positionClipSpaceZNorm.w; + vs_positionViewSpace = vec4(positionViewSpace); + + //gl_Position = positionClipSpaceZNorm; + gl_Position = positionClipSpace; +} diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index 2a9dabcea7..e1c8278561 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -505,6 +505,8 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) , _grid(DefaultSkirtedGridSegments, DefaultSkirtedGridSegments) , _leftRoot(Chunk(LeftHemisphereIndex)) , _rightRoot(Chunk(RightHemisphereIndex)) + , _ringsComponent(dictionary) + , _shadowComponent(dictionary) { _generalProperties.currentLodScaleFactor.setReadOnly(true); @@ -656,6 +658,20 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) _labelsDictionary = dictionary.value(KeyLabels); } + // Components + if (dictionary.hasKey("Rings")) { + _ringsComponent.initialize(); + addPropertySubOwner(_ringsComponent); + _hasRings = true; + + ghoul::Dictionary ringsDic; + dictionary.getValue("Rings", ringsDic); + if (ringsDic.hasKey("Shadows")) { + _shadowComponent.initialize(); + addPropertySubOwner(_shadowComponent); + } + } + #ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION _module = global::moduleEngine.module(); #endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION @@ -670,6 +686,11 @@ void RenderableGlobe::initializeGL() { _layerManager.update(); _grid.initializeGL(); + + _ringsComponent.initializeGL(); + + _shadowComponent.initializeGL(); + // Recompile the shaders directly so that it is not done the first time the render // function is called. recompileShaders(); @@ -691,6 +712,10 @@ void RenderableGlobe::deinitializeGL() { } _grid.deinitializeGL(); + + _ringsComponent.deinitializeGL(); + + _shadowComponent.deinitializeGL(); } bool RenderableGlobe::isReady() const { @@ -713,8 +738,49 @@ void RenderableGlobe::render(const RenderData& data, RendererTasks& rendererTask if (distanceToCamera < distance) { try { - renderChunks(data, rendererTask); - _globeLabelsComponent.draw(data); + // Before Shadows + //renderChunks(data, rendererTask); + //_globeLabelsComponent.draw(data); + + + if (_hasRings && _ringsComponent.isEnabled()) { + if (_shadowComponent.isEnabled()) { + + glDisablei(GL_BLEND, 3); + //glEnablei(GL_DEPTH_TEST, 3); + _shadowComponent.begin(data); + + //_ringsComponent.draw(data, RingsComponent::GeometryOnly); + //RenderData tmpRD = data; + //tmpRD.modelTransform.rotation = tmpRD.modelTransform.rotation * glm::dmat3(glm::rotate(glm::dmat4(1), 90.0, glm::dvec3(1.0, 0.0, 0.0))); + //_ringsComponent.draw(tmpRD, RingsComponent::GeometryOnly); + renderChunks(data, rendererTask, true); + + _shadowComponent.end(data); + glEnablei(GL_BLEND, 3); + + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(2.5f, 10.0f); + + _ringsComponent.draw( + data, + RingsComponent::GeometryAndShading, + _shadowComponent.shadowMapData() + ); + renderChunks(data, rendererTask); + + glDisable(GL_POLYGON_OFFSET_FILL); + } + else { + renderChunks(data, rendererTask); + _ringsComponent.draw(data, RingsComponent::GeometryAndShading); + } + } + else { + renderChunks(data, rendererTask); + } + + } catch (const ghoul::opengl::TextureUnit::TextureUnitError&) { std::string layer = _lastChangedLayer ? _lastChangedLayer->guiName() : ""; @@ -852,7 +918,8 @@ const glm::dmat4& RenderableGlobe::modelTransform() const { // Rendering code ////////////////////////////////////////////////////////////////////////////////////////// -void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&) { +void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&, + const bool renderGeomOnly) { if (_shadersNeedRecompilation) { recompileShaders(); } @@ -1086,7 +1153,7 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&) { // Render all chunks that want to be rendered globally _globalRenderer.program->activate(); for (int i = 0; i < std::min(globalCount, ChunkBufferSize); ++i) { - renderChunkGlobally(*global[i], data); + renderChunkGlobally(*global[i], data, renderGeomOnly); } _globalRenderer.program->deactivate(); @@ -1094,7 +1161,7 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&) { // Render all chunks that need to be rendered locally _localRenderer.program->activate(); for (int i = 0; i < std::min(localCount, ChunkBufferSize); ++i) { - renderChunkLocally(*local[i], data); + renderChunkLocally(*local[i], data, renderGeomOnly); } _localRenderer.program->deactivate(); @@ -1151,7 +1218,8 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&) { } } -void RenderableGlobe::renderChunkGlobally(const Chunk& chunk, const RenderData& data) { +void RenderableGlobe::renderChunkGlobally(const Chunk& chunk, const RenderData& data, + const bool renderGeomOnly) { //PerfMeasure("globally"); const TileIndex& tileIndex = chunk.tileIndex; ghoul::opengl::ProgramObject& program = *_globalRenderer.program; @@ -1199,16 +1267,21 @@ void RenderableGlobe::renderChunkGlobally(const Chunk& chunk, const RenderData& } glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); + + if (!renderGeomOnly) { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } _grid.drawUsingActiveProgram(); + for (GPULayerGroup& l : _globalRenderer.gpuLayerGroups) { l.deactivate(); } } -void RenderableGlobe::renderChunkLocally(const Chunk& chunk, const RenderData& data) { +void RenderableGlobe::renderChunkLocally(const Chunk& chunk, const RenderData& data, + const bool renderGeomOnly) { //PerfMeasure("locally"); const TileIndex& tileIndex = chunk.tileIndex; ghoul::opengl::ProgramObject& program = *_localRenderer.program; @@ -1301,9 +1374,11 @@ void RenderableGlobe::renderChunkLocally(const Chunk& chunk, const RenderData& d } glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - + if (!renderGeomOnly) { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } + _grid.drawUsingActiveProgram(); for (GPULayerGroup& l : _localRenderer.gpuLayerGroups) { diff --git a/modules/globebrowsing/src/renderableglobe.h b/modules/globebrowsing/src/renderableglobe.h index a15d671704..87fe7aa30b 100644 --- a/modules/globebrowsing/src/renderableglobe.h +++ b/modules/globebrowsing/src/renderableglobe.h @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include #include @@ -181,7 +183,8 @@ private: */ float getHeight(const glm::dvec3& position) const; - void renderChunks(const RenderData& data, RendererTasks& rendererTask); + void renderChunks(const RenderData& data, RendererTasks& rendererTask, + const bool renderGeomOnly = false); /** * Chunks can be rendered either globally or locally. Global rendering is performed @@ -191,7 +194,8 @@ private: * point precision by doing this which means that the camera too close to a global * tile will lead to jagging. We only render global chunks for lower chunk levels. */ - void renderChunkGlobally(const Chunk& chunk, const RenderData& data); + void renderChunkGlobally(const Chunk& chunk, const RenderData& data, + const bool renderGeomOnly = false); /** * Local rendering of chunks are done using linear interpolation in camera space. @@ -204,7 +208,8 @@ private: * levels) the better the approximation becomes. This is why we only render local * chunks for higher chunk levels. */ - void renderChunkLocally(const Chunk& chunk, const RenderData& data); + void renderChunkLocally(const Chunk& chunk, const RenderData& data, + const bool renderGeomOnly = false); void debugRenderChunk(const Chunk& chunk, const glm::dmat4& mvp, bool renderBounds, bool renderAABB) const; @@ -275,6 +280,11 @@ private: size_t _iterationsOfUnavailableData = 0; Layer* _lastChangedLayer = nullptr; + // Components + RingsComponent _ringsComponent; + ShadowComponent _shadowComponent; + bool _hasRings = false; + // Labels GlobeLabelsComponent _globeLabelsComponent; ghoul::Dictionary _labelsDictionary; diff --git a/modules/globebrowsing/src/ringscomponent.cpp b/modules/globebrowsing/src/ringscomponent.cpp new file mode 100644 index 0000000000..9c7e2003f2 --- /dev/null +++ b/modules/globebrowsing/src/ringscomponent.cpp @@ -0,0 +1,445 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2018 * +* * +* 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 + +namespace { + constexpr const std::array UniformNames = { + "modelViewMatrix", "projectionMatrix", "textureOffset", + "transparency", "_nightFactor", "sunPosition", "texture1" + }; + + constexpr const std::array GeomUniformNames = { + "modelViewMatrix", "projectionMatrix", "textureOffset" + }; + + constexpr openspace::properties::Property::PropertyInfo TextureInfo = { + "Texture", + "Texture", + "This value is the path to a texture on disk that contains a one-dimensional " + "texture which is used for these rings." + }; + + constexpr openspace::properties::Property::PropertyInfo SizeInfo = { + "Size", + "Size", + "This value specifies the radius of the rings in meter." + }; + + constexpr openspace::properties::Property::PropertyInfo OffsetInfo = { + "Offset", + "Offset", + "This value is used to limit the width of the rings.Each of the two values is a " + "value between 0 and 1, where 0 is the center of the ring and 1 is the maximum " + "extent at the radius. If this value is, for example {0.5, 1.0}, the ring is " + "only shown between radius/2 and radius. It defaults to {0.0, 1.0}." + }; + + constexpr openspace::properties::Property::PropertyInfo NightFactorInfo = { + "NightFactor", + "Night Factor", + "This value is a multiplicative factor that is applied to the side of the rings " + "that is facing away from the Sun. If this value is equal to '1', no darkening " + "of the night side occurs." + }; + + constexpr openspace::properties::Property::PropertyInfo TransparencyInfo = { + "Transparency", + "Transparency", + "This value determines the transparency of part of the rings depending on the " + "color values. For this value v, the transparency is equal to length(color) / v." + }; +} // namespace + +namespace openspace { + + documentation::Documentation RingsComponent::Documentation() { + using namespace documentation; + return { + "Rings Component", + "globebrowsing_rings_component", + { + { + TextureInfo.identifier, + new StringVerifier, + Optional::Yes, + TextureInfo.description + }, + { + SizeInfo.identifier, + new DoubleVerifier, + Optional::Yes, + SizeInfo.description + }, + { + OffsetInfo.identifier, + new DoubleVector2Verifier, + Optional::Yes, + OffsetInfo.description + }, + { + NightFactorInfo.identifier, + new DoubleVerifier, + Optional::Yes, + NightFactorInfo.description + }, + { + TransparencyInfo.identifier, + new DoubleVerifier, + Optional::Yes, + TransparencyInfo.description + } + } + }; + } + + RingsComponent::RingsComponent(const ghoul::Dictionary& dictionary) + : properties::PropertyOwner({ "Rings" }) + , _texturePath(TextureInfo) + , _size(SizeInfo, 1.f, 0.f, 1e25f) + , _offset(OffsetInfo, glm::vec2(0.f, 1.f), glm::vec2(0.f), glm::vec2(1.f)) + , _nightFactor(NightFactorInfo, 0.33f, 0.f, 1.f) + , _transparency(TransparencyInfo, 0.15f, 0.f, 1.f) + , _enabled({ "Enabled", "Enabled", "Enable/Disable Rings" }, true) + , _ringsDictionary(dictionary) + { + using ghoul::filesystem::File; + + if (dictionary.hasKey("Rings")) { + dictionary.getValue("Rings", _ringsDictionary); + } + + documentation::testSpecificationAndThrow( + Documentation(), + _ringsDictionary, + "RingsComponent" + ); + } + + void RingsComponent::initialize() + { + using ghoul::filesystem::File; + + addProperty(_enabled); + + _size = static_cast(_ringsDictionary.value(SizeInfo.identifier)); + //setBoundingSphere(_size); + _size.onChange([&]() { _planeIsDirty = true; }); + addProperty(_size); + + _texturePath = absPath(_ringsDictionary.value(TextureInfo.identifier)); + _textureFile = std::make_unique(_texturePath); + + if (_ringsDictionary.hasKeyAndValue(OffsetInfo.identifier)) { + _offset = _ringsDictionary.value(OffsetInfo.identifier); + } + addProperty(_offset); + + _texturePath.onChange([&]() { loadTexture(); }); + addProperty(_texturePath); + + _textureFile->setCallback([&](const File&) { _textureIsDirty = true; }); + + if (_ringsDictionary.hasKeyAndValue(NightFactorInfo.identifier)) { + _nightFactor = static_cast( + _ringsDictionary.value(NightFactorInfo.identifier) + ); + } + addProperty(_nightFactor); + + if (_ringsDictionary.hasKeyAndValue(TransparencyInfo.identifier)) { + _transparency = static_cast( + _ringsDictionary.value(TransparencyInfo.identifier) + ); + } + addProperty(_transparency); + } + + bool RingsComponent::isReady() const { + return (_shader || _geometryOnlyShader) && _texture; + } + + void RingsComponent::initializeGL() { + _shader = global::renderEngine.buildRenderProgram( + "RingsProgram", + absPath("${MODULE_GLOBEBROWSING}/shaders/rings_vs.glsl"), + absPath("${MODULE_GLOBEBROWSING}/shaders/rings_fs.glsl") + ); + + _geometryOnlyShader = global::renderEngine.buildRenderProgram( + "RingsGeomOnlyProgram", + absPath("${MODULE_GLOBEBROWSING}/shaders/rings_geom_vs.glsl"), + absPath("${MODULE_GLOBEBROWSING}/shaders/rings_geom_fs.glsl") + ); + + ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); + ghoul::opengl::updateUniformLocations( + *_geometryOnlyShader, + _geomUniformCache, + GeomUniformNames + ); + + glGenVertexArrays(1, &_quad); + glGenBuffers(1, &_vertexPositionBuffer); + + createPlane(); + loadTexture(); + } + + void RingsComponent::deinitializeGL() { + glDeleteVertexArrays(1, &_quad); + _quad = 0; + + glDeleteBuffers(1, &_vertexPositionBuffer); + _vertexPositionBuffer = 0; + + _textureFile = nullptr; + _texture = nullptr; + + global::renderEngine.removeRenderProgram(_shader.get()); + _shader = nullptr; + + global::renderEngine.removeRenderProgram(_geometryOnlyShader.get()); + _geometryOnlyShader = nullptr; + } + + void RingsComponent::draw( + const RenderData& data, + const RingsComponent::RenderPass renderPass, + const ShadowComponent::ShadowMapData& shadowData + ) { + + if (renderPass == GeometryAndShading) { + _shader->activate(); + } + else if (renderPass == GeometryOnly) { + _geometryOnlyShader->activate(); + } + + 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; + const glm::dmat4 projectionMatrix = glm::dmat4(data.camera.projectionMatrix()); + + ghoul::opengl::TextureUnit unit; + if (renderPass == GeometryAndShading) { + _shader->setUniform(_uniformCache.modelViewMatrix, modelViewTransform); + _shader->setUniform(_uniformCache.projectionMatrix, projectionMatrix); + _shader->setUniform(_uniformCache.textureOffset, _offset); + _shader->setUniform(_uniformCache.transparency, _transparency); + _shader->setUniform(_uniformCache.nightFactor, _nightFactor); + _shader->setUniform(_uniformCache.sunPosition, _sunPosition); + + unit.activate(); + _texture->bind(); + _shader->setUniform(_uniformCache.texture, unit); + + // Adding the model transformation to the final shadow matrix so we have a + // complete transformation from the model coordinates to the clip space of + // the light position. + _shader->setUniform("shadowMatrix", shadowData.shadowMatrix * modelTransform); + + ghoul::opengl::TextureUnit shadowMapUnit; + shadowMapUnit.activate(); + glBindTexture(GL_TEXTURE_2D, shadowData.shadowDepthTexture); + + _shader->setUniform("shadowMap", shadowMapUnit); + + // DEBUGGING + ghoul::opengl::TextureUnit shadowTextureUnit; + shadowTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, shadowData.positionInLightSpaceTexture); + _shader->setUniform("shadowPositionTexture", shadowTextureUnit); + _shader->setUniform("objectToLightSpaceMatrix", + shadowData.worldToLightSpaceMatrix * modelTransform); + + } + else if (renderPass == GeometryOnly) { + _geometryOnlyShader->setUniform( + _geomUniformCache.modelViewMatrix, + modelViewTransform + ); + _geometryOnlyShader->setUniform( + _geomUniformCache.projectionMatrix, + projectionMatrix + ); + _geometryOnlyShader->setUniform( + _geomUniformCache.textureOffset, + _offset + ); + } + + glEnable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glBindVertexArray(_quad); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glEnable(GL_CULL_FACE); + + if (renderPass == GeometryAndShading) { + _shader->deactivate(); + } + else if (renderPass == GeometryOnly) { + _geometryOnlyShader->deactivate(); + } + } + + void RingsComponent::update(const UpdateData& data) { + if (_shader->isDirty()) { + _shader->rebuildFromFile(); + ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); + } + + if (_geometryOnlyShader->isDirty()) { + _geometryOnlyShader->rebuildFromFile(); + ghoul::opengl::updateUniformLocations( + *_geometryOnlyShader, + _geomUniformCache, + GeomUniformNames + ); + } + + if (_planeIsDirty) { + createPlane(); + _planeIsDirty = false; + } + + if (_textureIsDirty) { + loadTexture(); + _textureIsDirty = false; + } + + _sunPosition = glm::normalize( + global::renderEngine.scene()->sceneGraphNode("Sun")->worldPosition() - + data.modelTransform.translation + ); + } + + void RingsComponent::loadTexture() { + if (!_texturePath.value().empty()) { + using namespace ghoul::io; + using namespace ghoul::opengl; + std::unique_ptr texture = TextureReader::ref().loadTexture( + absPath(_texturePath) + ); + + if (texture) { + LDEBUGC( + "RingsComponent", + fmt::format("Loaded texture from '{}'", absPath(_texturePath)) + ); + _texture = std::move(texture); + + _texture->uploadTexture(); + _texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + + _textureFile = std::make_unique(_texturePath); + _textureFile->setCallback( + [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } + ); + } + } + } + + void RingsComponent::createPlane() { + const GLfloat size = _size; + + struct VertexData { + GLfloat x; + GLfloat y; + GLfloat s; + GLfloat t; + }; + + VertexData data[] = { + { -size, -size, 0.f, 0.f }, + { size, size, 1.f, 1.f }, + { -size, size, 0.f, 1.f }, + { -size, -size, 0.f, 0.f }, + { size, -size, 1.f, 0.f }, + { size, size, 1.f, 1.f }, + }; + + glBindVertexArray(_quad); + glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer( + 0, + 2, + GL_FLOAT, + GL_FALSE, + sizeof(VertexData), + nullptr + ); + glEnableVertexAttribArray(1); + glVertexAttribPointer( + 1, + 2, + GL_FLOAT, + GL_FALSE, + sizeof(VertexData), + reinterpret_cast(offsetof(VertexData, s)) // NOLINT + ); + } + + bool RingsComponent::isEnabled() const { + return _enabled; + } +} // namespace openspace diff --git a/modules/globebrowsing/src/ringscomponent.h b/modules/globebrowsing/src/ringscomponent.h new file mode 100644 index 0000000000..4ebb05b963 --- /dev/null +++ b/modules/globebrowsing/src/ringscomponent.h @@ -0,0 +1,115 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2018 * +* * +* 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_GLOBEBROWSING___RINGSCOMPONENT___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___RINGSCOMPONENT___H__ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace ghoul { + class Dictionary; +} + +namespace ghoul::filesystem { class File; } + +namespace ghoul::opengl { + class ProgramObject; +} // namespace ghoul::opengl + +namespace openspace { + struct RenderData; + struct UpdateData; + + namespace documentation { struct Documentation; } + + class RingsComponent : public properties::PropertyOwner { + public: + enum RenderPass { + GeometryOnly, + GeometryAndShading + }; + public: + RingsComponent(const ghoul::Dictionary& dictionary); + + void initialize(); + void initializeGL(); + void deinitializeGL(); + + bool isReady() const; + + void draw( + const RenderData& data, + const RingsComponent::RenderPass renderPass, + const ShadowComponent::ShadowMapData& shadowData = {} + ); + void update(const UpdateData& data); + + static documentation::Documentation Documentation(); + + bool isEnabled() const; + + private: + void loadTexture(); + void createPlane(); + + properties::StringProperty _texturePath; + properties::FloatProperty _size; + properties::Vec2Property _offset; + properties::FloatProperty _nightFactor; + properties::FloatProperty _transparency; + properties::BoolProperty _enabled; + + std::unique_ptr _shader; + std::unique_ptr _geometryOnlyShader; + UniformCache(modelViewMatrix, projectionMatrix, textureOffset, + transparency, nightFactor, sunPosition, texture) _uniformCache; + UniformCache(modelViewMatrix, projectionMatrix, textureOffset) _geomUniformCache; + std::unique_ptr _texture; + std::unique_ptr _textureFile; + + ghoul::Dictionary _ringsDictionary; + bool _textureIsDirty = false; + GLuint _quad = 0; + GLuint _vertexPositionBuffer = 0; + bool _planeIsDirty = false; + + glm::vec3 _sunPosition; + }; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___RINGSCOMPONENT___H__ diff --git a/modules/globebrowsing/src/shadowcomponent.cpp b/modules/globebrowsing/src/shadowcomponent.cpp new file mode 100644 index 0000000000..6f51887b2a --- /dev/null +++ b/modules/globebrowsing/src/shadowcomponent.cpp @@ -0,0 +1,708 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2018 * +* * +* 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 + +#include +#include +#include + +namespace { + constexpr const char* _loggerCat = "ShadowComponent"; + + constexpr openspace::properties::Property::PropertyInfo SaveDepthTextureInfo = { + "SaveDepthTextureInfo", + "Save Depth Texture", + "Debug" + }; + + constexpr openspace::properties::Property::PropertyInfo DistanceFractionInfo = { + "DistanceFraction", + "Distance Fraction", + "Distance fraction of original distance from light source to the globe to be " + "considered as the new light source distance." + }; + + void checkFrameBufferState(const std::string& codePosition) + { + if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + LERROR("Framework not built. " + codePosition); + GLenum fbErr = glCheckFramebufferStatus(GL_FRAMEBUFFER); + switch (fbErr) { + case GL_FRAMEBUFFER_UNDEFINED: + LERROR("Indefined framebuffer."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + LERROR("Incomplete, missing attachement."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + LERROR("Framebuffer doesn't have at least one image attached to it."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + LERROR( + "Returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is " + "GL_NONE for any color attachment point(s) named by GL_DRAW_BUFFERi." + ); + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + LERROR( + "Returned if GL_READ_BUFFER is not GL_NONE and the value of " + "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color " + "attachment point named by GL_READ_BUFFER."); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + LERROR( + "Returned if the combination of internal formats of the attached " + "images violates an implementation - dependent set of restrictions." + ); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + LERROR( + "Returned if the value of GL_RENDERBUFFE_r_samples is not the same " + "for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES " + "is the not same for all attached textures; or , if the attached " + "images are a mix of renderbuffers and textures, the value of " + "GL_RENDERBUFFE_r_samples does not match the value of " + "GL_TEXTURE_SAMPLES." + ); + LERROR( + "Returned if the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not " + "the same for all attached textures; or , if the attached images are " + "a mix of renderbuffers and textures, the value of " + "GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached " + "textures." + ); + break; + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: + LERROR( + "Returned if any framebuffer attachment is layered, and any " + "populated attachment is not layered, or if all populated color " + "attachments are not from textures of the same target." + ); + break; + default: + LDEBUG("No error found checking framebuffer: " + codePosition); + break; + } + } + } +} // namespace + +namespace openspace { + + documentation::Documentation ShadowComponent::Documentation() { + using namespace documentation; + return { + "ShadowsRing Component", + "globebrowsing_shadows_component", + { + { + DistanceFractionInfo.identifier, + new DoubleVerifier, + Optional::Yes, + DistanceFractionInfo.description + } + } + }; + } + + ShadowComponent::ShadowComponent(const ghoul::Dictionary& dictionary) + : properties::PropertyOwner({ "Shadows" }) + , _saveDepthTexture(SaveDepthTextureInfo) + , _distanceFraction(DistanceFractionInfo, 30, 1, 100000) + , _enabled({ "Enabled", "Enabled", "Enable/Disable Shadows" }, true) + , _shadowMapDictionary(dictionary) + , _shadowDepthTextureHeight(1024) + , _shadowDepthTextureWidth(1024) + , _shadowDepthTexture(-1) + , _positionInLightSpaceTexture(-1) + , _shadowFBO(-1) + , _firstPassSubroutine(-1) + , _secondPassSubroutine(1) + , _defaultFBO(-1) + , _sunPosition(0.0) + , _shadowMatrix(1.0) + , _executeDepthTextureSave(false) + { + using ghoul::filesystem::File; + + if (dictionary.hasKey("Rings")) { + ghoul::Dictionary ringsDic; + dictionary.getValue("Rings", ringsDic); + if (ringsDic.hasKey("Shadows")) { + ringsDic.getValue("Shadows", _shadowMapDictionary); + } + } + + documentation::testSpecificationAndThrow( + Documentation(), + _shadowMapDictionary, + "ShadowComponent" + ); + + if (_shadowMapDictionary.hasKey(DistanceFractionInfo.identifier)) { + _distanceFraction = static_cast( + _shadowMapDictionary.value(DistanceFractionInfo.identifier) + ); + } + + _saveDepthTexture.onChange([&]() { + _executeDepthTextureSave = true; + }); + + addProperty(_enabled); + addProperty(_saveDepthTexture); + addProperty(_distanceFraction); + } + + void ShadowComponent::initialize() + { + using ghoul::filesystem::File; + } + + bool ShadowComponent::isReady() const { + return true; + } + + void ShadowComponent::initializeGL() { + createDepthTexture(); + createShadowFBO(); + } + + void ShadowComponent::deinitializeGL() { + glDeleteTextures(1, &_shadowDepthTexture); + glDeleteTextures(1, &_positionInLightSpaceTexture); + glDeleteFramebuffers(1, &_shadowFBO); + checkGLError("ShadowComponent::deinitializeGL() -- Deleted Textures and Framebuffer"); + } + + void ShadowComponent::begin(const RenderData& data) { + // =========================================== + // Builds light's ModelViewProjectionMatrix: + // =========================================== + + glm::dvec3 diffVector = glm::dvec3(_sunPosition) - data.modelTransform.translation; + double originalLightDistance = glm::length(diffVector); + glm::dvec3 lightDirection = glm::normalize(diffVector); + + // Percentage of the original light source distance (to avoid artifacts) + double multiplier = originalLightDistance * + (static_cast(_distanceFraction)/1.0E5); + + // New light source position + glm::dvec3 lightPosition = data.modelTransform.translation + + (lightDirection * multiplier); + + // Saving current Camera parameters + _cameraPos = data.camera.positionVec3(); + // JCC: We have aim and ancor nodes and position now. Need to fix this. + //_cameraFocus = data.camera.focusPositionVec3(); + _cameraRotation = data.camera.rotationQuaternion(); + + //=============== Automatically Created Camera Matrix =================== + //======================================================================= + //glm::dmat4 lightViewMatrix = glm::lookAt( + // //lightPosition, + // glm::dvec3(0.0), + // //glm::dvec3(_sunPosition), // position + // glm::dvec3(data.modelTransform.translation), // focus + // data.camera.lookUpVectorWorldSpace() // up + // //glm::dvec3(0.0, 1.0, 0.0) + //); + + //camera->setPositionVec3(lightPosition); + //camera->setFocusPositionVec3(data.modelTransform.translation); + //camera->setRotation(glm::dquat(glm::inverse(lightViewMatrix))); + + //======================================================================= + //======================================================================= + + + //=============== Manually Created Camera Matrix =================== + //================================================================== + // camera Z + glm::dvec3 cameraZ = lightDirection; + + // camera X + glm::dvec3 upVector = glm::dvec3(0.0, -1.0, 0.0); + glm::dvec3 cameraX = glm::normalize(glm::cross(upVector, cameraZ)); + + // camera Y + glm::dvec3 cameraY = glm::cross(cameraZ, cameraX); + + // init 4x4 matrix + glm::dmat4 cameraRotationMatrix(1.0); + + double* matrix = glm::value_ptr(cameraRotationMatrix); + matrix[0] = cameraX.x; + matrix[4] = cameraX.y; + matrix[8] = cameraX.z; + matrix[1] = cameraY.x; + matrix[5] = cameraY.y; + matrix[9] = cameraY.z; + matrix[2] = cameraZ.x; + matrix[6] = cameraZ.y; + matrix[10] = cameraZ.z; + + // set translation part + // We aren't setting the position here because it is set in + // the camera->setPosition() + //matrix[12] = -glm::dot(cameraX, lightPosition); + //matrix[13] = -glm::dot(cameraY, lightPosition); + //matrix[14] = -glm::dot(cameraZ, lightPosition); + + /*Scene* scene = camera->parent()->scene(); + global::navigationHandler.setFocusNode(data.); + */ + + Camera camera(data.camera); + camera.setPositionVec3(lightPosition); + // JCC: We have aim and ancor nodes and position now. Need to fix this. + //camera.setFocusPositionVec3(data.modelTransform.translation); + camera.setRotation(glm::dquat(glm::inverse(cameraRotationMatrix))); + + //======================================================================= + //======================================================================= + + + //============= Light Matrix by Camera Matrices Composition ============= + //======================================================================= + glm::dmat4 lightProjectionMatrix = glm::dmat4(camera.projectionMatrix()); + //glm::dmat4 lightProjectionMatrix = glm::ortho(-1000.0, 1000.0, -1000.0, 1000.0, 0.0010, 1000.0); + //glm::dmat4 lightProjectionMatrix = glm::frustum(-1.0, 1.0, -1.0, 1.0, 1.0, 1000000.0); + + // The model transformation missing in the final shadow matrix is add when rendering each + // object (using its transformations provided by the RenderData structure) + _shadowData.shadowMatrix = + _toTextureCoordsMatrix * + lightProjectionMatrix * + camera.combinedViewMatrix(); + + // temp + _shadowData.worldToLightSpaceMatrix = glm::dmat4(camera.combinedViewMatrix()); + + checkGLError("begin() -- Saving Current GL State"); + // Saves current state + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO); + glGetIntegerv(GL_VIEWPORT, _mViewport); + _faceCulling = glIsEnabled(GL_CULL_FACE); + glGetIntegerv(GL_CULL_FACE_MODE, &_faceToCull); + _polygonOffSet = glIsEnabled(GL_POLYGON_OFFSET_FILL); + glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &_polygonOffSetFactor); + glGetFloatv(GL_POLYGON_OFFSET_UNITS, &_polygonOffSetUnits); + glGetFloatv(GL_COLOR_CLEAR_VALUE, _colorClearValue); + glGetFloatv(GL_DEPTH_CLEAR_VALUE, &_depthClearValue); + _depthIsEnabled = glIsEnabled(GL_DEPTH_TEST); + glGetIntegerv(GL_DEPTH_FUNC, &_depthFunction); + _blendIsEnabled = glIsEnabled(GL_BLEND); + + + checkGLError("begin() -- before binding FBO"); + glBindFramebuffer(GL_FRAMEBUFFER, _shadowFBO); + checkGLError("begin() -- after binding FBO"); + glViewport(0, 0, _shadowDepthTextureWidth, _shadowDepthTextureHeight); + checkGLError("begin() -- set new viewport"); + glClearDepth(1.0f); + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + checkGLError("begin() -- after cleanning Depth buffer"); + + + /*glEnable(GL_CULL_FACE); + checkGLError("begin() -- enabled cull face"); + glCullFace(GL_FRONT); + checkGLError("begin() -- set cullface to front");*/ + /*glEnable(GL_POLYGON_OFFSET_FILL); + checkGLError("begin() -- enabled polygon offset fill"); + glPolygonOffset(2.5f, 10.0f); + checkGLError("begin() -- set values for polygon offset");*/ + + checkGLError("begin() finished"); + + } + + void ShadowComponent::end(const RenderData& data) { + checkGLError("end() -- Flushing"); + //glFlush(); + if (_executeDepthTextureSave) { + saveDepthBuffer(); + _executeDepthTextureSave = false; + } + + // Restores Camera Parameters + Camera camera = data.camera; + camera.setPositionVec3(_cameraPos); + // JCC: We have aim and ancor nodes and position now. Need to fix this. + //camera.setFocusPositionVec3(_cameraFocus); + camera.setRotation(_cameraRotation); + + // Restores system state + glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); + checkGLError("end() -- Rebinding default FBO"); + glViewport( + _mViewport[0], + _mViewport[1], + _mViewport[2], + _mViewport[3] + ); + + if (_faceCulling) { + glEnable(GL_CULL_FACE); + glCullFace(_faceToCull); + } + else { + glDisable(GL_CULL_FACE); + } + + if (_depthIsEnabled) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + + glDepthFunc(_depthFunction); + + if (_polygonOffSet) { + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(_polygonOffSetFactor, _polygonOffSetUnits); + } + else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + + glClearColor( + _colorClearValue[0], + _colorClearValue[1], + _colorClearValue[2], + _colorClearValue[3] + ); + glClearDepth(_depthClearValue); + + if (_blendIsEnabled) { + glEnable(GL_BLEND); + } + + checkGLError("end() finished"); + } + + void ShadowComponent::update(const UpdateData& /*data*/) { + _sunPosition = global::renderEngine.scene()->sceneGraphNode("Sun")->worldPosition(); + } + + void ShadowComponent::createDepthTexture() { + checkGLError("createDepthTexture() -- Starting configuration"); + glGenTextures(1, &_shadowDepthTexture); + glBindTexture(GL_TEXTURE_2D, _shadowDepthTexture); + glTexStorage2D( + GL_TEXTURE_2D, + 1, + GL_DEPTH_COMPONENT32F, + _shadowDepthTextureWidth, + _shadowDepthTextureHeight + ); + + /*glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_DEPTH_COMPONENT32F, + _shadowDepthTextureWidth, + _shadowDepthTextureHeight, + 0, + GL_DEPTH_COMPONENT, + GL_FLOAT, + 0 + );*/ + checkGLError("createDepthTexture() -- Depth testure created"); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, shadowBorder); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + checkGLError("createdDepthTexture"); + + glGenTextures(1, &_positionInLightSpaceTexture); + glBindTexture(GL_TEXTURE_2D, _positionInLightSpaceTexture); + //glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB32F, + _shadowDepthTextureWidth, + _shadowDepthTextureHeight, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); + checkGLError("createDepthTexture() -- Position/Distance buffer created"); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + checkGLError("createdPositionTexture"); + + glBindTexture(GL_TEXTURE_2D, 0); + + _shadowData.shadowDepthTexture = _shadowDepthTexture; + _shadowData.positionInLightSpaceTexture = _positionInLightSpaceTexture; + } + + void ShadowComponent::createShadowFBO() { + // Saves current FBO first + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO); + + /*GLint _mViewport[4]; + glGetIntegerv(GL_VIEWPORT, _mViewport);*/ + + glGenFramebuffers(1, &_shadowFBO); + glBindFramebuffer(GL_FRAMEBUFFER, _shadowFBO); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + _shadowDepthTexture, + 0 + ); + + glFramebufferTexture( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT3, + _positionInLightSpaceTexture, + 0 + ); + checkGLError("createShadowFBO() -- Created Shadow Framebuffer"); + //GLenum drawBuffers[] = { GL_NONE }; + GLenum drawBuffers[] = { GL_NONE, GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT3 }; + glDrawBuffers(4, drawBuffers); + + checkFrameBufferState("createShadowFBO()"); + + // Restores system state + glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); + /*glViewport( + _mViewport[0], + _mViewport[1], + _mViewport[2], + _mViewport[3] + );*/ + checkGLError("createShadowFBO() -- createdShadowFBO"); + } + + void ShadowComponent::saveDepthBuffer() { + int size = _shadowDepthTextureWidth * _shadowDepthTextureHeight; + GLubyte * buffer = new GLubyte[size]; + + glReadPixels( + 0, + 0, + _shadowDepthTextureWidth, + _shadowDepthTextureHeight, + GL_DEPTH_COMPONENT, + GL_UNSIGNED_BYTE, + buffer + ); + + checkGLError("readDepthBuffer To buffer"); + std::fstream ppmFile; + + ppmFile.open("depthBufferShadowMapping.ppm", std::fstream::out); + if (ppmFile.is_open()) { + + ppmFile << "P3" << std::endl; + ppmFile << _shadowDepthTextureWidth << " " << _shadowDepthTextureHeight + << std::endl; + ppmFile << "255" << std::endl; + + std::cout << "\n\nSaving depth texture to file depthBufferShadowMapping.ppm\n\n"; + int k = 0; + for (int i = 0; i < _shadowDepthTextureWidth; i++) { + for (int j = 0; j < _shadowDepthTextureHeight; j++, k++) { + unsigned int val = static_cast(buffer[k]); + ppmFile << val << " " << val << " " << val << " "; + } + ppmFile << std::endl; + } + + ppmFile.close(); + + std::cout << "Texture saved to file depthBufferShadowMapping.ppm\n\n"; + } + + delete[] buffer; + + GLfloat * bBuffer = new GLfloat[size * 4]; + + glReadBuffer(GL_COLOR_ATTACHMENT3); + glReadPixels( + 0, + 0, + _shadowDepthTextureWidth, + _shadowDepthTextureHeight, + GL_RGBA, + GL_FLOAT, + bBuffer + ); + + checkGLError("readPositionBuffer To buffer"); + ppmFile.clear(); + + ppmFile.open("positionBufferShadowMapping.ppm", std::fstream::out); + if (ppmFile.is_open()) { + + ppmFile << "P3" << std::endl; + ppmFile << _shadowDepthTextureWidth << " " << _shadowDepthTextureHeight + << std::endl; + ppmFile << "255" << std::endl; + + std::cout << "\n\nSaving texture position to positionBufferShadowMapping.ppm\n\n"; + + float biggestValue = 0.f; + + int k = 0; + for (int i = 0; i < _shadowDepthTextureWidth; i++) { + for (int j = 0; j < _shadowDepthTextureHeight; j++) { + biggestValue = bBuffer[k] > biggestValue ? + bBuffer[k] : biggestValue; + k += 4; + } + } + + biggestValue /= 255.f; + + k = 0; + for (int i = 0; i < _shadowDepthTextureWidth; i++) { + for (int j = 0; j < _shadowDepthTextureHeight; j++) { + ppmFile << static_cast(bBuffer[k] / biggestValue) << " " + << static_cast(bBuffer[k + 1] / biggestValue) << " " + << static_cast(bBuffer[k + 2] / biggestValue) << " "; + k += 4; + } + ppmFile << std::endl; + } + + ppmFile.close(); + + std::cout << "Texture saved to file positionBufferShadowMapping.ppm\n\n"; + } + + delete[] bBuffer; + } + + void ShadowComponent::checkGLError(const std::string & where) const { + const GLenum error = glGetError(); + switch (error) { + case GL_NO_ERROR: + break; + case GL_INVALID_ENUM: + LERRORC( + "OpenGL Invalid State", + fmt::format("Function {}: GL_INVALID_ENUM", where) + ); + break; + case GL_INVALID_VALUE: + LERRORC( + "OpenGL Invalid State", + fmt::format("Function {}: GL_INVALID_VALUE", where) + ); + break; + case GL_INVALID_OPERATION: + LERRORC( + "OpenGL Invalid State", + fmt::format( + "Function {}: GL_INVALID_OPERATION", where + )); + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + LERRORC( + "OpenGL Invalid State", + fmt::format( + "Function {}: GL_INVALID_FRAMEBUFFER_OPERATION", + where + ) + ); + break; + case GL_OUT_OF_MEMORY: + LERRORC( + "OpenGL Invalid State", + fmt::format("Function {}: GL_OUT_OF_MEMORY", where) + ); + break; + default: + LERRORC( + "OpenGL Invalid State", + fmt::format("Unknown error code: {0:x}", static_cast(error)) + ); + } + } + + bool ShadowComponent::isEnabled() const { + return _enabled; + } + + ShadowComponent::ShadowMapData ShadowComponent::shadowMapData() const { + return _shadowData; + } +} // namespace openspace diff --git a/modules/globebrowsing/src/shadowcomponent.h b/modules/globebrowsing/src/shadowcomponent.h new file mode 100644 index 0000000000..95208c59b9 --- /dev/null +++ b/modules/globebrowsing/src/shadowcomponent.h @@ -0,0 +1,160 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2018 * +* * +* 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_GLOBEBROWSING___SHADOWCOMPONENT___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___SHADOWCOMPONENT___H__ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace ghoul { + class Dictionary; +} + +namespace ghoul::filesystem { class File; } + +namespace ghoul::opengl { + class ProgramObject; +} // namespace ghoul::opengl + +namespace openspace { + struct RenderData; + struct UpdateData; + + namespace documentation { struct Documentation; } + + static const GLfloat shadowBorder[] = { 1.f, 1.f, 1.f, 1.f }; + + class ShadowComponent : public properties::PropertyOwner { + public: + struct ShadowMapData { + glm::dmat4 shadowMatrix; + // temp + glm::dmat4 worldToLightSpaceMatrix; + GLuint shadowDepthTexture; + GLuint positionInLightSpaceTexture; + }; + public: + ShadowComponent(const ghoul::Dictionary& dictionary); + + void initialize(); + void initializeGL(); + void deinitializeGL(); + //bool deinitialize(); + + bool isReady() const; + + void begin(const RenderData& data); + void end(const RenderData& data); + void update(const UpdateData& data); + + static documentation::Documentation Documentation(); + + bool isEnabled() const; + + ShadowComponent::ShadowMapData shadowMapData() const; + + private: + void createDepthTexture(); + void createShadowFBO(); + + // Debug + void saveDepthBuffer(); + void checkGLError(const std::string & where) const; + + private: + + ShadowMapData _shadowData; + + // Texture coords in [0, 1], while clip coords in [-1, 1] + const glm::dmat4 _toTextureCoordsMatrix = glm::dmat4( + glm::dvec4(0.5, 0.0, 0.0, 0.0), + glm::dvec4(0.0, 0.5, 0.0, 0.0), + glm::dvec4(0.0, 0.0, 0.5, 0.0), + glm::dvec4(0.5, 0.5, 0.5, 1.0) + ); + + // DEBUG + properties::TriggerProperty _saveDepthTexture; + properties::IntProperty _distanceFraction; + properties::BoolProperty _enabled; + + ghoul::Dictionary _shadowMapDictionary; + + int _shadowDepthTextureHeight; + int _shadowDepthTextureWidth; + + GLuint _shadowDepthTexture; + GLuint _positionInLightSpaceTexture; + GLuint _shadowFBO; + GLuint _firstPassSubroutine; + GLuint _secondPassSubroutine; + GLint _defaultFBO; + GLint _mViewport[4]; + + GLboolean _faceCulling; + GLboolean _polygonOffSet; + GLboolean _depthIsEnabled; + GLboolean _blendIsEnabled = false; + + GLenum _faceToCull; + GLenum _depthFunction; + + GLfloat _polygonOffSetFactor; + GLfloat _polygonOffSetUnits; + GLfloat _colorClearValue[4]; + GLfloat _depthClearValue; + + glm::vec3 _sunPosition; + + glm::dmat4 _shadowMatrix; + + glm::dvec3 _cameraPos; + glm::dvec3 _cameraFocus; + glm::dquat _cameraRotation; + + std::stringstream _serializedCamera; + + // DEBUG + bool _executeDepthTextureSave; + + }; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___SHADOWCOMPONENT___H__