From 2aa540a112c8af1164000e16e88b640021c7fe99 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 26 Apr 2021 13:13:36 +0200 Subject: [PATCH] Feature/interactionsphere (#1561) * Add ability to render the bounding sphere as a debug option * Separate boundingsphere and interactionspheres * Correctly compute BoundingSpheres for more renderables (RenderablePlanesCloud, RenderableOrbitalKepler) --- .../planets/earth/satellites/misc/iss.asset | 2 +- .../earth/satellites/weather/aqua.asset | 2 +- .../earth/satellites/weather/snpp.asset | 2 +- .../earth/satellites/weather/terra.asset | 2 +- include/openspace/rendering/helper.h | 15 +- include/openspace/rendering/renderable.h | 23 ++- include/openspace/scene/scenegraphnode.h | 13 +- include/openspace/util/updatestructures.h | 2 +- .../rendering/renderableatmosphere.cpp | 2 + modules/base/rendering/renderablesphere.cpp | 6 +- .../rendering/renderablebillboardscloud.cpp | 87 +++------ .../rendering/renderablebillboardscloud.h | 1 + .../rendering/renderableplanescloud.cpp | 74 +++----- .../rendering/renderableplanescloud.h | 1 + modules/globebrowsing/src/renderableglobe.cpp | 17 +- modules/globebrowsing/src/ringscomponent.cpp | 4 + modules/globebrowsing/src/ringscomponent.h | 1 + .../rendering/renderablehabitablezone.cpp | 1 + .../rendering/renderableorbitalkepler.cpp | 8 + modules/space/rendering/renderablestars.cpp | 35 +++- modules/touch/src/directinputsolver.cpp | 2 +- modules/touch/src/touchinteraction.cpp | 18 +- shaders/core/xyzuvrgba_fs.glsl | 54 ++++++ shaders/core/xyzuvrgba_vs.glsl | 43 +++++ src/engine/openspaceengine.cpp | 5 +- src/interaction/orbitalnavigator.cpp | 4 +- src/rendering/helper.cpp | 145 ++++++++++++-- src/rendering/renderable.cpp | 18 +- src/rendering/renderengine.cpp | 12 -- src/scene/scenegraphnode.cpp | 177 +++++++++++++++--- 30 files changed, 566 insertions(+), 210 deletions(-) create mode 100644 shaders/core/xyzuvrgba_fs.glsl create mode 100644 shaders/core/xyzuvrgba_vs.glsl diff --git a/data/assets/scene/solarsystem/planets/earth/satellites/misc/iss.asset b/data/assets/scene/solarsystem/planets/earth/satellites/misc/iss.asset index a21b3bd1a2..eee82d3659 100644 --- a/data/assets/scene/solarsystem/planets/earth/satellites/misc/iss.asset +++ b/data/assets/scene/solarsystem/planets/earth/satellites/misc/iss.asset @@ -24,7 +24,7 @@ local initializeAndAddNodes = function() local iss = { Identifier = "ISS", Parent = transforms.EarthInertial.Identifier, - BoundingSphere = 30, + InteractionSphere = 30, Transform = { Translation = { Type = "TLETranslation", diff --git a/data/assets/scene/solarsystem/planets/earth/satellites/weather/aqua.asset b/data/assets/scene/solarsystem/planets/earth/satellites/weather/aqua.asset index 9ebe43df49..a056478bdf 100644 --- a/data/assets/scene/solarsystem/planets/earth/satellites/weather/aqua.asset +++ b/data/assets/scene/solarsystem/planets/earth/satellites/weather/aqua.asset @@ -19,7 +19,7 @@ asset.onInitialize(function () local Aqua = { Identifier = "Aqua", Parent = transforms.EarthInertial.Identifier, - BoundingSphere = 30, + InteractionSphere = 30, Transform = { Translation = { Type = "TLETranslation", diff --git a/data/assets/scene/solarsystem/planets/earth/satellites/weather/snpp.asset b/data/assets/scene/solarsystem/planets/earth/satellites/weather/snpp.asset index 2b251d229d..eeec482047 100644 --- a/data/assets/scene/solarsystem/planets/earth/satellites/weather/snpp.asset +++ b/data/assets/scene/solarsystem/planets/earth/satellites/weather/snpp.asset @@ -19,7 +19,7 @@ asset.onInitialize(function () local SNPP = { Identifier = "SNPP", Parent = transforms.EarthInertial.Identifier, - BoundingSphere = 30, + InteractionSphere = 30, Transform = { Translation = { Type = "TLETranslation", diff --git a/data/assets/scene/solarsystem/planets/earth/satellites/weather/terra.asset b/data/assets/scene/solarsystem/planets/earth/satellites/weather/terra.asset index 3d064550ee..8b130ebf9b 100644 --- a/data/assets/scene/solarsystem/planets/earth/satellites/weather/terra.asset +++ b/data/assets/scene/solarsystem/planets/earth/satellites/weather/terra.asset @@ -19,7 +19,7 @@ asset.onInitialize(function () local Terra = { Identifier = "Terra", Parent = transforms.EarthInertial.Identifier, - BoundingSphere = 30, + InteractionSphere = 30, Transform = { Translation = { Type = "TLETranslation", diff --git a/include/openspace/rendering/helper.h b/include/openspace/rendering/helper.h index f82fabe5a0..51498da96c 100644 --- a/include/openspace/rendering/helper.h +++ b/include/openspace/rendering/helper.h @@ -61,12 +61,12 @@ void renderBox(const glm::vec2& position, const glm::vec2& size, const glm::vec4 struct Shaders { struct { std::unique_ptr program; - UniformCache(tex, hasTexture, shouldFlipTexture, ortho, color) cache; + UniformCache(tex, hasTexture, shouldFlipTexture, proj, color) cache; } xyuvrgba; struct { std::unique_ptr program; - UniformCache(tex, hasTexture, shouldFlipTexture, ortho, color) cache; + UniformCache(tex, hasTexture, shouldFlipTexture, proj, color) cache; } screenfilling; }; @@ -76,6 +76,14 @@ struct VertexObjects { GLuint vbo; } square; + struct { + GLuint vao; + GLuint vbo; + GLuint ibo; + + int nElements = 64; + } sphere; + struct { GLuint vao; } empty; @@ -108,6 +116,9 @@ std::vector convert(std::vector v); std::vector createRing(int nSegments, float radius, glm::vec4 colors = glm::vec4(1.f)); +std::pair, std::vector> +createSphere(int nSegments, glm::vec3 radii, glm::vec4 colors = glm::vec4(1.f)); + } // namespace openspace::rendering::helper #endif // __OPENSPACE_CORE___HELPER___H__ diff --git a/include/openspace/rendering/renderable.h b/include/openspace/rendering/renderable.h index 3fa96880d4..ca8d52c0e3 100644 --- a/include/openspace/rendering/renderable.h +++ b/include/openspace/rendering/renderable.h @@ -31,6 +31,7 @@ #include #include #include +#include #include namespace ghoul { class Dictionary; } @@ -75,11 +76,17 @@ public: bool isEnabled() const; bool shouldUpdateIfDisabled() const; - void setBoundingSphere(double boundingSphere); double boundingSphere() const; + double interactionSphere() const; virtual void render(const RenderData& data, RendererTasks& rendererTask); virtual void update(const UpdateData& data); + + // The 'surface' in this case is the interaction sphere of this renderable. In some + // cases (i.e., planets) this corresponds directly to the physical surface, but in + // many cases, models, volumetric data, this will not. Regardless of what the physical + // representation is, the 'surface' is always the sphere around which interaction is + // handled virtual SurfacePositionHandle calculateSurfacePositionHandle( const glm::dvec3& targetModelSpace) const; @@ -98,15 +105,25 @@ public: protected: properties::BoolProperty _enabled; properties::FloatProperty _opacity; - properties::DoubleProperty _boundingSphere; properties::StringProperty _renderableType; - bool _shouldUpdateIfDisabled = false; + void setBoundingSphere(double boundingSphere); void setRenderBinFromOpacity(); void registerUpdateRenderBinFromOpacity(); + double _boundingSphere = 0.0; + double _interactionSphere = 0.0; + SceneGraphNode* _parent = nullptr; + bool _shouldUpdateIfDisabled = false; + private: + // We only want the SceneGraphNode to be able manipulate the parent, so we don't want + // to provide a set method for this. Otherwise, anyone might mess around with our + // parentage and that's no bueno + friend ghoul::mm_unique_ptr SceneGraphNode::createFromDictionary( + const ghoul::Dictionary&); + RenderBin _renderBin = RenderBin::Opaque; }; diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index 879a78a352..d83fda6315 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -36,14 +36,16 @@ #include #include #include +#include #include #include +#include #include -#include //#define Debugging_Core_SceneGraphNode_Indices namespace ghoul { class Dictionary; } +namespace ghoul::opengl { class ProgramObject; } namespace openspace { @@ -127,6 +129,7 @@ public: std::vector children() const; double boundingSphere() const; + double interactionSphere() const; SceneGraphNode* childNode(const std::string& identifier); @@ -143,6 +146,7 @@ private: glm::dmat3 calculateWorldRotation() const; glm::dvec3 calculateWorldScale() const; void computeScreenSpaceData(RenderData& newData); + void renderDebugSphere(const Camera& camera, double size, glm::vec4 color); std::atomic _state = State::Loaded; std::vector> _children; @@ -178,6 +182,7 @@ private: glm::dmat4 _modelTransformCached = glm::dmat4(1.0); properties::DoubleProperty _boundingSphere; + properties::DoubleProperty _interactionSphere; properties::BoolProperty _computeScreenSpaceValues; properties::IVec2Property _screenSpacePosition; properties::BoolProperty _screenVisibility; @@ -189,6 +194,12 @@ private: // are calculated when _computeScreenSpaceValues is true) std::chrono::high_resolution_clock::time_point _lastScreenSpaceUpdateTime; + properties::BoolProperty _showDebugSphere; + static ghoul::opengl::ProgramObject* _debugSphereProgram; + + std::optional _overrideBoundingSphere; + std::optional _overrideInteractionSphere; + #ifdef Debugging_Core_SceneGraphNode_Indices int index = 0; static int nextIndex; diff --git a/include/openspace/util/updatestructures.h b/include/openspace/util/updatestructures.h index 5ea1f1fe5b..00e1f57004 100644 --- a/include/openspace/util/updatestructures.h +++ b/include/openspace/util/updatestructures.h @@ -50,7 +50,7 @@ struct UpdateData { struct RenderData { const Camera& camera; const Time time; - int renderBinMask = -1; + int8_t renderBinMask = -1; TransformData modelTransform; }; diff --git a/modules/atmosphere/rendering/renderableatmosphere.cpp b/modules/atmosphere/rendering/renderableatmosphere.cpp index a2ca9476bf..875605a976 100644 --- a/modules/atmosphere/rendering/renderableatmosphere.cpp +++ b/modules/atmosphere/rendering/renderableatmosphere.cpp @@ -381,6 +381,8 @@ RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary) _hardShadowsEnabled.onChange(updateWithoutCalculation); addProperty(_hardShadowsEnabled); } + + setBoundingSphere(_planetRadius * 1000.0); } void RenderableAtmosphere::deinitializeGL() { diff --git a/modules/base/rendering/renderablesphere.cpp b/modules/base/rendering/renderablesphere.cpp index 4f20d37fc2..dc9248f3f1 100644 --- a/modules/base/rendering/renderablesphere.cpp +++ b/modules/base/rendering/renderablesphere.cpp @@ -215,8 +215,11 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary) } addProperty(_orientation); + _size.onChange([this]() { + setBoundingSphere(_size); + _sphereIsDirty = true; + }); addProperty(_size); - _size.onChange([this]() { _sphereIsDirty = true; }); addProperty(_segments); _segments.onChange([this]() { _sphereIsDirty = true; }); @@ -257,6 +260,7 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary) setRenderBin(Renderable::RenderBin::Background); } + setBoundingSphere(_size); setRenderBinFromOpacity(); } diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp index 01a28f5214..3da6f82553 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp @@ -767,31 +767,6 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data, const glm::dvec3& orthoUp, float fadeInVariable) { - float scale = 0.f; - switch (_unit) { - case Meter: - scale = 1.f; - break; - case Kilometer: - scale = 1e3f; - break; - case Parsec: - scale = static_cast(PARSEC); - break; - case Kiloparsec: - scale = static_cast(1e3 * PARSEC); - break; - case Megaparsec: - scale = static_cast(1e6 * PARSEC); - break; - case Gigaparsec: - scale = static_cast(1e9 * PARSEC); - break; - case GigalightYears: - scale = static_cast(306391534.73091 * PARSEC); - break; - } - glm::vec4 textColor = glm::vec4( glm::vec3(_textColor), _textOpacity * fadeInVariable @@ -813,7 +788,7 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data, for (const std::pair& pair : _labelData) { //glm::vec3 scaledPos(_transformationMatrix * glm::dvec4(pair.first, 1.0)); glm::vec3 scaledPos(pair.first); - scaledPos *= scale; + scaledPos *= unitToMeter(_unit); ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render( *_font, scaledPos, @@ -825,36 +800,11 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data, } void RenderableBillboardsCloud::render(const RenderData& data, RendererTasks&) { - float scale = 0.f; - switch (_unit) { - case Meter: - scale = 1.f; - break; - case Kilometer: - scale = 1e3f; - break; - case Parsec: - scale = static_cast(PARSEC); - break; - case Kiloparsec: - scale = static_cast(1e3 * PARSEC); - break; - case Megaparsec: - scale = static_cast(1e6 * PARSEC); - break; - case Gigaparsec: - scale = static_cast(1e9 * PARSEC); - break; - case GigalightYears: - scale = static_cast(306391534.73091 * PARSEC); - break; - } - float fadeInVariable = 1.f; if (!_disableFadeInDistance) { float distCamera = static_cast(glm::length(data.camera.positionVec3())); const glm::vec2 fadeRange = _fadeInDistance; - const float a = 1.f / ((fadeRange.y - fadeRange.x) * scale); + const float a = 1.f / ((fadeRange.y - fadeRange.x) * unitToMeter(_unit)); const float b = -(fadeRange.x / (fadeRange.y - fadeRange.x)); const float funcValue = a * distCamera + b; fadeInVariable *= funcValue > 1.f ? 1.f : funcValue; @@ -1488,6 +1438,19 @@ bool RenderableBillboardsCloud::saveCachedFile(const std::string& file) const { return fileStream.good(); } +double RenderableBillboardsCloud::unitToMeter(Unit unit) const { + switch (_unit) { + case Meter: return 1.0; + case Kilometer: return 1e3; + case Parsec: return PARSEC; + case Kiloparsec: return 1000 * PARSEC; + case Megaparsec: return 1e6 * PARSEC; + case Gigaparsec: return 1e9 * PARSEC; + case GigalightYears: return 306391534.73091 * PARSEC; + default: throw ghoul::MissingCaseException(); + } +} + void RenderableBillboardsCloud::createDataSlice() { ZoneScoped @@ -1516,7 +1479,7 @@ void RenderableBillboardsCloud::createDataSlice() { _slicedData.push_back(_fullData[i + 3 + datavarInUse]); }; - auto addPosition = [&](const glm::vec4 &pos) { + auto addPosition = [&](const glm::vec4& pos) { for (int j = 0; j < 4; ++j) { _slicedData.push_back(pos[j]); } @@ -1531,17 +1494,24 @@ void RenderableBillboardsCloud::createDataSlice() { minColorIdx = colorIdx < minColorIdx ? colorIdx : minColorIdx; } + double maxRadius = 0.0; + float biggestCoord = -1.f; for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) { - glm::dvec4 transformedPos = _transformationMatrix * glm::dvec4( + glm::vec3 transformedPos = glm::vec3(_transformationMatrix * glm::vec4( _fullData[i + 0], _fullData[i + 1], _fullData[i + 2], 1.0 - ); - // W-normalization - transformedPos /= transformedPos.w; - glm::vec4 position(glm::vec3(transformedPos), static_cast(_unit)); + )); + glm::vec4 position(transformedPos, static_cast(_unit)); + + const double unitMeter = unitToMeter(_unit); + glm::dvec3 p = glm::dvec3(position) * unitMeter; + const double r = glm::length(p); + if (r > maxRadius) { + maxRadius = r; + } if (_hasColorMapFile) { for (int j = 0; j < 4; ++j) { @@ -1623,6 +1593,7 @@ void RenderableBillboardsCloud::createDataSlice() { addPosition(position); } } + setBoundingSphere(maxRadius); _fadeInDistance.setMaxValue(glm::vec2(10.f * biggestCoord)); } diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.h b/modules/digitaluniverse/rendering/renderablebillboardscloud.h index 8bbbf556fc..4f8d9a45f0 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.h +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.h @@ -76,6 +76,7 @@ private: Gigaparsec = 5, GigalightYears = 6 }; + double unitToMeter(Unit unit) const; void createDataSlice(); void createPolygonTexture(); diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.cpp b/modules/digitaluniverse/rendering/renderableplanescloud.cpp index b190a78757..7d754f9308 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.cpp +++ b/modules/digitaluniverse/rendering/renderableplanescloud.cpp @@ -572,35 +572,12 @@ void RenderablePlanesCloud::renderLabels(const RenderData& data, } void RenderablePlanesCloud::render(const RenderData& data, RendererTasks&) { - float scale = 0.f; - switch (_unit) { - case Meter: - scale = 1.f; - break; - case Kilometer: - scale = 1e3f; - break; - case Parsec: - scale = static_cast(PARSEC); - break; - case Kiloparsec: - scale = static_cast(1e3 * PARSEC); - break; - case Megaparsec: - scale = static_cast(1e6 * PARSEC); - break; - case Gigaparsec: - scale = static_cast(1e9 * PARSEC); - break; - case GigalightYears: - scale = static_cast(306391534.73091 * PARSEC); - break; - } + const double scale = unitToMeter(_unit); float fadeInVariable = 1.f; if (!_disableFadeInDistance) { float distCamera = static_cast(glm::length(data.camera.positionVec3())); - distCamera /= scale; + distCamera = static_cast(distCamera / scale); const glm::vec2 fadeRange = _fadeInDistance; //const float a = 1.f / ((fadeRange.y - fadeRange.x) * scale); const float a = 1.f / ((fadeRange.y - fadeRange.x)); @@ -1095,16 +1072,37 @@ bool RenderablePlanesCloud::saveCachedFile(const std::string& file) const { } } +double RenderablePlanesCloud::unitToMeter(Unit unit) const { + switch (_unit) { + case Meter: return 1.0; + case Kilometer: return 1e3; + case Parsec: return PARSEC; + case Kiloparsec: return 1000 * PARSEC; + case Megaparsec: return 1e6 * PARSEC; + case Gigaparsec: return 1e9 * PARSEC; + case GigalightYears: return 306391534.73091 * PARSEC; + default: throw ghoul::MissingCaseException(); + } +} + void RenderablePlanesCloud::createPlanes() { if (_dataIsDirty && _hasSpeckFile) { + const double scale = unitToMeter(_unit); + LDEBUG("Creating planes..."); float maxSize = 0.f; + double maxRadius = 0.0; for (size_t p = 0; p < _fullData.size(); p += _nValuesPerAstronomicalObject) { const glm::vec4 transformedPos = glm::vec4( _transformationMatrix * glm::dvec4(_fullData[p + 0], _fullData[p + 1], _fullData[p + 2], 1.0) ); + const double r = glm::length(glm::dvec3(transformedPos) * scale); + if (r > maxRadius) { + maxRadius = r; + } + // Plane vectors u and v glm::vec4 u = glm::vec4( _transformationMatrix * @@ -1145,31 +1143,6 @@ void RenderablePlanesCloud::createPlanes() { glm::vec4 vertex2 = transformedPos - u + v; glm::vec4 vertex4 = transformedPos + u - v; - float scale = 0.f; - switch (_unit) { - case Meter: - scale = 1.f; - break; - case Kilometer: - scale = 1e3f; - break; - case Parsec: - scale = static_cast(PARSEC); - break; - case Kiloparsec: - scale = static_cast(1e3 * PARSEC); - break; - case Megaparsec: - scale = static_cast(1e6 * PARSEC); - break; - case Gigaparsec: - scale = static_cast(1e9 * PARSEC); - break; - case GigalightYears: - scale = static_cast(306391534.73091 * PARSEC); - break; - } - for (int i = 0; i < 3; ++i) { maxSize = std::max(maxSize, vertex0[i]); maxSize = std::max(maxSize, vertex1[i]); @@ -1252,6 +1225,7 @@ void RenderablePlanesCloud::createPlanes() { _dataIsDirty = false; + setBoundingSphere(maxRadius * _scaleFactor); _fadeInDistance.setMaxValue(glm::vec2(10.f * maxSize)); } diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.h b/modules/digitaluniverse/rendering/renderableplanescloud.h index 2180769f03..9123200e5c 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.h +++ b/modules/digitaluniverse/rendering/renderableplanescloud.h @@ -80,6 +80,7 @@ private: Gigaparsec = 5, GigalightYears = 6 }; + double unitToMeter(Unit unit) const; struct PlaneAggregate { int textureIndex; diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index c0813352a8..024ec67996 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -544,12 +544,12 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) if (p.radii.has_value()) { if (std::holds_alternative(*p.radii)) { _ellipsoid = Ellipsoid(std::get(*p.radii)); - setBoundingSphere(static_cast(_ellipsoid.maximumRadius())); + setBoundingSphere(_ellipsoid.maximumRadius()); } else if (std::holds_alternative(*p.radii)) { const double radius = std::get(*p.radii); _ellipsoid = Ellipsoid({ radius, radius, radius }); - setBoundingSphere(static_cast(_ellipsoid.maximumRadius())); + setBoundingSphere(_ellipsoid.maximumRadius()); } else { throw ghoul::MissingCaseException(); @@ -843,9 +843,14 @@ void RenderableGlobe::update(const UpdateData& data) { ); } - setBoundingSphere(static_cast( - _ellipsoid.maximumRadius() * glm::compMax(data.modelTransform.scale) - )); + double bs = _ellipsoid.maximumRadius() * glm::compMax(data.modelTransform.scale); + if (_hasRings) { + const double ringSize = _ringsComponent.size(); + if (ringSize > bs) { + bs = ringSize; + } + } + setBoundingSphere(bs); glm::dmat4 translation = glm::translate(glm::dmat4(1.0), data.modelTransform.translation); @@ -1831,7 +1836,7 @@ SurfacePositionHandle RenderableGlobe::calculateSurfacePositionHandle( double heightToSurface = getHeight(targetModelSpace); heightToSurface = glm::isnan(heightToSurface) ? 0.0 : heightToSurface; centerToEllipsoidSurface = glm::isnan(glm::length(centerToEllipsoidSurface)) ? - (glm::dvec3(0.0, 1.0, 0.0) * static_cast(boundingSphere())) : + (glm::dvec3(0.0, 1.0, 0.0) * interactionSphere()) : centerToEllipsoidSurface; ellipsoidSurfaceOutDirection = glm::isnan(glm::length(ellipsoidSurfaceOutDirection)) ? glm::dvec3(0.0, 1.0, 0.0) : ellipsoidSurfaceOutDirection; diff --git a/modules/globebrowsing/src/ringscomponent.cpp b/modules/globebrowsing/src/ringscomponent.cpp index 6f0855196e..68c44f6677 100644 --- a/modules/globebrowsing/src/ringscomponent.cpp +++ b/modules/globebrowsing/src/ringscomponent.cpp @@ -866,4 +866,8 @@ bool RingsComponent::isEnabled() const { return _enabled; } +double RingsComponent::size() const { + return _size; +} + } // namespace openspace diff --git a/modules/globebrowsing/src/ringscomponent.h b/modules/globebrowsing/src/ringscomponent.h index a11ef59b18..c19a8565b9 100644 --- a/modules/globebrowsing/src/ringscomponent.h +++ b/modules/globebrowsing/src/ringscomponent.h @@ -71,6 +71,7 @@ public: static documentation::Documentation Documentation(); bool isEnabled() const; + double size() const; private: void loadTexture(); diff --git a/modules/space/rendering/renderablehabitablezone.cpp b/modules/space/rendering/renderablehabitablezone.cpp index 274fff4b96..d540d75906 100644 --- a/modules/space/rendering/renderablehabitablezone.cpp +++ b/modules/space/rendering/renderablehabitablezone.cpp @@ -143,6 +143,7 @@ RenderableHabitableZone::RenderableHabitableZone(const ghoul::Dictionary& dictio _width.setReadOnly(true); computeZone(); + setBoundingSphere(_size); } void RenderableHabitableZone::render(const RenderData& data, RendererTasks&) { diff --git a/modules/space/rendering/renderableorbitalkepler.cpp b/modules/space/rendering/renderableorbitalkepler.cpp index 291f8daa1b..040354458d 100644 --- a/modules/space/rendering/renderableorbitalkepler.cpp +++ b/modules/space/rendering/renderableorbitalkepler.cpp @@ -476,6 +476,14 @@ void RenderableOrbitalKepler::initializeGL() { _uniformCache.opacity = _programObject->uniformLocation("opacity"); updateBuffers(); + + double maxSemiMajorAxis = 0.0; + for (const KeplerParameters& kp : _data) { + if (kp.semiMajorAxis > maxSemiMajorAxis) { + maxSemiMajorAxis = kp.semiMajorAxis; + } + } + setBoundingSphere(maxSemiMajorAxis * 1000); } void RenderableOrbitalKepler::deinitializeGL() { diff --git a/modules/space/rendering/renderablestars.cpp b/modules/space/rendering/renderablestars.cpp index 1ce1c04748..eefa4ba887 100644 --- a/modules/space/rendering/renderablestars.cpp +++ b/modules/space/rendering/renderablestars.cpp @@ -1486,14 +1486,21 @@ void RenderableStars::createDataSlice(ColorOption option) { -std::numeric_limits::max() ); + double maxRadius = 0.0; + for (size_t i = 0; i < _fullData.size(); i += _nValuesPerStar) { - glm::vec3 position = glm::vec3( + glm::dvec3 position = glm::dvec3( _fullData[i + 0], _fullData[i + 1], _fullData[i + 2] ); position *= openspace::distanceconstants::Parsec; + const double r = glm::length(position); + if (r > maxRadius) { + maxRadius = r; + } + switch (option) { case ColorOption::Color: case ColorOption::FixedColor: @@ -1503,7 +1510,11 @@ void RenderableStars::createDataSlice(ColorOption option) { std::array data; } layout; - layout.value.position = { { position[0], position[1], position[2] } }; + layout.value.position = { { + static_cast(position[0]), + static_cast(position[1]), + static_cast(position[2]) + }}; if (_enableTestGrid) { float sunColor = 0.650f; @@ -1531,7 +1542,11 @@ void RenderableStars::createDataSlice(ColorOption option) { std::array data; } layout; - layout.value.position = { { position[0], position[1], position[2] } }; + layout.value.position = {{ + static_cast(position[0]), + static_cast(position[1]), + static_cast(position[2]) + }}; layout.value.value = _fullData[i + _bvColorArrayPos]; layout.value.luminance = _fullData[i + _lumArrayPos]; @@ -1556,7 +1571,11 @@ void RenderableStars::createDataSlice(ColorOption option) { std::array data; } layout; - layout.value.position = { { position[0], position[1], position[2] } }; + layout.value.position = {{ + static_cast(position[0]), + static_cast(position[1]), + static_cast(position[2]) + }}; layout.value.value = _fullData[i + _bvColorArrayPos]; layout.value.luminance = _fullData[i + _lumArrayPos]; @@ -1579,7 +1598,11 @@ void RenderableStars::createDataSlice(ColorOption option) { std::array data; } layout = {}; - layout.value.position = { { position[0], position[1], position[2] } }; + layout.value.position = {{ + static_cast(position[0]), + static_cast(position[1]), + static_cast(position[2]) + }}; int index = _otherDataOption.value(); // plus 3 because of the position @@ -1612,6 +1635,8 @@ void RenderableStars::createDataSlice(ColorOption option) { } } } + + setBoundingSphere(maxRadius); } } // namespace openspace diff --git a/modules/touch/src/directinputsolver.cpp b/modules/touch/src/directinputsolver.cpp index 5f19cb5d9f..7a0ea127ae 100644 --- a/modules/touch/src/directinputsolver.cpp +++ b/modules/touch/src/directinputsolver.cpp @@ -146,7 +146,7 @@ void gradient(double* g, double* par, int x, void* fdata, LMstat* lmstat) { FunctionData* ptr = reinterpret_cast(fdata); double f0 = distToMinimize(par, x, fdata, lmstat); // scale value to find minimum step size h, dependant on planet size - double scale = log10(ptr->node->boundingSphere()); + double scale = log10(ptr->node->interactionSphere()); std::vector dPar(ptr->nDOF, 0.0); dPar.assign(par, par + ptr->nDOF); diff --git a/modules/touch/src/touchinteraction.cpp b/modules/touch/src/touchinteraction.cpp index 9eededbcbf..cae7df51d4 100644 --- a/modules/touch/src/touchinteraction.cpp +++ b/modules/touch/src/touchinteraction.cpp @@ -507,15 +507,15 @@ void TouchInteraction::findSelectedNode(const std::vector& lis size_t id = inputHolder.fingerId(); for (SceneGraphNode* node : selectableNodes) { - double boundingSphereSquared = static_cast(node->boundingSphere()) * - static_cast(node->boundingSphere()); + double interactionSphereSquared = + node->interactionSphere() * node->interactionSphere(); glm::dvec3 camToSelectable = node->worldPosition() - camPos; double intersectionDist = 0.0; const bool intersected = glm::intersectRaySphere( camPos, raytrace, node->worldPosition(), - boundingSphereSquared, + interactionSphereSquared, intersectionDist ); if (intersected) { @@ -921,7 +921,7 @@ double TouchInteraction::computeTapZoomDistance(double zoomGain) { global::navigationHandler->orbitalNavigator().anchorNode()->worldPosition() ); - dist -= anchor->boundingSphere(); + dist -= anchor->interactionSphere(); double newVelocity = dist * _tapZoomFactor; newVelocity *= std::max(_touchScreenSize.value() * 0.1, 1.0); @@ -962,9 +962,9 @@ void TouchInteraction::step(double dt, bool directTouch) { dquat globalCamRot = normalize(quat_cast(inverse(lookAtMat))); dquat localCamRot = inverse(globalCamRot) * _camera->rotationQuaternion(); - const double boundingSphere = anchor->boundingSphere(); - const double distance = std::max(length(centerToCamera) - boundingSphere, 0.0); - _currentRadius = boundingSphere / + const double interactionSphere = anchor->interactionSphere(); + const double distance = std::max(length(centerToCamera) - interactionSphere, 0.0); + _currentRadius = interactionSphere / std::max(distance * _projectionScaleFactor, 1.0); { @@ -1010,7 +1010,7 @@ void TouchInteraction::step(double dt, bool directTouch) { // This is a rough estimate of the node surface // If nobody has set another zoom in limit, use this as default zoom in bounds - double zoomInBounds = boundingSphere * _zoomBoundarySphereMultiplier; + double zoomInBounds = interactionSphere * _zoomBoundarySphereMultiplier; bool isZoomInLimitSet = (_zoomInLimit.value() >= 0.0); if (isZoomInLimitSet && _zoomInLimit.value() < zoomInBounds) { @@ -1049,7 +1049,7 @@ void TouchInteraction::step(double dt, bool directTouch) { double zoomVelocity = _vel.zoom; if (!directTouch) { const double distanceFromSurface = - length(currentPosDistance) - anchor->boundingSphere(); + length(currentPosDistance) - anchor->interactionSphere(); if (distanceFromSurface > 0.1) { const double ratioOfDistanceToNodeVsSurf = length(currentPosDistance) / distanceFromSurface; diff --git a/shaders/core/xyzuvrgba_fs.glsl b/shaders/core/xyzuvrgba_fs.glsl new file mode 100644 index 0000000000..e26b92ca4c --- /dev/null +++ b/shaders/core/xyzuvrgba_fs.glsl @@ -0,0 +1,54 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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" + +uniform bool hasTexture = false; +uniform bvec2 shouldFlipTexture = bvec2(false, false); +uniform sampler2D tex; +uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0); + +in float depth; +in vec2 out_uv; +in vec4 out_color; + +Fragment getFragment() { + Fragment frag; + + if (hasTexture) { + vec2 uv = out_uv; + if (shouldFlipTexture.x) { + uv.x = 1.0 - uv.x; + } + if (shouldFlipTexture.y) { + uv.y = 1.0 - uv.y; + } + frag.color = out_color * color * texture(tex, uv); + } + else { + frag.color = out_color * color; + } + frag.depth = depth; + return frag; +} diff --git a/shaders/core/xyzuvrgba_vs.glsl b/shaders/core/xyzuvrgba_vs.glsl new file mode 100644 index 0000000000..9605a4c6d3 --- /dev/null +++ b/shaders/core/xyzuvrgba_vs.glsl @@ -0,0 +1,43 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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; +layout(location = 1) in vec2 in_uv; +layout(location = 2) in vec4 in_color; + +out float depth; +out vec2 out_uv; +out vec4 out_color; + +uniform mat4 proj; + +void main() { + out_uv = in_uv; + out_color = in_color; + vec4 p = proj * vec4(in_position, 1.0); + gl_Position = p; + depth = p.w; +} diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 2eca8e12f4..c04c60f985 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -416,8 +416,6 @@ void OpenSpaceEngine::initializeGL() { glbinding::Binding::initialize(global::windowDelegate->openGLProcedureAddress); //glbinding::Binding::useCurrentContext(); - rendering::helper::initialize(); - LDEBUG("Adding system components"); // Detect and log OpenCL and OpenGL versions and available devices SysCap.addComponent( @@ -427,7 +425,6 @@ void OpenSpaceEngine::initializeGL() { std::make_unique() ); - // @BUG: This will call OpenGL functions, should it should be in the initializeGL LDEBUG("Detecting capabilities"); SysCap.detectCapabilities(); @@ -467,6 +464,8 @@ void OpenSpaceEngine::initializeGL() { } } + rendering::helper::initialize(); + loadFonts(); _loadingScreen = std::make_unique( diff --git a/src/interaction/orbitalnavigator.cpp b/src/interaction/orbitalnavigator.cpp index 9c6d13a15d..b4815e9cc4 100644 --- a/src/interaction/orbitalnavigator.cpp +++ b/src/interaction/orbitalnavigator.cpp @@ -459,8 +459,8 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) { if (_applyLinearFlight) { // Calculate a position handle based on the camera position in world space glm::dvec3 camPosToAnchorPosDiff = prevCameraPosition - anchorPos; - // Use the boundingsphere to get an approximate distance to the node surface - double nodeRadius = static_cast(_anchorNode->boundingSphere()); + // Use the interaction sphere to get an approximate distance to the node surface + double nodeRadius = static_cast(_anchorNode->interactionSphere()); double distFromCameraToFocus = glm::distance(prevCameraPosition, anchorPos) - nodeRadius; diff --git a/src/rendering/helper.cpp b/src/rendering/helper.cpp index 99787c973c..43fa4fae94 100644 --- a/src/rendering/helper.cpp +++ b/src/rendering/helper.cpp @@ -53,17 +53,20 @@ layout(location = 0) in vec2 in_position; layout(location = 1) in vec2 in_uv; layout(location = 2) in vec4 in_color; +out float depth; out vec2 out_position; out vec2 out_uv; out vec4 out_color; -uniform mat4 ortho; +uniform mat4 proj; void main() { out_position = in_position; out_uv = in_uv; out_color = in_color; - gl_Position = ortho * vec4(in_position, 0.0, 1.0); + vec4 p = proj * vec4(in_position, 0.0, 1.0); + gl_Position = p; + depth = p.w; } )"; @@ -90,11 +93,14 @@ void main() { constexpr const char* XyuvrgbaFragmentCode = R"( #version __CONTEXT__ +#include "fragment.glsl" + uniform bool hasTexture = false; uniform bvec2 shouldFlipTexture = bvec2(false, false); uniform sampler2D tex; uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0); +in float depth; in vec2 out_uv; in vec4 out_color; @@ -119,6 +125,7 @@ void main() { } // namespace +#pragma optimize ("", off) namespace openspace::rendering::helper { namespace detail { @@ -150,7 +157,6 @@ void initialize() { vertexFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); vertexFile.open(xyuvrgbaVertexFile, std::fstream::out); vertexFile << XyuvrgbaVertexCode; - vertexFile.close(); } xyuvrgbaFragmentFile = absPath("${TEMPORARY}/xyuvrgba.frag"); @@ -159,13 +165,17 @@ void initialize() { fragmentFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); fragmentFile.open(xyuvrgbaFragmentFile, std::fstream::out); fragmentFile << XyuvrgbaFragmentCode; - fragmentFile.close(); } shaders.xyuvrgba.program = ghoul::opengl::ProgramObject::Build( - "xyuvrgba", xyuvrgbaVertexFile, xyuvrgbaFragmentFile); - ghoul::opengl::updateUniformLocations(*shaders.xyuvrgba.program, + "xyuvrgba", + xyuvrgbaVertexFile, + xyuvrgbaFragmentFile + ); + ghoul::opengl::updateUniformLocations( + *shaders.xyuvrgba.program, shaders.xyuvrgba.cache, - { "tex", "hasTexture", "shouldFlipTexture", "ortho", "color" }); + { "tex", "hasTexture", "shouldFlipTexture", "proj", "color" } + ); // // Screenfilling shader @@ -176,7 +186,6 @@ void initialize() { vertexFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); vertexFile.open(screenFillingVertexFile, std::fstream::out); vertexFile << ScreenFillingQuadVertexCode; - vertexFile.close(); } screenFillingFragmentFile = absPath("${TEMPORARY}/screenfilling.frag"); @@ -185,14 +194,18 @@ void initialize() { fragmentFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); fragmentFile.open(screenFillingFragmentFile, std::fstream::out); fragmentFile << XyuvrgbaFragmentCode; - fragmentFile.close(); } shaders.screenfilling.program = ghoul::opengl::ProgramObject::Build( - "screenfilling", xyuvrgbaVertexFile, xyuvrgbaFragmentFile); - ghoul::opengl::updateUniformLocations(*shaders.screenfilling.program, + "screenfilling", + xyuvrgbaVertexFile, + xyuvrgbaFragmentFile + ); + ghoul::opengl::updateUniformLocations( + *shaders.screenfilling.program, shaders.screenfilling.cache, - { "tex", "hasTexture", "shouldFlipTexture", "ortho", "color" }); + { "tex", "hasTexture", "shouldFlipTexture", "proj", "color" } + ); // @@ -231,6 +244,45 @@ void initialize() { reinterpret_cast(offsetof(VertexXYUVRGBA, rgba))); glBindVertexArray(0); + // + // Sphere vertex array object + // + std::pair, std::vector> sphereData = createSphere( + 64, glm::vec3(1.f, 1.f, 1.f), glm::vec4(1.f, 1.f, 1.f, 1.f) + ); + + glGenVertexArrays(1, &vertexObjects.sphere.vao); + glGenBuffers(1, &vertexObjects.sphere.vbo); + glGenBuffers(1, &vertexObjects.sphere.ibo); + + glBindVertexArray(vertexObjects.sphere.vao); + glBindBuffer(GL_ARRAY_BUFFER, vertexObjects.sphere.vbo); + glBufferData( + GL_ARRAY_BUFFER, + sphereData.first.size() * sizeof(Vertex), + sphereData.first.data(), + GL_STATIC_DRAW + ); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexObjects.sphere.ibo); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sphereData.second.size() * sizeof(GLushort), + sphereData.second.data(), + GL_STATIC_DRAW + ); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, uv))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), + reinterpret_cast(offsetof(Vertex, rgba))); + glBindVertexArray(0); + vertexObjects.sphere.nElements = static_cast(sphereData.second.size()); + + // // Empty vertex array objects // @@ -262,6 +314,10 @@ void deinitialize() { glDeleteVertexArrays(1, &vertexObjects.square.vao); glDeleteBuffers(1, &vertexObjects.square.vbo); + glDeleteVertexArrays(1, &vertexObjects.sphere.vao); + glDeleteBuffers(1, &vertexObjects.sphere.vbo); + glDeleteBuffers(1, &vertexObjects.sphere.ibo); + glDeleteVertexArrays(1, &vertexObjects.empty.vao); isInitialized = false; @@ -316,14 +372,14 @@ void renderBox(ghoul::opengl::ProgramObject& program, GLint orthoLocation, } void renderBox(const glm::vec2& position, const glm::vec2& size, const glm::vec4& color, - Anchor anchor) + Anchor anchor) { auto& shdr = shaders.xyuvrgba; shdr.program->activate(); shdr.program->setUniform(shdr.cache.hasTexture, 0); renderBox( *shdr.program, - shdr.cache.ortho, + shdr.cache.proj, shdr.cache.color, position, size, color, @@ -345,7 +401,7 @@ void renderBox(const glm::vec2& position, const glm::vec2& size, const glm::vec4 shdr.program->setUniform(shdr.cache.tex, unit); renderBox( *shdr.program, - shdr.cache.ortho, + shdr.cache.proj, shdr.cache.color, position, size, @@ -388,4 +444,63 @@ std::vector createRing(int nSegments, float radius, glm::vec4 colors) { return vertices; } +std::pair, std::vector> createSphere(int nSegments, + glm::vec3 radii, + glm::vec4 colors) +{ + std::vector vertices; + vertices.reserve(nSegments * nSegments); + for (int i = 0; i <= nSegments; i++) { + for (int j = 0; j <= nSegments; j++) { + const float fi = static_cast(i); + const float fj = static_cast(j); + // inclination angle (north to south) + // 0 -> PI + // azimuth angle (east to west) + const float theta = fi * glm::pi() / nSegments; + + // 0 -> 2*PI + const float phi = fj * glm::pi() * 2.f / nSegments; + + const float x = radii[0] * sin(theta) * cos(phi); + const float y = radii[1] * sin(theta) * sin(phi); + const float z = radii[2] * cos(theta); // Z points towards pole (theta = 0) + + Vertex v; + v.xyz[0] = x; + v.xyz[1] = y; + v.xyz[2] = z; + + const float t1 = fj / nSegments; + const float t2 = 1.f - (fi / nSegments); + + v.uv[0] = t1; + v.uv[1] = t2; + + v.rgba[0] = colors.r; + v.rgba[1] = colors.g; + v.rgba[2] = colors.b; + v.rgba[3] = colors.a; + + vertices.push_back(v); + } + } + + std::vector indices; + indices.reserve(vertices.size() * 3); + for (int i = 1; i <= nSegments; i++) { + for (int j = 0; j < nSegments; j++) { + const int t = nSegments + 1; + indices.push_back(static_cast(t * (i - 1) + j + 0)); + indices.push_back(static_cast(t * (i + 0) + j + 0)); + indices.push_back(static_cast(t * (i + 0) + j + 1)); + indices.push_back(static_cast(t * (i - 1) + j + 0)); + indices.push_back(static_cast(t * (i + 0) + j + 1)); + indices.push_back(static_cast(t * (i - 1) + j + 1)); + } + } + + return { vertices, indices }; +} + } // namespace openspace::rendering::helper diff --git a/src/rendering/renderable.cpp b/src/rendering/renderable.cpp index ffc5ceef16..e416a01e40 100644 --- a/src/rendering/renderable.cpp +++ b/src/rendering/renderable.cpp @@ -58,11 +58,6 @@ namespace { openspace::properties::Property::Visibility::Hidden }; - constexpr openspace::properties::Property::PropertyInfo BoundingSphereInfo = { - "BoundingSphere", - "Bounding Sphere", - "The size of the bounding sphere radius." - }; struct [[codegen::Dictionary(Renderable)]] Parameters { // [[codegen::verbatim(EnabledInfo.description)]] std::optional enabled; @@ -76,9 +71,6 @@ namespace { // [[codegen::verbatim(RenderableTypeInfo.description)]] std::optional type; - - // [[codegen::verbatim(BoundingSphereInfo.description)]] - std::optional boundingSphere; }; #include "renderable_codegen.cpp" } // namespace @@ -120,7 +112,6 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary) : properties::PropertyOwner({ "Renderable" }) , _enabled(EnabledInfo, true) , _opacity(OpacityInfo, 1.f, 0.f, 1.f) - , _boundingSphere(BoundingSphereInfo, 0.f, 0.f, 3e10f) , _renderableType(RenderableTypeInfo, "Renderable") { ZoneScoped @@ -154,9 +145,6 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary) // set type for UI _renderableType = p.type.value_or(_renderableType); addProperty(_renderableType); - - _boundingSphere = p.boundingSphere.value_or(_boundingSphere); - addProperty(_boundingSphere); } void Renderable::initialize() {} @@ -179,12 +167,16 @@ double Renderable::boundingSphere() const { return _boundingSphere; } +double Renderable::interactionSphere() const { + return _interactionSphere; +} + SurfacePositionHandle Renderable::calculateSurfacePositionHandle( const glm::dvec3& targetModelSpace) const { const glm::dvec3 directionFromCenterToTarget = glm::normalize(targetModelSpace); return { - directionFromCenterToTarget * boundingSphere(), + directionFromCenterToTarget * _parent->interactionSphere(), directionFromCenterToTarget, 0.0 }; diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index fdc278b9bd..ff37ec8c07 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -1111,30 +1111,18 @@ void RenderEngine::takeScreenshot() { _latestScreenshotNumber = global::windowDelegate->takeScreenshot(_applyWarping); } -/** - * Get the latest screenshot filename - */ unsigned int RenderEngine::latestScreenshotNumber() const { return _latestScreenshotNumber; } -/** - * Set raycasting uniforms on the program object, and setup raycasting. - */ void RenderEngine::preRaycast(ghoul::opengl::ProgramObject& programObject) { _renderer->preRaycast(programObject); } -/** - * Tear down raycasting for the specified program object. - */ void RenderEngine::postRaycast(ghoul::opengl::ProgramObject& programObject) { _renderer->postRaycast(programObject); } -/** - * Set renderer - */ void RenderEngine::setRenderer(std::unique_ptr renderer) { ZoneScoped diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 0f3b760444..2a44f4a5bd 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -31,12 +31,15 @@ #include #include #include +#include #include +#include #include #include #include #include #include +#include #include #include #include @@ -89,11 +92,23 @@ namespace { constexpr openspace::properties::Property::PropertyInfo BoundingSphereInfo = { "BoundingSphere", "Bounding Sphere", - "The bounding sphere of the scene graph node. This can be the " - "bounding sphere of an attached renderable or directly specified to the node. " - "If there is a boundingsphere on both the renderable and the node, the largest " - "number will be picked.", - openspace::properties::Property::Visibility::Hidden + "The bounding sphere of the scene graph node meaning that everything that this " + "scene graph node renders must be contained within this sphere. This value is " + "only used as an override to the bounding sphere calculated by the Renderable, " + "if present. If this value is -1, the Renderable's computed bounding sphere is " + "used", + openspace::properties::Property::Visibility::Developer + }; + + constexpr openspace::properties::Property::PropertyInfo InteractionSphereInfo = { + "InteractionSphere", + "Interaction Sphere", + "The minimum radius that the camera is allowed to get close to this scene graph " + "node. This value is " + "only used as an override to the bounding sphere calculated by the Renderable, " + "if present. If this value is -1, the Renderable's computed interaction sphere " + "is used", + openspace::properties::Property::Visibility::Developer }; constexpr openspace::properties::Property::PropertyInfo GuiPathInfo = { @@ -128,6 +143,14 @@ namespace { openspace::properties::Property::Visibility::Hidden }; + constexpr openspace::properties::Property::PropertyInfo ShowDebugSphereInfo = { + "ShowDebugSphere", + "Show Debug Sphere", + "If enabled the bounding sphere of this scene graph node is rendered as a debug " + "method", + openspace::properties::Property::Visibility::Developer + }; + struct [[codegen::Dictionary(SceneGraphNode)]] Parameters { // The identifier of this scenegraph node. This name must be unique among all // scene graph nodes that are loaded in a specific scene. If a duplicate is @@ -156,6 +179,10 @@ namespace { // sphere needs to be overwritten for some reason std::optional boundingSphere; + // A hard-coded radius for limiting the interaction radius, meaning the minimal + // distance that the camera can approach this scene graph node + std::optional interactionSphere; + struct Transform { // This node describes a translation that is applied to the scenegraph node // and all its children. Depending on the 'Type' of the translation, this can @@ -258,10 +285,8 @@ ghoul::mm_unique_ptr SceneGraphNode::createFromDictionary( } } - if (p.boundingSphere.has_value()) { - result->_boundingSphere = *p.boundingSphere; - result->_boundingSphere.setVisibility(properties::Property::Visibility::All); - } + result->_overrideBoundingSphere = p.boundingSphere; + result->_overrideInteractionSphere = p.interactionSphere; if (p.transform.has_value()) { if (p.transform->translation.has_value()) { @@ -345,23 +370,11 @@ ghoul::mm_unique_ptr SceneGraphNode::createFromDictionary( if (p.renderable.has_value()) { result->_renderable = Renderable::createFromDictionary(*p.renderable); ghoul_assert(result->_renderable, "Failed to create Renderable"); + result->_renderable->_parent = result.get(); result->addPropertySubOwner(result->_renderable.get()); LDEBUG(fmt::format( "Successfully created renderable for '{}'", result->identifier() )); - - // If the renderable child has a larger bounding sphere, we allow it to override - if (result->_renderable->boundingSphere() > result->_boundingSphere) { - result->_boundingSphere = result->_renderable->boundingSphere(); - - if (p.boundingSphere.has_value()) { - LWARNING(fmt::format( - "The specified property 'BoundingSphere' for '{}' was overwritten " - "by a child renderable", - result->_identifier - )); - } - } } if (p.tag.has_value()) { @@ -392,6 +405,8 @@ documentation::Documentation SceneGraphNode::Documentation() { return doc; } +ghoul::opengl::ProgramObject* SceneGraphNode::_debugSphereProgram = nullptr; + SceneGraphNode::SceneGraphNode() : properties::PropertyOwner({ "" }) , _guiHidden(GuiHiddenInfo) @@ -409,13 +424,15 @@ SceneGraphNode::SceneGraphNode() global::memoryManager->PersistentMemory.alloc() ) } - , _boundingSphere(BoundingSphereInfo, 0.0) + , _boundingSphere(BoundingSphereInfo, -1.0, -1.0, 1e12) + , _interactionSphere(InteractionSphereInfo, -1.0, -1.0, -1.0, 1e12) , _computeScreenSpaceValues(ComputeScreenSpaceInfo, false) , _screenSpacePosition(ScreenSpacePositionInfo, glm::ivec2(-1, -1)) , _screenVisibility(ScreenVisibilityInfo, false) , _distFromCamToNode(DistanceFromCamToNodeInfo, -1.0) , _screenSizeRadius(ScreenSizeRadiusInfo, 0) , _visibilityDistance(VisibilityDistanceInfo, 6e10f) + , _showDebugSphere(ShowDebugSphereInfo, false) { addProperty(_computeScreenSpaceValues); addProperty(_screenSpacePosition); @@ -423,7 +440,25 @@ SceneGraphNode::SceneGraphNode() addProperty(_distFromCamToNode); addProperty(_screenSizeRadius); addProperty(_visibilityDistance); + _boundingSphere.onChange([this]() { + if (_boundingSphere >= 0.0) { + _overrideBoundingSphere = _boundingSphere; + } + else { + _overrideBoundingSphere = std::nullopt; + } + }); addProperty(_boundingSphere); + _interactionSphere.onChange([this]() { + if (_interactionSphere >= 0.0) { + _overrideInteractionSphere = _interactionSphere; + } + else { + _overrideInteractionSphere = std::nullopt; + } + }); + addProperty(_interactionSphere); + addProperty(_showDebugSphere); } SceneGraphNode::~SceneGraphNode() {} // NOLINT @@ -461,6 +496,25 @@ void SceneGraphNode::initializeGL() { if (_renderable) { _renderable->initializeGL(); } + + // The first one to get here will create program shared between all scene graph nodes + if (_debugSphereProgram == nullptr) { + std::unique_ptr shader = + global::renderEngine->buildRenderProgram( + "DebugSphere", + absPath("${SHADERS}/core/xyzuvrgba_vs.glsl"), + absPath("${SHADERS}/core/xyzuvrgba_fs.glsl") + ); + // Since we are only going to create a single of these shaders for the lifetime of + // the program, we are not bothering with freeing it as the overhead of detecting + // when the last scenegraph node will be destroyed would be a bit too much for the + // benefit that we would gain from it + _debugSphereProgram = shader.release(); + _debugSphereProgram->setIgnoreUniformLocationError( + ghoul::opengl::ProgramObject::IgnoreError::Yes + ); + } + _state = State::GLInitialized; LDEBUG(fmt::format("Finished initializating GL: {}", identifier())); @@ -598,6 +652,59 @@ void SceneGraphNode::render(const RenderData& data, RendererTasks& tasks) { computeScreenSpaceData(newData); } } + + if (_showDebugSphere) { + if (const double bs = boundingSphere(); bs > 0.0) { + renderDebugSphere(data.camera, bs, glm::vec4(0.5f, 0.15f, 0.5f, 0.75f)); + } + + if (const double is = interactionSphere(); is > 0.0) { + renderDebugSphere(data.camera, is, glm::vec4(0.15f, 0.35f, 0.85f, 0.75f)); + } + } +} + +void SceneGraphNode::renderDebugSphere(const Camera& camera, double size, glm::vec4 color) +{ + glm::dvec3 scaleVec = _worldScaleCached * size; + glm::dmat4 modelTransform = + glm::translate(glm::dmat4(1.0), _worldPositionCached) * + glm::dmat4(_worldRotationCached) * + glm::scale(glm::dmat4(1.0), scaleVec); + + glm::mat4 modelViewProjection = camera.projectionMatrix() * + glm::mat4(camera.combinedViewMatrix() * modelTransform); + + _debugSphereProgram->activate(); + _debugSphereProgram->setUniform("hasTexture", 0); + _debugSphereProgram->setUniform("proj", modelViewProjection); + _debugSphereProgram->setUniform("color", color); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + glBindVertexArray(rendering::helper::vertexObjects.sphere.vao); + glDrawElements( + GL_TRIANGLES, + rendering::helper::vertexObjects.sphere.nElements, + GL_UNSIGNED_SHORT, + nullptr + ); + + glLineWidth(2.0); + _debugSphereProgram->setUniform("color", glm::vec4(1.f, 1.f, 1.f, 1.f)); + glDrawElements( + GL_LINES, + rendering::helper::vertexObjects.sphere.nElements, + GL_UNSIGNED_SHORT, + nullptr + ); + + glBindVertexArray(0); + + _debugSphereProgram->deactivate(); } void SceneGraphNode::setParent(SceneGraphNode& parent) { @@ -955,7 +1062,29 @@ std::vector SceneGraphNode::children() const { } double SceneGraphNode::boundingSphere() const { - return _boundingSphere; + if (_overrideBoundingSphere.has_value()) { + return glm::compMax(scale() * *_overrideBoundingSphere); + } + + if (_renderable) { + return glm::compMax(scale() * _renderable->boundingSphere()); + } + else { + return 0.0; + } +} + +double SceneGraphNode::interactionSphere() const { + if (_overrideInteractionSphere.has_value()) { + return glm::compMax(scale() * *_overrideInteractionSphere); + } + + if (_renderable) { + return glm::compMax(scale() * _renderable->interactionSphere()); + } + else { + return 0.0; + } } const Renderable* SceneGraphNode::renderable() const {