diff --git a/Source/src/scene/OEScene.cpp b/Source/src/scene/OEScene.cpp new file mode 100644 index 00000000..dd910c80 --- /dev/null +++ b/Source/src/scene/OEScene.cpp @@ -0,0 +1,154 @@ +#include "scene/OEScene.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common/SpdLogger.h" +#include "common/RecourceHelper.h" +#include "scene/ScopedTimer.h" +#include "viewer/OsgView.h" + + +OEScene::OEScene() { + osgDB::FilePathList& pathList = osgDB::Registry::instance()->getDataFilePathList(); + const std::string& basePath = RecourceHelper::Get().GetBasePath().toStdString(); + pathList.push_back(basePath + "/resources/earth/"); + pathList.push_back(basePath + "resources/textures/"); + + earthRootNode_ = osgDB::readNodeFile("triton.earth"); + if (!earthRootNode_) { + LOG_ERROR("read earth node(triton.earth) failed"); + return; + } + + earthMapNode_ = osgEarth::MapNode::get(earthRootNode_); + LOG_INFO("earth map node get success: {}", earthMapNode_.valid()); + + entityRoot_ = new osg::Group; + if (earthMapNode_) { + earthMapNode_->addChild(entityRoot_); + } +} + + +void OEScene::InitEventHandle(class OsgView* view) { + if (nullptr == view) { + LOG_WARN("view is nullptr"); + return; + } + + view->GetView()->addEventHandler(new osgEarth::Util::EarthManipulator()); + + view->GetView()->addEventHandler(new osgViewer::HelpHandler); +} + +void OEScene::AttachView(OsgView* view) { + if (nullptr == view) { + LOG_WARN("view is nullptr"); + return; + } + + //osg::Node* node1 = osgDB::readNodeFile("chaff_bombs.osg"); + //node1->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + //osg::MatrixTransform* mt = new osg::MatrixTransform; + //mt->addChild(node1); + ////GetOceanScene()->addChild(mt); + //entityRoot_->addChild(mt); + //osg::Vec3 eye(0.0f, 60.f, 50.0f); + //mt->setMatrix(osg::Matrix::translate(eye)/* * osg::Matrix::scale(osg::Vec3(10, 10, 10))*/); + //////mt->getOrCreateStateSet()- + + //osg::Node* node = view->GetView()->getSceneData(); + //if (nullptr == node) { + // LOG_INFO("view scene data is nullptr, root valid:{}", scene_.valid()); + // view->GetView()->setSceneData(scene_); + //} else { + // osg::Group* group = node->asGroup(); + // if (nullptr != group) { + // LOG_INFO("node is group"); + // group->addChild(scene_); + // } else { + // LOG_INFO("node is not group"); + // } + //} +} + +void OEScene::DetachView(OsgView* view) { + if (nullptr != earthRootNode_) { + std::vector parents = earthRootNode_->getParents(); + for (const auto& parent : parents) { + parent->removeChild(earthRootNode_); + } + } + view->GetView()->setSceneData(nullptr); +} + +#define USE_CUSTOM_SHADER + +osg::ref_ptr OEScene::LoadCubeMapTextures(const std::string& dir) { + enum { + POS_X, NEG_X, POS_Y, NEG_Y, POS_Z, NEG_Z + }; + + std::string filenames[6]; + + std::string basePath = RecourceHelper::Get().GetBasePath().toStdString(); + + filenames[POS_X] = basePath + "/resources/textures/" + dir + "/east.png"; + filenames[NEG_X] = basePath + "/resources/textures/" + dir + "/west.png"; + filenames[POS_Z] = basePath + "/resources/textures/" + dir + "/north.png"; + filenames[NEG_Z] = basePath + "/resources/textures/" + dir + "/south.png"; + filenames[POS_Y] = basePath + "/resources/textures/" + dir + "/down.png"; + filenames[NEG_Y] = basePath + "/resources/textures/" + dir + "/up.png"; + + osg::ref_ptr cubeMap = new osg::TextureCubeMap; + cubeMap->setInternalFormat(GL_RGBA); + + cubeMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); + cubeMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + cubeMap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + cubeMap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + + cubeMap->setImage(osg::TextureCubeMap::NEGATIVE_X, osgDB::readImageFile(filenames[NEG_X])); + cubeMap->setImage(osg::TextureCubeMap::POSITIVE_X, osgDB::readImageFile(filenames[POS_X])); + cubeMap->setImage(osg::TextureCubeMap::NEGATIVE_Y, osgDB::readImageFile(filenames[NEG_Y])); + cubeMap->setImage(osg::TextureCubeMap::POSITIVE_Y, osgDB::readImageFile(filenames[POS_Y])); + cubeMap->setImage(osg::TextureCubeMap::NEGATIVE_Z, osgDB::readImageFile(filenames[NEG_Z])); + cubeMap->setImage(osg::TextureCubeMap::POSITIVE_Z, osgDB::readImageFile(filenames[POS_Z])); + + return cubeMap; +} + +osg::Group* OEScene::GetData() { + return earthMapNode_; +} + + + +void OEScene::AddToScene(osg::Node* node) { + assert(nullptr != entityRoot_); + entityRoot_->addChild(node); + /* osgOcean::OceanScene* oceanScene = GetOceanScene(); + if (nullptr == oceanScene) { + LOG_WARN("oceanScene is nullptr"); + return; + } + + node->setNodeMask(oceanScene->getNormalSceneMask() | + oceanScene->getReflectedSceneMask() | + oceanScene->getRefractedSceneMask() | + CAST_SHADOW | RECEIVE_SHADOW); + oceanScene->addChild(node);*/ + /* osg::Group* root = GetScene(); + root->addChild(node);*/ +} diff --git a/Source/src/scene/OEScene.h b/Source/src/scene/OEScene.h new file mode 100644 index 00000000..d3ea478d --- /dev/null +++ b/Source/src/scene/OEScene.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include +#include + +#include "scene/SkyDome.h" + +class OsgView; + +class OEScene : public osg::Referenced { +public: + OEScene(); + void InitEventHandle(OsgView* view); + void AttachView(OsgView* view); + void DetachView(OsgView* view); + + osg::ref_ptr LoadCubeMapTextures(const std::string& dir); + + inline osg::Vec4f IntColor(unsigned r, unsigned g, unsigned b, unsigned a = 255) { + float div = 1.f / 255.f; + return osg::Vec4f(div * (float)r, div * (float)g, div * float(b), div * (float)a); + } + + osg::Group* GetData(); + + inline osg::Group* GetScene(void) { + return earthMapNode_.get(); + } + + void AddToScene(osg::Node* node); + +private: + osg::ref_ptr earthRootNode_; + osg::ref_ptr earthMapNode_; + osg::ref_ptr entityRoot_; +}; diff --git a/Source/src/scene/TritonAPIWrapper.cpp b/Source/src/scene/TritonAPIWrapper.cpp new file mode 100644 index 00000000..73e24fef --- /dev/null +++ b/Source/src/scene/TritonAPIWrapper.cpp @@ -0,0 +1,114 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#include "TritonAPIWrapper.h" +#include + +#define LC "[TritonAPI] " + +using namespace osgEarth::Triton; + +#define SETGET_EXPLICIT(NS, SETTER, GETTER, TYPE) \ + void NS :: SETTER (TYPE value) { HANDLE-> SETTER (value); } \ + TYPE NS :: GETTER () const { return HANDLE -> GETTER (); } + +#define SETGET(NS, FUNC, TYPE) \ + SETGET_EXPLICIT(NS, Set##FUNC, Get##FUNC, TYPE) + +#define TOVEC3(X) ::Triton::Vector3(X.x(),X.y(),X.z()) +#define FROMVEC3(X) osg::Vec3(X.x,X.y,X.z) + +//................................ +#undef HANDLE +#define HANDLE ((::Triton::BreakingWavesParameters*)_handle) + +SETGET(BreakingWavesParameters, Steepness, float); +SETGET(BreakingWavesParameters, Wavelength, float); + +//................................ +#undef HANDLE +#define HANDLE ((::Triton::Environment*)_handle) + +BreakingWavesParameters Environment::GetBreakingWavesParameters() const { + return BreakingWavesParameters((uintptr_t)&HANDLE->GetBreakingWavesParameters()); +} +void Environment::SetDirectionalLight(const osg::Vec3& dir, const osg::Vec3& color) { + HANDLE->SetDirectionalLight(TOVEC3(dir), TOVEC3(color)); +} +osg::Vec3 Environment::GetLightDirection() const { + const ::Triton::Vector3& v = HANDLE->GetLightDirection(); + return FROMVEC3(v); +} +osg::Vec3 Environment::GetDirectionalLightColor() const { + const ::Triton::Vector3& v = HANDLE->GetDirectionalLightColor(); + return FROMVEC3(v); +} +void Environment::SetAmbientLight(const osg::Vec3& color) { + HANDLE->SetAmbientLight(TOVEC3(color)); +} +osg::Vec3 Environment::GetAmbientLightColor() const { + const ::Triton::Vector3& v = HANDLE->GetAmbientLightColor(); + return FROMVEC3(v); +} +void Environment::SimulateSeaState(double bscale, double winddir) { + HANDLE->SimulateSeaState(bscale, winddir); +} +void Environment::SetAboveWaterVisibility(double visibility, osg::Vec3 fog_color) { + HANDLE->SetAboveWaterVisibility(visibility, TOVEC3(fog_color)); +} +void Environment::GetAboveWaterVisibility(double &visibility, osg::Vec3 &fog_color) const { + ::Triton::Vector3 triton_fog_col; + HANDLE->GetAboveWaterVisibility(visibility, triton_fog_col); + fog_color = FROMVEC3(triton_fog_col); +} +void Environment::SetEnvironmentMap(GLuint cubeMap, const osg::Matrixd &textureMatrix) { + ::Triton::Matrix3 triton_tex_mat( + textureMatrix(0, 0), textureMatrix(0, 1), textureMatrix(0, 2), + textureMatrix(1, 0), textureMatrix(1, 1), textureMatrix(1, 2), + textureMatrix(2, 0), textureMatrix(2, 1), textureMatrix(2, 2)); + ::Triton::TextureHandle tex_handle = (::Triton::TextureHandle)(static_cast(cubeMap)); + HANDLE->SetEnvironmentMap(tex_handle, triton_tex_mat); +} +GLuint Environment::GetEnvironmentMap() const { + ::Triton::TextureHandle tex_handle = HANDLE->GetEnvironmentMap(); + return static_cast((size_t)tex_handle); +} +osg::Matrixd Environment::GetEnvironmentMapMatrix() const { + ::Triton::Matrix3 m = HANDLE->GetEnvironmentMapMatrix(); + osg::Matrixd env_map_matrix( + m.elem[0][0], m.elem[0][1], m.elem[0][2], 0, + m.elem[1][0], m.elem[1][1], m.elem[1][2], 0, + m.elem[2][0], m.elem[2][1], m.elem[2][2], 0, + 0, 0, 0, 1); + return env_map_matrix; +} +SETGET(Environment, SunIntensity, float); + +//................................ +#undef HANDLE +#define HANDLE ((::Triton::Ocean*)_handle) + +SETGET(Ocean, Choppiness, float); +//SETGET(Ocean, MaximumWavePeriod, double); +SETGET_EXPLICIT(Ocean, EnableSpray, SprayEnabled, bool); +SETGET_EXPLICIT(Ocean, EnableGodRays, GodRaysEnabled, bool); +SETGET(Ocean, GodRaysFade, float); + +void Ocean::EnableWireframe(bool wireframe) { HANDLE->EnableWireframe(wireframe); } +void Ocean::SetQuality(OceanQuality value) { HANDLE->SetQuality((::Triton::OceanQuality)value); } +OceanQuality Ocean::GetQuality() const { return (OceanQuality)HANDLE->GetQuality(); } diff --git a/Source/src/scene/TritonAPIWrapper.h b/Source/src/scene/TritonAPIWrapper.h new file mode 100644 index 00000000..f1921ad6 --- /dev/null +++ b/Source/src/scene/TritonAPIWrapper.h @@ -0,0 +1,112 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef OSGEARTH_TRITON_API_WRAPPER +#define OSGEARTH_TRITON_API_WRAPPER 1 + +//#include "Common" +#include +#include +#include +#include // for uintptr_t + +namespace osgEarth { + namespace Util { + class OceanNode; + } +} + +namespace osgEarth { namespace Triton +{ + /** Enumerates the ocean quality settings used in Ocean::SetQuality() */ + enum OceanQuality { + GOOD, + BETTER, + BEST + }; + + class BreakingWavesParameters + { + public: + void SetSteepness(float); + float GetSteepness() const; + + void SetWavelength(float); + float GetWavelength() const; + + public: + BreakingWavesParameters(uintptr_t handle) : _handle(handle) { } + uintptr_t _handle; + }; + + class Environment + { + public: + void SetDirectionalLight(const osg::Vec3& dir, const osg::Vec3& color); + osg::Vec3 GetLightDirection() const; + osg::Vec3 GetDirectionalLightColor() const; + + void SetAmbientLight(const osg::Vec3& color); + osg::Vec3 GetAmbientLightColor() const; + + void SetSunIntensity(float intensity); + float GetSunIntensity() const; + + BreakingWavesParameters GetBreakingWavesParameters() const; + + void SimulateSeaState(double bs, double winddir); + + void SetAboveWaterVisibility(double visibility, osg::Vec3 fog_color); + void GetAboveWaterVisibility(double &visibility, osg::Vec3 &fog_color) const; + + void SetEnvironmentMap(GLuint id, const osg::Matrixd &textureMatrix = osg::Matrixd()); + GLuint GetEnvironmentMap() const; + osg::Matrixd GetEnvironmentMapMatrix() const; + public: + Environment(uintptr_t handle) : _handle(handle) { } + uintptr_t _handle; + }; + + class Ocean + { + public: + void SetChoppiness(float); + float GetChoppiness() const; + + void EnableWireframe(bool wireframe); + + void SetQuality(OceanQuality value); + OceanQuality GetQuality() const; + + void EnableSpray(bool enabled); + bool SprayEnabled() const; + + void EnableGodRays(bool enabled); + bool GodRaysEnabled() const; + + void SetGodRaysFade(float fadeAmount); + float GetGodRaysFade() const; + + public: + Ocean(uintptr_t handle) : _handle(handle) { } + uintptr_t _handle; + }; + +} } // namespace osgEarth::SilverLining + +#endif // OSGEARTH_TRITON_API_WRAPPER diff --git a/Source/src/scene/TritonCallback.h b/Source/src/scene/TritonCallback.h new file mode 100644 index 00000000..122aa23c --- /dev/null +++ b/Source/src/scene/TritonCallback.h @@ -0,0 +1,39 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef OSGEARTH_TRITON_CALLBACK_H +#define OSGEARTH_TRITON_CALLBACK_H 1 + +#include "TritonAPIWrapper.h" + +namespace osgEarth { namespace Triton +{ + /** + * A callback that lets you execute code in the proper context. + */ + class Callback : public osg::Referenced + { + public: + virtual void onInitialize(Environment& env, Ocean& ocean) { } + + virtual void onDrawOcean(Environment& env, Ocean& ocean) { } + }; + +} } // namespace osgEarth::Triton + +#endif // OSGEARTH_TRITON_CALLBACK_H diff --git a/Source/src/scene/TritonContext.cpp b/Source/src/scene/TritonContext.cpp new file mode 100644 index 00000000..8606e581 --- /dev/null +++ b/Source/src/scene/TritonContext.cpp @@ -0,0 +1,222 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#include "TritonContext.h" +#include +#include +#include +#include +#include + +#define LC "[TritonContext] " + +using namespace osgEarth::Triton; + + +TritonContext::TritonContext(const TritonLayer::Options& options) : +_options ( options ), +_initAttempted ( false ), +_initFailed ( false ), +_resourceLoader ( 0L ), +_environment ( 0L ), +_environmentWrapper ( 0L ), +_ocean ( 0L ), +_oceanWrapper ( 0L ) +{ + //nop +} + +TritonContext::~TritonContext() +{ + if (_oceanWrapper) + delete _oceanWrapper; + + if (_environmentWrapper) + delete _environmentWrapper; +} + +void +TritonContext::setSRS(const osgEarth::SpatialReference* srs) +{ + _srs = srs; +} + +void +TritonContext::setCallback(Callback* callback) +{ + _callback = callback; +} + +bool +TritonContext::passHeightMapToTriton() const +{ + return _options.useHeightMap() == true; +} + +int +TritonContext::getHeightMapSize() const +{ + return osg::clampBetween(_options.heightMapSize().get(), 64u, 2048u); +} + +const std::string& +TritonContext::getMaskLayerName() const +{ + return _options.maskLayer().externalLayerName().get(); +} + +void +TritonContext::initialize(osg::RenderInfo& renderInfo) +{ + if ( !_initAttempted && !_initFailed ) + { + // lock/double-check: + std::lock_guard excl(_initMutex); + if ( !_initAttempted && !_initFailed ) + { + _initAttempted = true; + + std::string resourcePath = _options.resourcePath().get(); + if (resourcePath.empty() && ::getenv("TRITON_PATH")) + { + resourcePath = osgDB::concatPaths(::getenv("TRITON_PATH"), "Resources"); + } + + _resourceLoader = new ::Triton::ResourceLoader(resourcePath.c_str()); + + _environment = new ::Triton::Environment(); + + _environmentWrapper = new Environment((uintptr_t)_environment); + + _environment->SetLicenseCode( + _options.user()->c_str(), + _options.licenseCode()->c_str() ); + + // "WGS84" is used to represent any ellipsoid. + ::Triton::CoordinateSystem cs = + _srs->isGeographic() ? ::Triton::WGS84_ZUP : + ::Triton::FLAT_ZUP; + + // Set the ellipsoid to match the one in our map's SRS. + if ( _srs->isGeographic() ) + { + const Ellipsoid& ellipsoid = _srs->getEllipsoid(); + + std::string eqRadius = Stringify() << ellipsoid.getRadiusEquator(); + std::string poRadius = Stringify() << ellipsoid.getRadiusPolar(); + + _environment->SetConfigOption( "equatorial-earth-radius-meters", eqRadius.c_str() ); + _environment->SetConfigOption( "polar-earth-radius-meters", poRadius.c_str() ); + } + + //_environment->SetConfigOption("avoid-opengl-stalls", "yes"); + //_environment->SetConfigOption("fft-texture-update-frame-delayed", "yes"); + + float openGLVersion = osg::getGLVersionNumber(); + enum ::Triton::Renderer tritonOpenGlVersion = ::Triton::OPENGL_2_0; +#ifndef OSG_GL_FIXED_FUNCTION_AVAILABLE + if( openGLVersion >= 4.1 ) + tritonOpenGlVersion = ::Triton::OPENGL_4_1; + else if( openGLVersion >= 4.0 ) + tritonOpenGlVersion = ::Triton::OPENGL_4_0; + else if( openGLVersion >= 3.2 ) + tritonOpenGlVersion = ::Triton::OPENGL_3_2; +#endif + + ::Triton::EnvironmentError err = _environment->Initialize( + cs, + tritonOpenGlVersion, + _resourceLoader ); + + if ( err == ::Triton::SUCCEEDED ) + { + ::Triton::WindFetch wf; + wf.SetWind( 10.0, 0.0 ); + _environment->AddWindFetch( wf ); + + _ocean = ::Triton::Ocean::Create( + _environment, + ::Triton::JONSWAP, + true ); // enableHeightTests - activated GetHeight for intersections + } + + if ( _ocean ) + { + _oceanWrapper = new Ocean((uintptr_t)_ocean); + + // fire init callback if available + if (_callback.valid()) + { + _callback->onInitialize(getEnvironmentWrapper(), getOceanWrapper()); + } + + OE_INFO << LC << "Triton initialized OK!" << std::endl; + } + else + { + _initFailed = true; + OE_WARN << LC << "Triton initialization failed- err=" << err << std::endl; + } + } + } +} + +bool +TritonContext::intersect(const osg::Vec3d& start, const osg::Vec3d& dir, float& out_height, osg::Vec3f& out_normal) const +{ + ::Triton::Vector3 p(start.ptr()); + ::Triton::Vector3 d(dir.ptr()); + ::Triton::Vector3 normal; + bool ok = _ocean->GetHeight(p, d, out_height, normal); + out_normal.set(normal.x, normal.y, normal.z); + return ok; +} + +void +TritonContext::resizeGLObjectBuffers(unsigned maxSize) +{ + osg::Object::resizeGLObjectBuffers(maxSize); +} + +void +TritonContext::releaseGLObjects(osg::State* state) const +{ + osg::Object::releaseGLObjects(state); + + OE_DEBUG << LC << "Triton shutting down - releasing GL resources\n"; + if (state) + { + if ( _ocean ) + { + delete _ocean; + _ocean = 0L; + } + + if ( _environment ) + { + delete _environment; + _environment = 0L; + } + + if ( _resourceLoader ) + { + delete _resourceLoader; + _resourceLoader = 0L; + } + } +} diff --git a/Source/src/scene/TritonContext.h b/Source/src/scene/TritonContext.h new file mode 100644 index 00000000..dee9d4cf --- /dev/null +++ b/Source/src/scene/TritonContext.h @@ -0,0 +1,115 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef OSGEARTH_TRITON_CONTEXT_H +#define OSGEARTH_TRITON_CONTEXT_H + +#include +#include // Triton +#include "TritonLayer.h" +#include "TritonAPIWrapper.h" +#include "TritonCallback.h" +#include +#include +#include + +namespace osgEarth { + class SpatialReference; +} + +namespace osgEarth { namespace Triton +{ + /** + * Contains all the Triton SDK handles. + */ + class TritonContext : public osg::Object + { + public: + META_Object(osgEarth, TritonContext); + + TritonContext(const TritonLayer::Options&); + + /** Sets the spatial reference system of the map */ + void setSRS(const osgEarth::SpatialReference* srs); + + /** Sets the user callback */ + void setCallback(Callback* callback); + Callback* getCallback() const { return _callback.get(); } + + public: // accessors + + bool ready() const { return _initAttempted && !_initFailed; } + + /** Spatial reference of the map */ + const osgEarth::SpatialReference* getSRS() const { return _srs.get(); } + + void initialize(osg::RenderInfo& renderInfo); + + bool intersect(const osg::Vec3d& start, const osg::Vec3d& dir, float& out_height, osg::Vec3f& out_normal) const; + + ::Triton::Environment* getEnvironment() { return _environment; } + Environment& getEnvironmentWrapper() const { return *_environmentWrapper; } + + ::Triton::Ocean* getOcean() { return _ocean; } + Ocean& getOceanWrapper() const { return *_oceanWrapper; } + + bool passHeightMapToTriton() const; + + int getHeightMapSize() const; + + const std::string& getMaskLayerName() const; + + public: // osg::Object + + void resizeGLObjectBuffers(unsigned maxSize); + + /** If State is non-zero, this function releases any associated OpenGL objects for + * the specified graphics context. Otherwise, releases OpenGL objects + * for all graphics contexts. */ + void releaseGLObjects(osg::State* state) const; + + protected: + + virtual ~TritonContext(); + + // hidden ctors (for META_Object) + TritonContext() { } + TritonContext(const TritonContext&, const osg::CopyOp&) { } + + private: + TritonLayer::Options _options; + + bool _initAttempted; + bool _initFailed; + std::mutex _initMutex; + + osg::ref_ptr _srs; + + mutable ::Triton::ResourceLoader* _resourceLoader; + mutable ::Triton::Environment* _environment; + mutable ::Triton::Ocean* _ocean; + + Environment* _environmentWrapper; + Ocean* _oceanWrapper; + + osg::ref_ptr _callback; + }; + +} } // namespace osgEarth::Triton + +#endif // OSGEARTH_TRITON_CONTEXT_H \ No newline at end of file diff --git a/Source/src/scene/TritonDrawable.cpp b/Source/src/scene/TritonDrawable.cpp new file mode 100644 index 00000000..b21c8579 --- /dev/null +++ b/Source/src/scene/TritonDrawable.cpp @@ -0,0 +1,330 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#include "TritonContext.h" +#include "TritonDrawable.h" +#include "TritonHeightMap.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#undef LC +#define LC "[TritonDrawable] " + +//#define DEBUG_HEIGHTMAP + +using namespace osgEarth::Triton; + + +TritonDrawable::TritonDrawable(TritonContext* TRITON) : + _TRITON(TRITON) +{ + // call this to ensure draw() gets called every frame. + setSupportsDisplayList( false ); + setUseVertexBufferObjects( false ); + + // dynamic variance prevents update/cull overlap when drawing this + setDataVariance( osg::Object::DYNAMIC ); + + _wgs84 = SpatialReference::get("wgs84"); + _ecef = _wgs84->getGeocentricSRS(); +} + +TritonDrawable::~TritonDrawable() +{ + //nop +} + +void +TritonDrawable::setMaskLayer(const osgEarth::ImageLayer* layer) +{ + _maskLayer = layer; +} + +void +TritonDrawable::setHeightMapGenerator(TritonHeightMap* value) +{ + _heightMapGenerator = value; +} + +void +TritonDrawable::setPlanarReflectionMap(osg::Texture2D* map) +{ + _planarReflectionMap = map; +} + +void +TritonDrawable::setPlanarReflectionProjection(osg::RefMatrix* proj) +{ + _planarReflectionProjection = proj; +} + +osg::BoundingBox +TritonDrawable::computeBoundingBox() const +{ + return osg::BoundingBox(); +} + +namespace { + +// Wrapper around Ocean->GetShaderObject() to account for API changes from Triton 3.x to 4.x +GLint +getOceanShader(::Triton::Ocean* ocean, ::Triton::Shaders shaderProgram, void* context, const ::Triton::Camera* camera) +{ +#if (TRITON_MAJOR_VERSION >= 4) + return (GLint)ocean->GetShaderObject( shaderProgram, context, camera ); +#else + return (GLint)ocean->GetShaderObject( shaderProgram ); +#endif +} + +} + +void +TritonDrawable::drawImplementation(osg::RenderInfo& renderInfo) const +{ + OE_GL_ZONE; + + osg::State* state = renderInfo.getState(); + + state->disableAllVertexArrays(); + + _TRITON->initialize( renderInfo ); + if ( !_TRITON->ready() ) + return; + + // Configure the height map generator. + // If configuration fails, attempt to continue without a heightmap. + if (_heightMapGenerator.valid()) + { + bool configOK = _heightMapGenerator->configure(_TRITON->getHeightMapSize(), *state); + if (configOK == false) + { + _heightMapGenerator = 0L; + OE_WARN << LC << "Failed to establish a legal FBO configuration; disabling height map generator!" << std::endl; + } + } + + ::Triton::Environment* environment = _TRITON->getEnvironment(); + + // Find or create the Triton camera for this OSG camera: + CameraLocal& local = _local.get(renderInfo.getCurrentCamera()); + if (local._tritonCam == 0L) + { + local._tritonCam = environment->CreateCamera(); + local._tritonCam->SetName(renderInfo.getCurrentCamera()->getName().c_str()); + } + ::Triton::Camera* tritonCam = local._tritonCam; + + auto cid = GLUtils::getSharedContextID(*state); + osgEarth::NativeProgramAdapterCollection& adapters = _adapters[cid]; + if ( adapters.empty() ) + { + OE_DEBUG << LC << "Initializing Triton program adapters" << std::endl; + const std::vector prefixes = { "osg_", "oe_" }; + adapters.push_back( new osgEarth::NativeProgramAdapter(state, getOceanShader(_TRITON->getOcean(), ::Triton::WATER_SURFACE, 0L, tritonCam), prefixes, "WATER_SURFACE")); + adapters.push_back( new osgEarth::NativeProgramAdapter(state, getOceanShader(_TRITON->getOcean(), ::Triton::WATER_SURFACE_PATCH, 0L, tritonCam), prefixes, "WATER_SURFACE_PATCH")); + adapters.push_back( new osgEarth::NativeProgramAdapter(state, getOceanShader(_TRITON->getOcean(), ::Triton::GOD_RAYS, 0L, tritonCam), prefixes, "GOD_RAYS")); + adapters.push_back( new osgEarth::NativeProgramAdapter(state, getOceanShader(_TRITON->getOcean(), ::Triton::SPRAY_PARTICLES, 0L, tritonCam), prefixes, "SPRAY_PARTICLES")); + adapters.push_back( new osgEarth::NativeProgramAdapter(state, getOceanShader(_TRITON->getOcean(), ::Triton::WAKE_SPRAY_PARTICLES, 0L, tritonCam), prefixes, "WAKE_SPRAY_PARTICLES")); +#if 0 + // In older Triton (3.91), this line causes problems in Core profile and prevents the ocean from drawing. In newer Triton (4.01), + // this line causes a crash because there is no context passed in to GetShaderObject(), resulting in multiple NULL references. + adapters.push_back( new osgEarth::NativeProgramAdapter(state, getOceanShader(_TRITON->getOcean(), ::Triton::WATER_DECALS, 0L, tritonCam), prefixes, "WATER_DECALS")); +#endif + } + adapters.apply( state ); + + + // Pass the final view and projection matrices into Triton. + if ( environment ) + { + tritonCam->SetCameraMatrix(state->getModelViewMatrix().ptr()); + tritonCam->SetProjectionMatrix(state->getProjectionMatrix().ptr()); + } + + // Calculate sea level based on the camera: + if (_verticalDatum.valid()) + { + GeoPoint cam(_ecef, osg::Vec3d(0, 0, 0) * state->getInitialInverseViewMatrix(), ALTMODE_ABSOLUTE); + cam.transformInPlace(_wgs84); + auto msl = _verticalDatum->hae2msl(cam.y(), cam.x(), 0.0); + environment->SetSeaLevel(-msl); + } + + if (_heightMapGenerator.valid()) + { + GLint texName; + osg::Matrix hMM; + if (_heightMapGenerator->getTextureAndMatrix(renderInfo, texName, hMM)) + { + // copy the OSG matrix to a Triton matrix: + ::Triton::Matrix4 texMat( + hMM(0, 0), hMM(0, 1), hMM(0, 2), hMM(0, 3), + hMM(1, 0), hMM(1, 1), hMM(1, 2), hMM(1, 3), + hMM(2, 0), hMM(2, 1), hMM(2, 2), hMM(2, 3), + hMM(3, 0), hMM(3, 1), hMM(3, 2), hMM(3, 3)); + + environment->SetHeightMap((::Triton::TextureHandle)texName, texMat, 0L, tritonCam); + + //OE_DEBUG << LC << "Updating height map, FN=" << renderInfo.getState()->getFrameStamp()->getFrameNumber() << std::endl; + } + } + + state->dirtyAllVertexArrays(); + + // Now light and draw the ocean: + if ( environment ) + { + // User pre-draw callback: + if (_TRITON->getCallback()) + { + _TRITON->getCallback()->onDrawOcean( + _TRITON->getEnvironmentWrapper(), + _TRITON->getOceanWrapper()); + } + + osg::Light* light = renderInfo.getView() ? renderInfo.getView()->getLight() : NULL; + + // This is the light attached to View so there are no transformations above.. + // But in general case you would need to accumulate all transforms above the light into this matrix + osg::Matrix lightLocalToWorldMatrix = osg::Matrix::identity(); + + // If you don't know where the sun lightsource is attached and don't know its local to world matrix you may use + // following elaborate scheme to grab the light source while drawing Triton ocean: + // - Install cull callback to catch CullVisitor and record pointer to its associated RenderStage + // I was hoping RenderStage can be found from renderInfo in drawImplementation but I didn't figure how ... + // - When TritonDrawable::drawImplementation is called all lights will be already applied to OpenGL + // then just find proper infinite directional light by scanning renderStage->PositionalStateContainer. + // - Note that we canot scan for the lights inside cull because they may not be traversed before Triton drawable + // - When you found interesting ligt source that can work as Sun, read its modelview matrix and lighting params + // Multiply light position by ( modelview * inverse camera view ) and pass this to Triton with lighting colors + + if ( light && light->getPosition().w() == 0 ) + { + osg::Vec4 ambient = light->getAmbient(); + osg::Vec4 diffuse = light->getDiffuse(); + osg::Vec4 position = light->getPosition(); + + // Compute light position/direction in the world + position = position * lightLocalToWorldMatrix; + + // Diffuse direction and color + environment->SetDirectionalLight( + ::Triton::Vector3( position[0], position[1], position[2] ), + ::Triton::Vector3( diffuse[0], diffuse[1], diffuse[2] ) ); + + // Sun-based ambient value: + osg::Vec3d up = osg::Vec3d(0,0,0) * renderInfo.getCurrentCamera()->getInverseViewMatrix(); + up.normalize(); + osg::Vec3d pos3 = osg::Vec3d(position.x(), position.y(), position.z()); + pos3.normalize(); + float dot = osg::clampAbove(up*pos3, 0.0); dot*=dot; + float sunAmbient = (float)osg::clampBetween( dot, 0.0f, 0.88f ); + float fa = osg::maximum(sunAmbient, ambient[0]); + + // Ambient color based on the zenith color in the cube map + environment->SetAmbientLight( ::Triton::Vector3(fa, fa, fa) ); + } + + else + { + environment->SetDirectionalLight( ::Triton::Vector3(0,0,1), ::Triton::Vector3(1,1,1) ); + environment->SetAmbientLight( ::Triton::Vector3(0.88f, 0.88f, 0.88f) ); + } + + if ( _cubeMap.valid() ) + { + // Build transform from our cube map orientation space to native Triton orientation + // See worldToCubeMap function used in SkyBox to orient sky texture so that sky is up and earth is down + osg::Matrix m = osg::Matrix::rotate( osg::PI_2, osg::X_AXIS ); // = worldToCubeMap + + ::Triton::Matrix3 transformFromYUpToZUpCubeMapCoords( + m(0,0), m(0,1), m(0,2), + m(1,0), m(1,1), m(1,2), + m(2,0), m(2,1), m(2,2) ); + + // Grab the cube map from our sky box and give it to Triton to use as an _environment map + // GLenum texture = renderInfo.getState()->getLastAppliedTextureAttribute( _stage, osg::StateAttribute::TEXTURE ); + environment->SetEnvironmentMap( + (::Triton::TextureHandle)_cubeMap->getTextureObject(cid)->id(), + transformFromYUpToZUpCubeMapCoords ); + + if( _planarReflectionMap.valid() && _planarReflectionProjection.valid() ) + { + osg::Matrix & p = *_planarReflectionProjection; + + ::Triton::Matrix3 planarProjection( + p(0,0), p(0,1), p(0,2), + p(1,0), p(1,1), p(1,2), + p(2,0), p(2,1), p(2,2) ); + + environment->SetPlanarReflectionMap( + (::Triton::TextureHandle)_planarReflectionMap->getTextureObject(cid)->id(), + planarProjection, + 0.125 ); + } + } + + // Draw the ocean for the current time sample + if ( _TRITON->getOcean() ) + { + osg::GLExtensions* ext = osg::GLExtensions::Get(cid, true); + + bool writeDepth = true; + const osg::Depth* depth = static_cast(state->getLastAppliedAttribute(osg::StateAttribute::DEPTH)); + if (depth) + writeDepth = depth->getWriteMask(); + + double simTime = renderInfo.getView()->getFrameStamp()->getSimulationTime(); + simTime = fmod(simTime, 86400.0); + + _TRITON->getOcean()->Draw( + simTime, + writeDepth, // depth writes + true, // draw water + true, // draw particles + NULL, // optional context + tritonCam); + + } + } + + // Put GL back in a state that won't confuse the OSG state tracking: + state->dirtyAllVertexArrays(); + state->dirtyAllAttributes(); + state->dirtyAllModes(); + +#ifndef OSG_GL_FIXED_FUNCTION_AVAILABLE + // Keep OSG from reapplying GL_LIGHTING on next state change after dirtyAllModes(). + state->setModeValidity(GL_LIGHTING, false); +#endif + + // Keep an eye on this. + // I had to remove something similar in another module (Rex engine) because it was causing + // positional attributes (like clip planes) to re-apply with an incorrect MVM. -gw + state->apply(); +} diff --git a/Source/src/scene/TritonDrawable.h b/Source/src/scene/TritonDrawable.h new file mode 100644 index 00000000..95f28727 --- /dev/null +++ b/Source/src/scene/TritonDrawable.h @@ -0,0 +1,102 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef OSGEARTH_TRITON_DRAWABLE_H +#define OSGEARTH_TRITON_DRAWABLE_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +const unsigned int TRITON_OCEAN_MASK = 0x4; // 0100 + +namespace osgEarth { namespace Triton +{ + class TritonContext; + class TritonHeightMap; + + /** + * Custom drawable for rendering the Triton ocean effects + */ + class TritonDrawable : public osg::Drawable + { + public: + TritonDrawable(TritonContext* TRITON); + + //! Layer to use as an ocean rendering mask + void setMaskLayer(const osgEarth::ImageLayer* maskLayer); + + //! Height map generator to use to mask out the ocean where the + //! terrain has positive elevation + void setHeightMapGenerator(TritonHeightMap* heightMap); + + //! Vertical datum to use to calculate sea level + void setVerticalDatum(VerticalDatum* value) { + _verticalDatum = value; + } + + //! Gets the Triton Camera associated with an osg Camera + ::Triton::Camera* getTritonCam(const osg::Camera* cam) { + return _local.get(cam)._tritonCam; + } + + void setPlanarReflectionMap(osg::Texture2D* map); + + void setPlanarReflectionProjection(osg::RefMatrix* proj); + + public: // osg::Drawable + + void drawImplementation(osg::RenderInfo& ri) const override; + + osg::BoundingBox computeBoundingBox() const override; + + protected: + virtual ~TritonDrawable(); + + osg::observer_ptr _TRITON; + osg::observer_ptr _maskLayer; + osg::ref_ptr _cubeMap; + + osg::ref_ptr _planarReflectionMap; + osg::ref_ptr _planarReflectionProjection; + + mutable osg::ref_ptr _heightMapGenerator; + + osg::ref_ptr _verticalDatum; + osg::ref_ptr _wgs84, _ecef; + + mutable osg::buffered_object _adapters; + + struct CameraLocal + { + ::Triton::Camera* _tritonCam = nullptr; + }; + mutable PerObjectFastMap _local; + }; + +} } // namespace osgEarth::Triton + +#endif // OSGEARTH_TRITON_DRAWABLE_H diff --git a/Source/src/scene/TritonHeightMap.cpp b/Source/src/scene/TritonHeightMap.cpp new file mode 100644 index 00000000..c0057d7a --- /dev/null +++ b/Source/src/scene/TritonHeightMap.cpp @@ -0,0 +1,434 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#include "TritonHeightMap.h" +#include "TritonContext.h" +#include +#include +#include +#include + +#define LC "[TritonHeightMap] " + +using namespace osgEarth::Triton; + +namespace +{ + const char* vertexShader = + "#pragma import_defines(OE_TRITON_MASK_MATRIX);\n" + + "// terrain SDK:\n" + "float oe_terrain_getElevation(); \n" + + "out float oe_triton_elev;\n" + + "#ifdef OE_TRITON_MASK_MATRIX\n" + "out vec2 maskCoords;\n" + "uniform mat4 OE_TRITON_MASK_MATRIX;\n" + "vec4 oe_layer_tilec;\n" + "#endif\n" + + "void oe_triton_setupHeightMap(inout vec4 unused) \n" + "{ \n" + " oe_triton_elev = oe_terrain_getElevation(); \n" + "#ifdef OE_TRITON_MASK_MATRIX\n" + " maskCoords = (OE_TRITON_MASK_MATRIX * oe_layer_tilec).st;\n" + "#endif\n" + "} \n"; + + // The fragment shader simply takes the texture index that we generated + // in the vertex shader and does a texture lookup. In this case we're + // just wholesale replacing the color, so if the map had any existing + // imagery, this will overwrite it. + + const char* fragmentShader = + "#pragma import_defines(OE_TRITON_MASK_SAMPLER);\n" + + "in float oe_triton_elev;\n" + + "#ifdef OE_TRITON_MASK_SAMPLER\n" + "in vec2 maskCoords;\n" + "uniform sampler2D OE_TRITON_MASK_SAMPLER;\n" + "#endif\n" + + "out vec4 out_height; \n" + + "void oe_triton_drawHeightMap(inout vec4 unused) \n" + "{ \n" +#ifdef DEBUG_HEIGHTMAP + // Map to black = -500m, white = +500m + " float nHeight = clamp(oe_triton_elev / 1000.0 + 0.5, 0.0, 1.0);\n" +#else + " float nHeight = oe_triton_elev;\n" + + "#ifdef OE_TRITON_MASK_SAMPLER\n" + " float mask = texture(OE_TRITON_MASK_SAMPLER, maskCoords).a;\n" + " nHeight *= mask; \n" + "#endif\n" + +#endif + " out_height = vec4( nHeight, 0.0, 0.0, 1.0 ); \n" + "} \n"; + + struct TerrainDirtyCallback : public osgEarth::TerrainCallback + { + osg::observer_ptr _hm; + TerrainDirtyCallback(TritonHeightMap* hm) : _hm(hm) { } + void onTileUpdate(const osgEarth::TileKey&, osg::Node*, osgEarth::TerrainCallbackContext&) + { + osg::ref_ptr hm; + if (_hm.lock(hm)) + hm->dirty(); + } + }; +} + +TritonHeightMap::TritonHeightMap() : +_texSize(0u), +_internalFormat((GLint)0), +_sourceFormat((GLenum)0) +{ + setCullingActive(false); +} + +TritonHeightMap::~TritonHeightMap() +{ + osgEarth::TerrainEngineNode* t = dynamic_cast(_terrain.get()); + if (t) + { + t->getTerrain()->removeTerrainCallback(static_cast(_terrainCallback.get())); + _terrainCallback = NULL; + } +} + +void +TritonHeightMap::setTerrain(osg::Node* node) +{ + _terrain = node; + + osgEarth::TerrainEngineNode* t = dynamic_cast(node); + if (t) + { + TerrainDirtyCallback* cb = new TerrainDirtyCallback(this); + t->getTerrain()->addTerrainCallback(cb); + _terrainCallback = cb; + } +} + +void +TritonHeightMap::setMaskLayer(const osgEarth::ImageLayer* layer) +{ + _maskLayer = layer; +} + +void +TritonHeightMap::SetDirty::operator()(CameraLocal& local) +{ + local._mvpw.makeIdentity(); +} + +void +TritonHeightMap::dirty() +{ + SetDirty setDirty; + _local.forEach(setDirty); +} + +bool +TritonHeightMap::configure(unsigned texSize, osg::State& state) +{ + bool result = true; + + if (_texSize == 0u) + { + // first time through, single-lane and set up FBO parameters. + static std::mutex s_mutex; + std::lock_guard lock(s_mutex); + + if (_texSize == 0u) + { + _texSize = texSize; + if (!getBestFBOConfig(state, _internalFormat, _sourceFormat)) + { + result = false; + } + } + } + return result; +} + +namespace { + struct Format { + Format(GLint i, GLenum s, const std::string& n) : + internalFormat(i), sourceFormat(s), name(n) { } + GLint internalFormat; + GLenum sourceFormat; + std::string name; + }; +} + +bool +TritonHeightMap::getBestFBOConfig(osg::State& state, GLint& out_internalFormat, GLenum& out_sourceFormat) +{ +#ifdef GL_LUMINANCE_FLOAT16_ATI +# define GL_LUMINANCE_FLOAT16_ATI 0x881E +#endif + + std::vector formats; + +#ifdef GL_R16F + formats.push_back(Format(GL_R16F, GL_RED, "GL_R16F")); +#endif +#ifdef GL_LUMINANCE16F_ARB + formats.push_back(Format(GL_LUMINANCE16F_ARB, GL_LUMINANCE, "GL_LUMINANCE16F_ARB")); +#endif +#ifdef GL_LUMINANCE_FLOAT16_ATI + formats.push_back(Format(GL_LUMINANCE_FLOAT16_ATI, GL_LUMINANCE, "GL_LUMINANCE_FLOAT16_ATI")); +#endif +#ifdef GL_R32F + formats.push_back(Format(GL_R32F, GL_RED, "GL_R32F")); +#endif +#ifdef GL_LUMINANCE32F_ARB + formats.push_back(Format(GL_LUMINANCE32F_ARB, GL_LUMINANCE, "GL_LUMINANCE32F_ARB")); +#endif +#ifdef GL_RGB16F_ARB + formats.push_back(Format(GL_RGB16F_ARB, GL_RGB, "GL_RGB16F_ARB")); +#endif +#ifdef GL_RGBA16F_ARB + formats.push_back(Format(GL_RGBA16F_ARB, GL_RGBA, "GL_RGBA16F_ARB")); +#endif +#ifdef GL_RGB32F_ARB + formats.push_back(Format(GL_RGB32F_ARB, GL_RGB, "GL_RGB32F_ARB")); +#endif +#ifdef GL_RGBA32F_ARB + formats.push_back(Format(GL_RGBA32F_ARB, GL_RGBA, "GL_RGBA32F_ARB")); +#endif + + + auto cid = GLUtils::getSharedContextID(state); + osg::GLExtensions* ext = osg::GLExtensions::Get(cid, true); + + osg::State::CheckForGLErrors check = state.getCheckForGLErrors(); + state.setCheckForGLErrors(state.NEVER_CHECK_GL_ERRORS); + + bool found = false; + + for(int i=0; i tex = new osg::Texture2D(); + tex->setTextureSize(1, 1); + tex->setInternalFormat( format.internalFormat ); + tex->setSourceFormat ( format.sourceFormat ); + + osg::ref_ptr fbo = new osg::FrameBufferObject(); + fbo->setAttachment( osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(tex.get()) ); + + fbo->apply( state ); + + GLenum status = ext->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); + + fbo->releaseGLObjects( &state ); + tex->releaseGLObjects( &state ); + + if ( status == GL_FRAMEBUFFER_COMPLETE_EXT ) + { + out_internalFormat = format.internalFormat; + out_sourceFormat = format.sourceFormat; + found = true; + } + } + + state.setCheckForGLErrors(check); + + return found; +} + +bool +TritonHeightMap::isConfigurationComplete() const +{ + return + _texSize > 0u && + _internalFormat != (GLint)0 && + _sourceFormat != (GLenum)0; +} + +void +TritonHeightMap::setup(CameraLocal& local, const std::string& name) +{ + // make sure the FBO params are configured: + if (!isConfigurationComplete()) + return; + + local._frameNum = 0u; + + local._tex = new osg::Texture2D(); + local._tex->setName(Stringify() << "Triton HM (" << name << ")"); + local._tex->setTextureSize(_texSize, _texSize); + local._tex->setInternalFormat( _internalFormat ); + local._tex->setSourceFormat( _sourceFormat ); + local._tex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); + local._tex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); + + // Triton prob doesn't need this but it's good practice + if (_sourceFormat == GL_RED) + { + local._tex->setSwizzle(osg::Vec4i(GL_RED, GL_RED, GL_RED, GL_ONE)); + } + + local._rtt = new osg::Camera(); + local._rtt->setName(local._tex->getName()); + local._rtt->setReferenceFrame(osg::Transform::ABSOLUTE_RF_INHERIT_VIEWPOINT); + local._rtt->setClearMask(GL_COLOR_BUFFER_BIT); //GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + local._rtt->setClearColor(osg::Vec4(-1000.0, -1000.0, -1000.0, 1.0f)); + local._rtt->setViewport(0, 0, _texSize, _texSize); + local._rtt->setRenderOrder(osg::Camera::PRE_RENDER); + local._rtt->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + local._rtt->setImplicitBufferAttachmentMask(0, 0); + local._rtt->attach(osg::Camera::COLOR_BUFFER0, local._tex.get()); + //local._rtt->setCullMask( ~TRITON_OCEAN_MASK ); + local._rtt->setAllowEventFocus(false); + local._rtt->setDrawBuffer(GL_FRONT); + local._rtt->setReadBuffer(GL_FRONT); + local._rtt->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + + // TODO: create this once and just re-use it for all RTT cameras + osg::StateSet* rttSS = local._rtt->getOrCreateStateSet(); + + osgEarth::VirtualProgram* rttVP = osgEarth::VirtualProgram::getOrCreate(rttSS); + rttVP->setName("Triton Height Map"); + rttVP->setFunction( "oe_triton_setupHeightMap", vertexShader, VirtualProgram::LOCATION_VERTEX_MODEL); + rttVP->setFunction( "oe_triton_drawHeightMap", fragmentShader, VirtualProgram::LOCATION_FRAGMENT_OUTPUT); + rttVP->setInheritShaders(false); + + osg::StateAttribute::OverrideValue off = osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE; + rttSS->setDefine("OE_IS_DEPTH_CAMERA"); + rttSS->setDefine("OE_TERRAIN_RENDER_IMAGERY", off); + rttSS->setDefine("OE_TERRAIN_RENDER_NORMAL_MAP", off); + rttSS->setDefine("OE_TERRAIN_BLEND_IMAGERY", off); + rttSS->setDefine("OE_TERRAIN_MORPH_GEOMETRY", off); + + osg::ref_ptr maskLayer; + if (_maskLayer.lock(maskLayer)) + { + rttSS->setDefine("OE_TRITON_MASK_SAMPLER", maskLayer->getSharedTextureUniformName()); + rttSS->setDefine("OE_TRITON_MASK_MATRIX", maskLayer->getSharedTextureMatrixUniformName()); + OE_DEBUG << LC << "Using mask layer \"" << maskLayer->getName() << "\", sampler=" << maskLayer->getSharedTextureUniformName() << ", matrix=" << maskLayer->getSharedTextureMatrixUniformName() << std::endl; + } + + if (_terrain.valid()) + { + local._rtt->addChild(_terrain.get()); + } + else + { + OE_WARN << LC << "Illegal: no terrain set (must call setTerrain)" << std::endl; + } +} + +#define MAXABS4(A,B,C,D) \ + osg::maximum(fabs(A), osg::maximum(fabs(B), osg::maximum(fabs(C),fabs(D)))) + +void +TritonHeightMap::update(CameraLocal& local, const osg::Camera* cam, osgEarth::Horizon* horizon) +{ + osg::Vec3d eye = osg::Vec3d(0,0,0) * cam->getInverseViewMatrix(); + + double hd = horizon->getDistanceToVisibleHorizon(); + + local._rtt->setProjectionMatrix(osg::Matrix::ortho(-hd, hd, -hd, hd, 1.0, eye.length())); + local._rtt->setViewMatrixAsLookAt(eye, osg::Vec3d(0.0,0.0,0.0), osg::Vec3d(0.0,0.0,1.0)); + + static const osg::Matrixd scaleBias( + 0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + + // Matrix that Triton will use to position the heightmap for sampling. + local._texMatrix = local._rtt->getViewMatrix() * local._rtt->getProjectionMatrix() * scaleBias; +} + +void +TritonHeightMap::traverse(osg::NodeVisitor& nv) +{ + if (nv.getVisitorType() == nv.CULL_VISITOR) + { + osgUtil::CullVisitor* cv = osgEarth::Culling::asCullVisitor(nv); + const osg::Camera* camera = cv->getCurrentCamera(); + if (camera) + { + CameraLocal& local = _local.get(camera); + + if (isConfigurationComplete()) + { + // create the RTT for this camera on first encounter: + if (!local._rtt.valid()) + { + setup(local, camera->getName()); + } + + // only update when the MVPW changes. + if (local._mvpw != *cv->getMVPW()) + { + // update the RTT based on the current camera: + osg::ref_ptr horizon; + ObjectStorage::get(&nv, horizon); + update(local, camera, horizon); + + // finally, traverse the camera to build the height map. + local._rtt->accept(nv); + + local._frameNum = nv.getFrameStamp()->getFrameNumber(); + local._mvpw = *cv->getMVPW(); + } + } + else + { + //OE_DEBUG << LC << "Configuration not yet complete..." << std::endl; + } + } + } +} + +bool +TritonHeightMap::getTextureAndMatrix(osg::RenderInfo& ri, GLint& out_texName, osg::Matrix& out_matrix) +{ + if (!isConfigurationComplete()) + return false; + + CameraLocal& local = _local.get(ri.getCurrentCamera()); + if (!local._tex.valid()) + return false; + + // did the texture change? + //OE_DEBUG << "FN=" << ri.getState()->getFrameStamp()->getFrameNumber() << "; localFN=" << local._frameNum << std::endl; + + if (ri.getState()->getFrameStamp()->getFrameNumber() > local._frameNum) + return false; + + auto cid = GLUtils::getSharedContextID(*ri.getState()); + osg::Texture::TextureObject* obj = local._tex->getTextureObject(cid); + if (!obj) + return false; + + out_texName = obj->id(); + out_matrix = local._texMatrix; + return true; +} diff --git a/Source/src/scene/TritonHeightMap.h b/Source/src/scene/TritonHeightMap.h new file mode 100644 index 00000000..b7896a7f --- /dev/null +++ b/Source/src/scene/TritonHeightMap.h @@ -0,0 +1,109 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef OSGEARTH_TRITON_HEIGHT_MAP +#define OSGEARTH_TRITON_HEIGHT_MAP 1 + +#include +#include +#include +#include +#include + +namespace osgEarth { + class Horizon; +} + +namespace osgEarth { namespace Triton +{ + /** + * Creates a height map that Triton can use to mask out the ocean where + * the terrain is above sea level. + */ + class TritonHeightMap : public osg::Node + { + public: + + TritonHeightMap(); + + //! Sets the root of the terrain scene graph + void setTerrain(osg::Node*); + + //! Sets the masking layer + void setMaskLayer(const osgEarth::ImageLayer* layer); + + //! Configure the generator; return success t/f + bool configure(unsigned texSize, osg::State& state); + + //! Fetch the heightmap texture and matrix generated for a camera. + bool getTextureAndMatrix(osg::RenderInfo&, GLint& out_texName, osg::Matrix& out_matrix); + + //! Mark all height maps (for all cameras) for regeneration + void dirty(); + + public: // osg::Node + + void traverse(osg::NodeVisitor&); + + protected: + + virtual ~TritonHeightMap(); + + private: + + struct CameraLocal + { + osg::ref_ptr _rtt; + osg::ref_ptr _tex; + osg::Matrix _texMatrix; + osg::Matrix _mvpw; + unsigned _frameNum; + }; + + typedef osgEarth::PerObjectFastMap Locals; + + struct SetDirty : public Locals::Functor + { + void operator()(CameraLocal&); + }; + + //! Sets up an RTT camera for the first time + void setup(CameraLocal& local, const std::string& name); + + //! Updates an RTT camera for the new view/projection matrices of the camera + void update(CameraLocal& local, const osg::Camera*, osgEarth::Horizon*); + + //! Whether FBO configuration has happened yet + bool isConfigurationComplete() const; + + //! Figures out the best FBO format on this GPU + static bool getBestFBOConfig(osg::State& state, GLint& internalFormat, GLenum& sourceFormat); + + + Locals _local; + osg::observer_ptr _terrain; + unsigned _texSize; + GLint _internalFormat; + GLenum _sourceFormat; + osg::observer_ptr _maskLayer; + osg::ref_ptr _terrainCallback; + }; + +} } // namespace osgEarth::Triton + +#endif // OSGEARTH_TRITON_HEIGHT_MAP diff --git a/Source/src/scene/TritonIntersections.cpp b/Source/src/scene/TritonIntersections.cpp new file mode 100644 index 00000000..31a6615a --- /dev/null +++ b/Source/src/scene/TritonIntersections.cpp @@ -0,0 +1,51 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#include "TritonIntersections.h" + +using namespace osgEarth; +using namespace osgEarth::Triton; + +TritonIntersections::TritonIntersections() : +_maxRange(2.0, Units::KILOMETERS) +{ +} + +void +TritonIntersections::setAnchor(const GeoPoint& value) +{ + _anchor = value; + + // zero out the other stuff: + _anchor.z() = 0.0; + _anchor.altitudeMode() = ALTMODE_ABSOLUTE; +} + +void +TritonIntersections::addLocalPoint(const osg::Vec3d& p) +{ + _input.push_back(p); + _heights.resize(_input.size()); + _normals.resize(_input.size()); +} + +void +TritonIntersections::setMaxRange(const Distance& range) +{ + _maxRange = range; +} diff --git a/Source/src/scene/TritonIntersections.h b/Source/src/scene/TritonIntersections.h new file mode 100644 index 00000000..de35c76a --- /dev/null +++ b/Source/src/scene/TritonIntersections.h @@ -0,0 +1,72 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef OSGEARTH_TRITON_INTERSECTIONS +#define OSGEARTH_TRITON_INTERSECTIONS 1 + +#include +#include +#include "TritonCallback.h" + +namespace osgEarth { namespace Triton +{ + /** + * Pass this structure to TritonLayer and it will automatically + * populate the results with ocean wave intersections (local coordinates + * and normals.) + */ + class TritonIntersections : public osg::Referenced + { + public: + //! Construct an empty set + TritonIntersections(); + + //! Anchor point for intersectsions in this set. Only the X and Y + //! components are used. Any local points you add to this set will + //! be in the local coordinate system (LTP) around this anchor point. + void setAnchor(const GeoPoint& p); + + //! Adds a point to the intersection set. The point should be in the + //! local coordinate system of the anchor point. + void addLocalPoint(const osg::Vec3d& p); + + //! Maximum range at which to perform intersections. Beyond this + //! range Triton will skip this set. Default is 2km. + void setMaxRange(const Distance& value); + const Distance& getMaxRange() const { return _maxRange; } + + //! Vector of input local points added by addLocalPoint. + const std::vector& getInput() const { return _input; } + + //! Vector of heights resulting from the intersection tests. + const std::vector& getHeights() const { return _heights; } + + //! Vector of normals resulting from the intersection tests. + const std::vector& getNormals() const { return _normals; } + + private: + GeoPoint _anchor; + std::vector _input; + std::vector _heights; + std::vector _normals; + Distance _maxRange; + friend class TritonLayerNode; + }; +} } + +#endif // OSGEARTH_TRITON_INTERSECTIONS diff --git a/Source/src/scene/TritonLayer.cpp b/Source/src/scene/TritonLayer.cpp new file mode 100644 index 00000000..9f015255 --- /dev/null +++ b/Source/src/scene/TritonLayer.cpp @@ -0,0 +1,386 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#include "TritonLayer.h" +#include "TritonContext.h" +#include "TritonDrawable.h" +#include "TritonHeightMap.h" +#include "TritonCallback.h" + +#include +#include +#include +#include +#include +#include + +#include + +#define LC "[TritonLayer] " + +using namespace osgEarth::Triton; + +namespace osgEarth { namespace Triton +{ + class TritonLayerNode : public osg::Group + { + public: + TritonLayerNode(osgEarth::Triton::TritonLayer* layer, + LayerReference& mask) : + _tritonLayer(layer), + _maskLayer(mask), + _callback(0L), + _needsMapNode(true) + { + // To detect the map node: + ADJUST_UPDATE_TRAV_COUNT(this, +1); + + // Disable bounds culling + setCullingActive(false); + } + + ~TritonLayerNode() + { + //nop + } + + void setUserCallback(osgEarth::Triton::Callback* callback) + { + _callback = callback; + } + + void dirty() + { + create(); + } + + /** MapNode to use; will be discovered automatically if not set here */ + void setMapNode(osgEarth::MapNode* mapNode) + { + if (!mapNode) + { + this->removeChildren(0, this->getNumChildren()); + _drawable = 0L; + _TRITON = 0L; + _needsMapNode = true; + } + else + { + _mapNode = mapNode; + create(); + } + } + + void create() + { + this->removeChildren(0, this->getNumChildren()); + _drawable = 0L; + + osg::ref_ptr mapNode; + if (!_mapNode.lock(mapNode)) + return; + + const osgEarth::Map* map = mapNode->getMap(); + + // create an object to house Triton data and resources. + if (!_TRITON.valid()) + _TRITON = new TritonContext(_tritonLayer->options()); + + if (map) + _TRITON->setSRS(map->getSRS()); + + if (_callback.valid()) + _TRITON->setCallback(_callback.get()); + + TritonDrawable* drawable = new TritonDrawable(_TRITON.get()); + _drawable = drawable; + _drawable->setNodeMask(TRITON_OCEAN_MASK); + drawable->setMaskLayer(_maskLayer.getLayer()); + this->addChild(_drawable); + + // Place in the depth-sorted bin and set a rendering order. + // We want Triton to render after the terrain. + _drawable->getOrCreateStateSet()->setRenderBinDetails( + _tritonLayer->getRenderBinNumber(), + "DepthSortedBin"); + + // Install a vdatum for sea level calculations: + auto vdatum = VerticalDatum::get(_tritonLayer->options().vdatum().value()); + if (vdatum) + drawable->setVerticalDatum(vdatum); + + // If the user requested a height map, install it now. + // Configuration of the height map generator will take place later when + // we have a valid graphics context. + if (_tritonLayer->getUseHeightMap() == true) + { + TritonHeightMap* heightMapGen = new TritonHeightMap(); + heightMapGen->setTerrain(mapNode->getTerrainEngine()->getNode()); + if (_maskLayer.getLayer()) + heightMapGen->setMaskLayer(_maskLayer.getLayer()); + this->addChild(heightMapGen); + drawable->setHeightMapGenerator(heightMapGen); + } + } + + void traverse(osg::NodeVisitor& nv) + { + if (nv.getVisitorType() == nv.UPDATE_VISITOR) + { + // Find a MapNode in the traversal path if necessary: + if (_needsMapNode) + { + osgEarth::MapNode* mapNode = osgEarth::findInNodePath(nv); + if (mapNode) + { + setMapNode(mapNode); + _needsMapNode = false; + ADJUST_UPDATE_TRAV_COUNT(this, -1); + } + } + } + + else if (nv.getVisitorType() == nv.CULL_VISITOR && _drawable && _TRITON.valid() && _TRITON->getOcean()) + { + // Update any intersections. + // For now this is running in the CULL traversal, which is not ideal. + // However the Triton Ocean::GetHeight method requires a pointer to the Triton "camera" + // and under our framework this is only available in CULL or DRAW. + // Running the intersection in eithe CULL or DRAW will result in a frame + // incoherency w.r.t the Triton update() call that updates the ocean state. + ::Triton::Ocean* ocean = _TRITON->getOcean(); + + // Find the TritonCam associated with this osg Cam + osgUtil::CullVisitor* cv = dynamic_cast(&nv); + ::Triton::Camera* tritonCam = static_cast(_drawable)->getTritonCam(cv->getCurrentCamera()); + + osg::Vec3d eye = osg::Vec3d(0,0,0) * cv->getCurrentCamera()->getInverseViewMatrix(); + + for(std::vector >::iterator i = _isect.begin(); + i != _isect.end(); + ++i) + { + TritonIntersections* ir = i->get(); + + // allocate enough space for the output: + ir->_input.resize(ir->_input.size()); + ir->_normals.resize(ir->_input.size()); + + osg::Matrix local2world; + ir->_anchor.createLocalToWorld(local2world); + + // Make sure it's in range so as not to waste cycles: + osg::Vec3d anchor = osg::Vec3d(0,0,0) * local2world; + double m = ir->getMaxRange().as(Units::METERS); + if ((eye-anchor).length2() > (m*m)) + { + continue; + } + + osg::Matrix world2local; + world2local.invert(local2world); + + for(unsigned i=0; i_input.size(); ++i) + { + const osg::Vec3d& local = ir->_input[i]; + + // transform the ray to world coordinates + osg::Vec3d start = local * local2world; + osg::Vec3d dir = osg::Matrix::transform3x3(local2world, osg::Vec3d(0,0,1)); + + // intersect the ocean + float& out_height = ir->_heights[i]; + ::Triton::Vector3 out_normalT; + + bool ok = ocean->GetHeight( + ::Triton::Vector3(start.x(), start.y(), start.z()), + ::Triton::Vector3(dir.x(), dir.y(), dir.z()), + out_height, + out_normalT, + true, // visualCorrelation + true, // includeWakes + true, // highResolution + true, // threadSafe, + 0L, // intersectionPoint, + true, // autoFlip + tritonCam); + + if (ok) + { + // populate the output data in local coordinates + osg::Vec3d& normal = ir->_normals[i]; + normal.set(out_normalT.x, out_normalT.y, out_normalT.z); + normal = osg::Matrix::transform3x3(normal, world2local); + } + else + { + // todo...what? + OE_WARN << "GetHeight returned false dude" << std::endl; + } + } + } + } + + osg::Group::traverse(nv); + } + + osg::ref_ptr _TRITON; + osg::Drawable* _drawable; + LayerReference& _maskLayer; + osg::observer_ptr _mapNode; + osg::observer_ptr _tritonLayer; + osg::ref_ptr _callback; + bool _needsMapNode; + std::vector > _isect; + + }; +} } + +//........................................................................ + +void +TritonLayer::Options::fromConfig(const osgEarth::Config& conf) +{ + conf.get("user", _user); + conf.get("license_code", _licenseCode); + conf.get("resource_path", _resourcePath); + conf.get("use_height_map", _useHeightMap); + conf.get("height_map_size", _heightMapSize); + conf.get("render_bin_number", _renderBinNumber); + conf.get("max_altitude", _maxAltitude); + conf.get("vdatum", vdatum()); + maskLayer().get(conf, "mask_layer"); +} + +osgEarth::Config +TritonLayer::Options::getConfig() const +{ + osgEarth::Config conf = osgEarth::VisibleLayer::Options::getConfig(); + conf.set("user", _user); + conf.set("license_code", _licenseCode); + conf.set("resource_path", _resourcePath); + conf.set("use_height_map", _useHeightMap); + conf.set("height_map_size", _heightMapSize); + conf.set("render_bin_number", _renderBinNumber); + conf.set("max_altitude", _maxAltitude); + conf.set("vdatum", vdatum()); + maskLayer().set(conf, "mask_layer"); + + return conf; +} + +//........................................................................ + +/** Register this layer so it can be used in an earth file */ +namespace osgEarth { namespace Triton +{ + REGISTER_OSGEARTH_LAYER(triton, TritonLayer); + REGISTER_OSGEARTH_LAYER(triton_ocean, TritonLayer); +} } + +OE_LAYER_PROPERTY_IMPL(TritonLayer, std::string, UserName, user); +OE_LAYER_PROPERTY_IMPL(TritonLayer, std::string, LicenseCode, licenseCode); +OE_LAYER_PROPERTY_IMPL(TritonLayer, std::string, ResourcePath, resourcePath); +OE_LAYER_PROPERTY_IMPL(TritonLayer, bool, UseHeightMap, useHeightMap); +OE_LAYER_PROPERTY_IMPL(TritonLayer, unsigned, HeightMapSize, heightMapSize); +OE_LAYER_PROPERTY_IMPL(TritonLayer, int, RenderBinNumber, renderBinNumber); +OE_LAYER_PROPERTY_IMPL(TritonLayer, float, MaxAltitude, maxAltitude); +OE_LAYER_PROPERTY_IMPL(TritonLayer, std::string, VerticalDatum, vdatum); + +void +TritonLayer::init() +{ + super::init(); + + _seaLevel = 0.0f; + + // Trick to force the VisibleLayer to install its opacity shader, + // which a modified Triton user-functions.glsl shader needs in order to control + // sea surface opacity. + float opacity = getOpacity(); + setOpacity(0.0f); + setOpacity(opacity); + + this->setName("Triton"); + setRenderType(RENDERTYPE_CUSTOM); + + ElevationLOD* lod = new ElevationLOD(); + _root = lod; + if (options().maxAltitude().isSet()) + { + OE_DEBUG << LC << "Setting max altitude = " << options().maxAltitude().get() << std::endl; + lod->setMaxElevation(options().maxAltitude().get()); + } + + _tritonNode = new TritonLayerNode(this, options().maskLayer()); + _root->addChild(_tritonNode.get()); +} + +void +TritonLayer::setUserCallback(Callback* callback) +{ + static_cast(_tritonNode.get())->setUserCallback(callback); +} + +osg::Node* +TritonLayer::getNode() const +{ + return _root.get(); +} + +void +TritonLayer::setMaskLayer(osgEarth::ImageLayer* maskLayer) +{ + options().maskLayer().setLayer(maskLayer); + static_cast(_tritonNode.get())->dirty(); +} + +osgEarth::ImageLayer* +TritonLayer::getMaskLayer() const +{ + return options().maskLayer().getLayer(); +} + +void +TritonLayer::addedToMap(const osgEarth::Map* map) +{ + VisibleLayer::addedToMap(map); + options().maskLayer().addedToMap(map); +} + +void +TritonLayer::removedFromMap(const osgEarth::Map* map) +{ + VisibleLayer::removedFromMap(map); + options().maskLayer().removedFromMap(map); + setMaskLayer(0L); +} + +void +TritonLayer::addIntersections(TritonIntersections* value) +{ + TritonLayerNode* node = static_cast(_tritonNode.get()); + node->_isect.push_back(value); +} + +osgEarth::Config +TritonLayer::getConfig() const +{ + osgEarth::Config c = osgEarth::VisibleLayer::getConfig(); + return c; +} diff --git a/Source/src/scene/TritonLayer.h b/Source/src/scene/TritonLayer.h new file mode 100644 index 00000000..d5b6cd0c --- /dev/null +++ b/Source/src/scene/TritonLayer.h @@ -0,0 +1,125 @@ +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef OSGEARTH_TRITON_LAYER +#define OSGEARTH_TRITON_LAYER 1 + +#include +#include +#include +#include "TritonCallback.h" +#include "TritonIntersections.h" + +namespace osgEarth { namespace Triton +{ + /** + * Node that roots the Triton adapter. + */ + class TritonLayer : public osgEarth::VisibleLayer + { + public: + class Options : public osgEarth::VisibleLayer::Options { + public: + META_LayerOptions(osgEarth, Options, osgEarth::VisibleLayer::Options); + OE_OPTION(std::string, user); + OE_OPTION(std::string, licenseCode); + OE_OPTION(std::string, resourcePath); + OE_OPTION(bool, useHeightMap, true); + OE_OPTION(unsigned, heightMapSize, 1024); + OE_OPTION(int, renderBinNumber, 12); + OE_OPTION(float, maxAltitude, 50000.0f); + OE_OPTION(std::string, vdatum, "egm96"); + OE_OPTION_LAYER(osgEarth::ImageLayer, maskLayer); + virtual Config getConfig() const; + private: + void fromConfig(const Config& conf); + }; + + public: + META_Layer(osgEarth, TritonLayer, Options, osgEarth::VisibleLayer, triton); + + //! Sets the user callback that's invoked when Triton start up + void setUserCallback(Triton::Callback* callback); + + //! User name for license + void setUserName(const std::string& value); + const std::string& getUserName() const; + + //! License code + void setLicenseCode(const std::string& value); + const std::string& getLicenseCode() const; + + //! Triton resource path + void setResourcePath(const std::string& value); + const std::string& getResourcePath() const; + + //! Whether to use a height map to fade out the ocean at the coastline + void setUseHeightMap(const bool& value); + const bool& getUseHeightMap() const; + + //! Size in texels of the height map (each dimension) + void setHeightMapSize(const unsigned& value); + const unsigned& getHeightMapSize() const; + + //! Render bin number to use for the ocean rendering + void setRenderBinNumber(const int& value); + const int& getRenderBinNumber() const; + + //! Masking layer for the ocean + void setMaskLayer(osgEarth::ImageLayer* maskLayer); + osgEarth::ImageLayer* getMaskLayer() const; + + //! Maximum visibility altitude + void setMaxAltitude(const float& value); + const float& getMaxAltitude() const; + + //! Vertical datum to use to calculate sea level + void setVerticalDatum(const std::string& value); + const std::string& getVerticalDatum() const; + + //! Adds an intersection set. + //! Each frame, Triton will perform intersections against the ocean surface + //! (including the waves) and populate the set with the results. + void addIntersections(TritonIntersections*); + + public: // Layer + + virtual osg::Node* getNode() const; + + //! Serialize + virtual Config getConfig() const; + + protected: // Layer + + virtual void init(); + + virtual void addedToMap(const class osgEarth::Map*); + + virtual void removedFromMap(const class osgEarth::Map*); + + private: + + osg::ref_ptr _root; + osg::ref_ptr _tritonNode; + float _seaLevel; + float _opacity; + }; + +} } // namespace osgEarth::Triton + +#endif // OSGEARTH_TRITON_LAYER diff --git a/Source/src/workspace/WorkSpace.cpp b/Source/src/workspace/WorkSpace.cpp index 4feea23d..a3cd8a61 100644 --- a/Source/src/workspace/WorkSpace.cpp +++ b/Source/src/workspace/WorkSpace.cpp @@ -12,7 +12,6 @@ #include "xml/tinyxml2.h" #include "common/SpdLogger.h" #include "entities/Entity.h" -#include "scene/OsgScene.h" //#include "workspace/WorkSpaceItemGroup.h" //#include "workspace/WorkSpaceRiverGroup.h" //#include "workspace/WorkSpaceRiverNetGroup.h" @@ -45,18 +44,29 @@ bool WorkSpace::CreateScene(const std::string& scene) { return false; } - /*if (nullptr != activeScene_) { + if (nullptr != activeScene_) { activeScene_->DetachView(view_); - }*/ + } - /* name_ = name_.fromStdString(scene); + name_ = name_.fromStdString(scene); +#if 0 activeScene_ = new OsgScene; activeScene_->UseShadows(false); activeScene_->ChangeScene(OsgScene::CLOUDY); activeScene_->AttachView(view_); - activeScene_->InitEventHandle(view_);*/ + activeScene_->InitEventHandle(view_); +#else + activeScene_ = new OEScene; + activeScene_->UseShadows(false); + + activeScene_->ChangeScene(OsgScene::CLOUDY); + + activeScene_->AttachView(view_); + activeScene_->InitEventHandle(view_); +#endif + return true; } diff --git a/Source/src/workspace/WorkSpace.h b/Source/src/workspace/WorkSpace.h index c54b5771..78f99013 100644 --- a/Source/src/workspace/WorkSpace.h +++ b/Source/src/workspace/WorkSpace.h @@ -4,6 +4,7 @@ #include #include "scene/OsgScene.h" +#include "scene/OEScene.h" //#include "../ui/chartPlot/DYTChart.h" @@ -84,7 +85,7 @@ private: QString path_; std::vector entities_; - //osg::ref_ptr activeScene_; + osg::ref_ptr activeScene_; class OsgView* view_{ nullptr }; class Timestep* timestep_{ nullptr }; class LampStatus* lampStatus_{ nullptr };