diff --git a/CHANGELOG.md b/CHANGELOG.md index a9ec5a946e..7e136d707a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ Feature #5489: MCP: Telekinesis fix for activators Feature #5996: Support Lua scripts in OpenMW Feature #6017: Separate persistent and temporary cell references when saving + Feature #6032: Reverse-z depth buffer Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly 0.47.0 diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index c902ceb88a..e4ce2a9593 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -8,14 +8,12 @@ #include "MyGUI_FactoryManager.h" #include -#include -#include -#include -#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwrender/util.hpp" + namespace MWGui { struct TypesetBookImpl; @@ -1221,7 +1219,7 @@ public: RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); - float z = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ() ? 1.0 : -1.0; + float z = MWRender::getReverseZ() ? 1.f : -1.f; GlyphStream glyphStream(textFormat.mFont, static_cast(mCoord.left), static_cast(mCoord.top - mViewTop), z /*mNode->getNodeDepth()*/, vertices, renderXform); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 538b866cac..efb434d3bc 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -741,7 +741,7 @@ namespace MWGui } // ------------------------------------------------------------------------------------------ - MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue, bool reverseZ) + MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue) : WindowPinnableBase("openmw_map_window.layout") , LocalMapBase(customMarkers, localMapRender) , NoDrop(drag, mMainWidget) @@ -751,7 +751,7 @@ namespace MWGui , mGlobal(Settings::Manager::getBool("global", "Map")) , mEventBoxGlobal(nullptr) , mEventBoxLocal(nullptr) - , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot(), workQueue, reverseZ)) + , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot(), workQueue)) , mEditNoteDialog() { static bool registered = false; diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index b7664505af..cb0d368b30 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -222,7 +222,7 @@ namespace MWGui class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop { public: - MapWindow(CustomMarkerCollection& customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue, bool reverseZ); + MapWindow(CustomMarkerCollection& customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue); virtual ~MapWindow(); void setCellName(const std::string& cellName); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5feed41864..e42d606966 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -35,7 +35,6 @@ #include #include -#include #include @@ -305,9 +304,8 @@ namespace MWGui mGuiModeStates[GM_MainMenu] = GuiModeState(menu); mWindows.push_back(menu); - bool reverseZ = mResourceSystem->getSceneManager()->getReverseZ(); - mLocalMapRender = new MWRender::LocalMap(mViewer->getSceneData()->asGroup(), reverseZ); - mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender, mWorkQueue, reverseZ); + mLocalMapRender = new MWRender::LocalMap(mViewer->getSceneData()->asGroup()); + mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender, mWorkQueue); mWindows.push_back(mMap); mMap->renderGlobalMap(); trackWindow(mMap, "map"); diff --git a/apps/openmw/mwrender/actorspaths.cpp b/apps/openmw/mwrender/actorspaths.cpp index 35b2553555..4e3bfd79a6 100644 --- a/apps/openmw/mwrender/actorspaths.cpp +++ b/apps/openmw/mwrender/actorspaths.cpp @@ -2,9 +2,13 @@ #include "vismask.hpp" #include +#include +#include #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" namespace MWRender { ActorsPaths::ActorsPaths(const osg::ref_ptr& root, bool enabled) @@ -43,6 +47,7 @@ namespace MWRender const auto newGroup = SceneUtil::createAgentPathGroup(path, halfExtents, start, end, settings); if (newGroup) { + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(newGroup, "debug"); newGroup->setNodeMask(Mask_Debug); mRootNode->addChild(newGroup); mGroups[actor] = newGroup; diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index 207b0ee504..9039ec53ce 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -10,10 +10,19 @@ #include #include #include +#include #include "bulletdebugdraw.hpp" #include "vismask.hpp" +#include +#include + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + namespace MWRender { @@ -30,7 +39,6 @@ void DebugDrawer::createGeometry() { mGeometry = new osg::Geometry; mGeometry->setNodeMask(Mask_Debug); - mVertices = new osg::Vec3Array; mColors = new osg::Vec4Array; @@ -42,6 +50,8 @@ void DebugDrawer::createGeometry() mGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); mGeometry->setDataVariance(osg::Object::DYNAMIC); mGeometry->addPrimitiveSet(mDrawArrays); + // make this friendly to recreateShaders() + mGeometry->setStateSet(new osg::StateSet); mParentNode->addChild(mGeometry); @@ -52,6 +62,8 @@ void DebugDrawer::createGeometry() mShapesRoot->setDataVariance(osg::Object::DYNAMIC); mShapesRoot->setNodeMask(Mask_Debug); mParentNode->addChild(mShapesRoot); + + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(mGeometry, "debug"); } } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c4c628e04d..580451ff8f 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -168,7 +168,7 @@ namespace MWRender mCamera->setRenderOrder(osg::Camera::PRE_RENDER); mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture, 0, 0, false, Settings::Manager::getInt("antialiasing", "Video")); mCamera->setName("CharacterPreview"); - mCamera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); + mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); mCamera->setCullMask(~(Mask_UpdateVisitor)); mCamera->setNodeMask(Mask_RenderToTexture); @@ -187,6 +187,7 @@ namespace MWRender defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); stateset->setAttribute(defaultMat); + stateset->addUniform(new osg::Uniform("projectionMatrix", static_cast(mCamera->getProjectionMatrix()))); osg::ref_ptr depth = new osg::Depth; stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index f9bbb3d2f4..2f160933fd 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -25,6 +25,7 @@ #include "../mwworld/esmstore.hpp" #include "vismask.hpp" +#include "util.hpp" namespace { @@ -220,14 +221,13 @@ namespace MWRender osg::ref_ptr mOverlayTexture; }; - GlobalMap::GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue, bool reverseZ) + GlobalMap::GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue) : mRoot(root) , mWorkQueue(workQueue) , mWidth(0) , mHeight(0) , mMinX(0), mMaxX(0) , mMinY(0), mMaxY(0) - , mReverseZ(reverseZ) { mCellSize = Settings::Manager::getInt("global map cell size", "Map"); @@ -325,7 +325,7 @@ namespace MWRender if (texture) { osg::ref_ptr geom = createTexturedQuad(srcLeft, srcTop, srcRight, srcBottom); - auto depth = SceneUtil::createDepth(mReverseZ); + auto depth = SceneUtil::createDepth(getReverseZ()); depth->setWriteMask(0); osg::StateSet* stateset = geom->getOrCreateStateSet(); stateset->setAttribute(depth); diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index a27621c9f4..fd8a8d1016 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -33,7 +33,7 @@ namespace MWRender class GlobalMap { public: - GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue, bool reverseZ); + GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue); ~GlobalMap(); void render(); @@ -126,8 +126,6 @@ namespace MWRender int mHeight; int mMinX, mMaxX, mMinY, mMaxY; - - bool mReverseZ; }; } diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index e0b3d6c69b..72f2828ace 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -30,6 +30,7 @@ #include "../mwworld/cellstore.hpp" #include "vismask.hpp" +#include "util.hpp" namespace { @@ -83,14 +84,13 @@ namespace namespace MWRender { -LocalMap::LocalMap(osg::Group* root, bool reverseZ) +LocalMap::LocalMap(osg::Group* root) : mRoot(root) , mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) , mMapWorldSize(Constants::CellSizeInUnits) , mCellDistance(Constants::CellGridRadius) , mAngle(0.f) , mInterior(false) - , mReverseZ(reverseZ) { // Increase map resolution, if use UI scaling float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor(); @@ -178,7 +178,7 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f { osg::ref_ptr camera (new osg::Camera); - if (mReverseZ) + if (getReverseZ()) camera->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10)); else camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); @@ -201,13 +201,15 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f osg::ref_ptr stateset = new osg::StateSet; stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE); - if (mReverseZ) + if (getReverseZ()) { camera->setClearDepth(0.0); - auto depth = SceneUtil::createDepth(mReverseZ); + auto depth = SceneUtil::createDepth(true); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } + stateset->addUniform(new osg::Uniform("projectionMatrix", static_cast(camera->getProjectionMatrix())), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); + // assign large value to effectively turn off fog // shaders don't respect glDisable(GL_FOG) osg::ref_ptr fog (new osg::Fog); diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 316cbd53d3..f9ccd5a011 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -36,7 +36,7 @@ namespace MWRender class LocalMap { public: - LocalMap(osg::Group* root, bool reverseZ); + LocalMap(osg::Group* root); ~LocalMap(); /** @@ -156,7 +156,6 @@ namespace MWRender void setupRenderToTexture(osg::ref_ptr camera, int x, int y); bool mInterior; - bool mReverseZ; osg::BoundingBox mBounds; }; diff --git a/apps/openmw/mwrender/navmesh.cpp b/apps/openmw/mwrender/navmesh.cpp index 791c41a1a0..523f7531af 100644 --- a/apps/openmw/mwrender/navmesh.cpp +++ b/apps/openmw/mwrender/navmesh.cpp @@ -2,9 +2,14 @@ #include "vismask.hpp" #include +#include +#include #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + namespace MWRender { NavMesh::NavMesh(const osg::ref_ptr& root, bool enabled) @@ -45,6 +50,7 @@ namespace MWRender mGroup = SceneUtil::createNavMeshGroup(navMesh, settings); if (mGroup) { + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(mGroup, "debug"); mGroup->setNodeMask(Mask_Debug); mRootNode->addChild(mGroup); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d103234f31..64dc9ecfe0 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -43,6 +43,7 @@ #include "rotatecontroller.hpp" #include "renderbin.hpp" #include "vismask.hpp" +#include "util.hpp" namespace { @@ -370,9 +371,9 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) class DepthClearCallback : public osgUtil::RenderBin::DrawCallback { public: - DepthClearCallback(bool reverseZ) + DepthClearCallback() { - mDepth = SceneUtil::createDepth(reverseZ); + mDepth = SceneUtil::createDepth(getReverseZ()); mDepth->setWriteMask(true); } @@ -432,7 +433,7 @@ void NpcAnimation::setRenderBin() if (!prototypeAdded) { osg::ref_ptr depthClearBin (new osgUtil::RenderBin); - depthClearBin->setDrawCallback(new DepthClearCallback(mResourceSystem->getSceneManager()->getReverseZ())); + depthClearBin->setDrawCallback(new DepthClearCallback); osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); prototypeAdded = true; } diff --git a/apps/openmw/mwrender/pathgrid.cpp b/apps/openmw/mwrender/pathgrid.cpp index c20e81bb2d..42d9150811 100644 --- a/apps/openmw/mwrender/pathgrid.cpp +++ b/apps/openmw/mwrender/pathgrid.cpp @@ -8,7 +8,10 @@ #include #include +#include #include +#include +#include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" @@ -112,6 +115,8 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store) osg::ref_ptr geometry = SceneUtil::createPathgridGeometry(*pathgrid); + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(geometry, "debug"); + cellPathGrid->addChild(geometry); mPathGridRoot->addChild(cellPathGrid); diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 43765a25ba..a412ca8cfa 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -9,6 +9,11 @@ #include #include +#include +#include + +#include "vismask.hpp" +#include "renderingmanager.hpp" namespace { @@ -57,6 +62,8 @@ namespace traverse(node, nv); } + + private: MWRender::PostProcessor* mPostProcessor; unsigned int mLastFrameNumber; }; @@ -70,6 +77,7 @@ namespace void resizedImplementation(osg::GraphicsContext* gc, int x, int y, int width, int height) override { + gc->resizedImplementation(x, y, width, height); mPostProcessor->resize(width, height); } @@ -79,22 +87,55 @@ namespace namespace MWRender { - PostProcessor::PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode) + PostProcessor::PostProcessor(RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode) : mViewer(viewer) , mRootNode(new osg::Group) + , mRendering(rendering) { + osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext(); + unsigned int contextID = gc->getState()->getContextID(); + osg::GLExtensions* ext = gc->getState()->get(); + + constexpr char errPreamble[] = "Postprocessing and floating point depth buffers disabled: "; + + if (!ext->isFrameBufferObjectSupported) + { + Log(Debug::Warning) << errPreamble << "FrameBufferObject unsupported."; + return; + } + + if (Settings::Manager::getInt("antialiasing", "Video") > 1 && !ext->isRenderbufferMultisampleSupported()) + { + Log(Debug::Warning) << errPreamble << "RenderBufferMultiSample unsupported. Disabling antialiasing will resolve this issue."; + return; + } + + if (osg::isGLExtensionSupported(contextID, "GL_ARB_depth_buffer_float")) + mDepthFormat = GL_DEPTH_COMPONENT32F; + else if (osg::isGLExtensionSupported(contextID, "GL_NV_depth_buffer_float")) + mDepthFormat = GL_DEPTH_COMPONENT32F_NV; + else + { + // TODO: Once we have post-processing implemented we want to skip this return and continue with setup. + // Rendering to a FBO to fullscreen geometry has overhead (especially when MSAA is enabled) and there are no + // benefits if no floating point depth formats are supported. + mDepthFormat = GL_DEPTH_COMPONENT24; + Log(Debug::Warning) << errPreamble << "'GL_ARB_depth_buffer_float' and 'GL_NV_depth_buffer_float' unsupported."; + return; + } + int width = viewer->getCamera()->getViewport()->width(); int height = viewer->getCamera()->getViewport()->height(); createTexturesAndCamera(width, height); - resize(width, height); + resize(width, height, true); mRootNode->addChild(mHUDCamera); mRootNode->addChild(rootNode); mViewer->setSceneData(mRootNode); - // Main camera is treated specially, we need to manually set the FBO and - // resolve FBO during the cull callback. + // We need to manually set the FBO and resolve FBO during the cull callback. If we were using a separate + // RTT camera this would not be needed. mViewer->getCamera()->addCullCallback(new CullCallback(this)); mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); mViewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, mSceneTex); @@ -103,12 +144,12 @@ namespace MWRender mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this)); } - void PostProcessor::resize(int width, int height) + void PostProcessor::resize(int width, int height, bool init) { mDepthTex->setTextureSize(width, height); mSceneTex->setTextureSize(width, height); - mDepthTex->dirtyTextureObject(); - mSceneTex->dirtyTextureObject(); + mDepthTex->dirtyTextureObject(); + mSceneTex->dirtyTextureObject(); int samples = Settings::Manager::getInt("antialiasing", "Video"); @@ -130,15 +171,13 @@ namespace MWRender mMsaaFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(depthRB)); } - double prevWidth = mViewer->getCamera()->getViewport()->width(); - double prevHeight = mViewer->getCamera()->getViewport()->height(); - double scaleX = prevWidth / width; - double scaleY = prevHeight / height; + if (init) + return; - mViewer->getCamera()->resize(width,height); - mHUDCamera->resize(width,height); + mViewer->getCamera()->resize(width, height); + mHUDCamera->resize(width, height); - mViewer->getCamera()->getProjectionMatrix() *= osg::Matrix::scale(scaleX, scaleY, 1.0); + mRendering.updateProjectionMatrix(); } void PostProcessor::createTexturesAndCamera(int width, int height) @@ -146,8 +185,8 @@ namespace MWRender mDepthTex = new osg::Texture2D; mDepthTex->setTextureSize(width, height); mDepthTex->setSourceFormat(GL_DEPTH_COMPONENT); - mDepthTex->setSourceType(GL_FLOAT); - mDepthTex->setInternalFormat(GL_DEPTH_COMPONENT32F); + mDepthTex->setSourceType(SceneUtil::isFloatingPointDepthFormat(getDepthFormat()) ? GL_FLOAT : GL_UNSIGNED_INT); + mDepthTex->setInternalFormat(mDepthFormat); mDepthTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); mDepthTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); mDepthTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); @@ -206,6 +245,7 @@ namespace MWRender program->addShader(fragShader); mHUDCamera->addChild(createFullScreenTri()); + mHUDCamera->setNodeMask(Mask_RenderToTexture); auto* stateset = mHUDCamera->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, mSceneTex, osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/postprocessor.hpp b/apps/openmw/mwrender/postprocessor.hpp index c9d353f8cb..5be58f33ac 100644 --- a/apps/openmw/mwrender/postprocessor.hpp +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -18,15 +18,19 @@ namespace osgViewer namespace MWRender { + class RenderingManager; + class PostProcessor { public: - PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode); + PostProcessor(RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode); auto getMsaaFbo() { return mMsaaFbo; } auto getFbo() { return mFbo; } - void resize(int width, int height); + int getDepthFormat() { return mDepthFormat; } + + void resize(int width, int height, bool init=false); private: osgViewer::Viewer* mViewer; @@ -39,7 +43,11 @@ namespace MWRender osg::ref_ptr mSceneTex; osg::ref_ptr mDepthTex; + int mDepthFormat; + void createTexturesAndCamera(int width, int height); + + RenderingManager& mRendering; }; } diff --git a/apps/openmw/mwrender/recastmesh.cpp b/apps/openmw/mwrender/recastmesh.cpp index 7c7c6b8eb1..5afa78cd93 100644 --- a/apps/openmw/mwrender/recastmesh.cpp +++ b/apps/openmw/mwrender/recastmesh.cpp @@ -1,11 +1,15 @@ #include "recastmesh.hpp" #include +#include +#include #include #include "vismask.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" namespace MWRender { RecastMesh::RecastMesh(const osg::ref_ptr& root, bool enabled) @@ -49,6 +53,7 @@ namespace MWRender || it->second.mRevision != tile->second->getRevision()) { const auto group = SceneUtil::createRecastMeshGroup(*tile->second, settings); + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(group, "debug"); group->setNodeMask(Mask_Debug); mRootNode->removeChild(it->second.mValue); mRootNode->addChild(group); @@ -66,6 +71,7 @@ namespace MWRender if (mGroups.count(tile.first)) continue; const auto group = SceneUtil::createRecastMeshGroup(*tile.second, settings); + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(group, "debug"); group->setNodeMask(Mask_Debug); mGroups.emplace(tile.first, Group {tile.second->getGeneration(), tile.second->getRevision(), group}); mRootNode->addChild(group); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 65aa4a5a6b..c64bcd3383 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -74,6 +74,45 @@ namespace MWRender { + class SharedUniformStateUpdater : public SceneUtil::StateSetUpdater + { + public: + SharedUniformStateUpdater() + : mLinearFac(0.f) + { + } + + void setDefaults(osg::StateSet *stateset) override + { + stateset->addUniform(new osg::Uniform("projectionMatrix", osg::Matrixf{})); + stateset->addUniform(new osg::Uniform("linearFac", 0.f)); + } + + void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override + { + auto* uProjectionMatrix = stateset->getUniform("projectionMatrix"); + if (uProjectionMatrix) + uProjectionMatrix->set(mProjectionMatrix); + + auto* uLinearFac = stateset->getUniform("linearFac"); + if (uLinearFac) + uLinearFac->set(mLinearFac); + } + + void setProjectionMatrix(const osg::Matrixf& projectionMatrix) + { + mProjectionMatrix = projectionMatrix; + } + + void setLinearFac(float linearFac) + { + mLinearFac = linearFac; + } + + private: + osg::Matrixf mProjectionMatrix; + float mLinearFac; + }; class StateUpdater : public SceneUtil::StateSetUpdater { @@ -202,10 +241,7 @@ namespace MWRender , mFieldOfViewOverride(0.f) { auto ext = osg::GLExtensions::Get(0, false); - bool reverseZ = ext && ext->isClipControlSupported; - - if (getenv("OPENMW_DISABLE_REVERSEZ") != nullptr) - reverseZ = false; + bool reverseZ = Settings::Manager::getBool("reverse z", "Camera") && ext && ext->isClipControlSupported; if (reverseZ) Log(Debug::Info) << "Using reverse-z depth buffer"; @@ -219,7 +255,8 @@ namespace MWRender bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows") - || lightingMethod != SceneUtil::LightingMethod::FFP; + || lightingMethod != SceneUtil::LightingMethod::FFP + || reverseZ; resourceSystem->getSceneManager()->setForceShaders(forceShaders); // FIXME: calling dummy method because terrain needs to know whether lighting is clamped resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders")); @@ -370,6 +407,10 @@ namespace MWRender // Use a stub grid to avoid splitting between chunks for active grid and chunks for distant cells. mGroundcoverWorld->setActiveGrid(osg::Vec4i(0, 0, 0, 0)); } + + mPostProcessor.reset(new PostProcessor(*this, viewer, mRootNode)); + resourceSystem->getSceneManager()->setDepthFormat(mPostProcessor->getDepthFormat()); + // water goes after terrain for correct waterculling order mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); @@ -412,6 +453,9 @@ namespace MWRender mStateUpdater = new StateUpdater; sceneRoot->addUpdateCallback(mStateUpdater); + mSharedUniformStateUpdater = new SharedUniformStateUpdater; + rootNode->addUpdateCallback(mSharedUniformStateUpdater); + osg::Camera::CullingMode cullingMode = osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING; if (!Settings::Manager::getBool("small feature culling", "Camera")) @@ -433,7 +477,9 @@ namespace MWRender NifOsg::Loader::setReverseZ(reverseZ); Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models")); - mNearClip = Settings::Manager::getFloat("near clip", "Camera"); + // TODO: Near clip should not need to be bounded like this, but too small values break OSG shadow calculations CPU-side. + // See issue: #6072 + mNearClip = std::max(0.005f, Settings::Manager::getFloat("near clip", "Camera")); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); float fov = Settings::Manager::getFloat("field of view", "Camera"); mFieldOfView = std::min(std::max(1.f, fov), 179.f); @@ -462,11 +508,6 @@ namespace MWRender mRootNode->getOrCreateStateSet()->setAttributeAndModes(clipcontrol, osg::StateAttribute::ON); } - if (ext && ext->isFrameBufferObjectSupported) - mPostProcessor.reset(new PostProcessor(viewer, mRootNode)); - else - Log(Debug::Warning) << "Disabling postprocessing and using default framebuffer for rendering: FrameBufferObjects not supported"; - updateProjectionMatrix(); } @@ -1100,19 +1141,15 @@ namespace MWRender if (mFieldOfViewOverridden) fov = mFieldOfViewOverride; + mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); + if (mResourceSystem->getSceneManager()->getReverseZ()) { - mViewer->getCamera()->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspectiveInf(fov, aspect, mNearClip)); - float linearFac = -mNearClip / (mViewDistance - mNearClip) - 1.0; - mRootNode->getOrCreateStateSet()->getOrCreateUniform("linearFac", osg::Uniform::FLOAT, 1)->set(linearFac); - - osg::Matrix shadowProj = osg::Matrix::perspective(fov, aspect, mNearClip, mViewDistance); - mViewer->getCamera()->setUserValue("shadowProj", shadowProj); - mViewer->getCamera()->setUserValue("near", static_cast(mNearClip)); - mViewer->getCamera()->setUserValue("far", static_cast(mViewDistance)); + mSharedUniformStateUpdater->setLinearFac(-mNearClip / (mViewDistance - mNearClip) - 1.f); + mSharedUniformStateUpdater->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance)); } else - mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); + mSharedUniformStateUpdater->setProjectionMatrix(mViewer->getCamera()->getProjectionMatrix()); mUniformNear->set(mNearClip); mUniformFar->set(mViewDistance); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b63841494b..cbe25addf1 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -72,6 +72,7 @@ namespace MWRender { class GroundcoverUpdater; class StateUpdater; + class SharedUniformStateUpdater; class EffectManager; class ScreenshotManager; @@ -240,9 +241,9 @@ namespace MWRender void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); bool pagingUnlockCache(); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); - - private: + void updateProjectionMatrix(); + private: void updateTextureFiltering(); void updateAmbient(); void setFogColor(const osg::Vec4f& color); @@ -296,6 +297,7 @@ namespace MWRender osg::Vec3f mCurrentCameraPos; osg::ref_ptr mStateUpdater; + osg::ref_ptr mSharedUniformStateUpdater; osg::Vec4f mAmbientColor; float mMinimumAmbientLuminance; diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index ba4282e5b6..967e282f30 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -298,7 +298,7 @@ namespace MWRender camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT,osg::Camera::PIXEL_BUFFER_RTT); - if (MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ()) + if (getReverseZ()) camera->setClearDepth(0.0); camera->setViewport(0, 0, w, h); @@ -336,7 +336,7 @@ namespace MWRender float nearClip = Settings::Manager::getFloat("near clip", "Camera"); float viewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); // each cubemap side sees 90 degrees - if (MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ()) + if (getReverseZ()) rttCamera->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspectiveInf(90.0, w/float(h), nearClip)); else rttCamera->setProjectionMatrixAsPerspective(90.0, w/float(h), nearClip, viewDistance); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 604e417a45..dd97f79eda 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -54,6 +54,7 @@ #include "vismask.hpp" #include "renderbin.hpp" +#include "util.hpp" namespace { @@ -473,7 +474,7 @@ const float CelestialBody::mDistance = 1000.0f; class Sun : public CelestialBody { public: - Sun(osg::Group* parentNode, Resource::ImageManager& imageManager, bool reverseZ) + Sun(osg::Group* parentNode, Resource::ImageManager& imageManager) : CelestialBody(parentNode, 1.0f, 1, Mask_Sun) , mUpdater(new Updater) { @@ -502,8 +503,8 @@ public: mTransform->addChild(queryNode); - mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true, reverseZ); - mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false, reverseZ); + mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true); + mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false); createSunFlash(imageManager); createSunGlare(); @@ -556,7 +557,7 @@ private: }; /// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels. - osg::ref_ptr createOcclusionQueryNode(osg::Group* parent, bool queryVisible, bool reverseZ) + osg::ref_ptr createOcclusionQueryNode(osg::Group* parent, bool queryVisible) { osg::ref_ptr oqn = new osg::OcclusionQueryNode; oqn->setQueriesEnabled(true); @@ -594,11 +595,11 @@ private: osg::StateSet* queryStateSet = new osg::StateSet; if (queryVisible) { - auto depth = SceneUtil::createDepth(reverseZ); + auto depth = SceneUtil::createDepth(getReverseZ()); // This is a trick to make fragments written by the query always use the maximum depth value, // without having to retrieve the current far clipping distance. // We want the sun glare to be "infinitely" far away. - float far = reverseZ ? 0.0 : 1.0; + double far = getReverseZ() ? 0.0 : 1.0; depth->setZNear(far); depth->setZFar(far); depth->setWriteMask(false); @@ -1188,8 +1189,7 @@ void SkyManager::create() mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getImageManager()); atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater); - bool reverseZ = mSceneManager->getReverseZ(); - mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), reverseZ)); + mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager())); mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat("Moons_Masser_Size")/125, Moon::Type_Masser)); mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda)); @@ -1210,7 +1210,7 @@ void SkyManager::create() mCloudMesh2->addUpdateCallback(mCloudUpdater2); mCloudMesh2->setNodeMask(0); - auto depth = SceneUtil::createDepth(reverseZ); + auto depth = SceneUtil::createDepth(mSceneManager->getReverseZ()); depth->setWriteMask(false); mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/util.cpp b/apps/openmw/mwrender/util.cpp index e3fc48040f..5740f7df6c 100644 --- a/apps/openmw/mwrender/util.cpp +++ b/apps/openmw/mwrender/util.cpp @@ -4,10 +4,13 @@ #include #include +#include #include #include #include +#include "../mwbase/environment.hpp" + namespace MWRender { @@ -64,4 +67,9 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou node->setStateSet(stateset); } +bool getReverseZ() +{ + return MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ(); +} + } diff --git a/apps/openmw/mwrender/util.hpp b/apps/openmw/mwrender/util.hpp index a89baa22b1..c005dd9b23 100644 --- a/apps/openmw/mwrender/util.hpp +++ b/apps/openmw/mwrender/util.hpp @@ -32,6 +32,8 @@ namespace MWRender // no traverse() } }; + + bool getReverseZ(); } #endif diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 70e849b234..3c548ead23 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -285,6 +285,16 @@ namespace Resource return mReverseZ; } + void SceneManager::setDepthFormat(GLenum format) + { + mDepthFormat = format; + } + + GLenum SceneManager::getDepthFormat() const + { + return mDepthFormat; + } + void SceneManager::setAutoUseNormalMaps(bool use) { mAutoUseNormalMaps = use; diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 260fd4717f..780375f77e 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -95,6 +95,9 @@ namespace Resource void setReverseZ(bool reverseZ); bool getReverseZ() const; + void setDepthFormat(GLenum format); + GLenum getDepthFormat() const; + /// @see ShaderVisitor::setAutoUseNormalMaps void setAutoUseNormalMaps(bool use); @@ -206,6 +209,7 @@ namespace Resource SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods; bool mConvertAlphaTestToAlphaToCoverage; bool mReverseZ; + GLenum mDepthFormat; osg::ref_ptr mInstanceCache; diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index bbda5442bf..0004045dc8 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -28,8 +28,6 @@ #include #include "shadowsbin.hpp" -#include - namespace { using namespace osgShadow; @@ -350,11 +348,6 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) _projectionMatrix = cv->getProjectionMatrix(); } -bool isOrthographicViewFrustum(const osg::Matrix& m) -{ - return m(0,3)==0.0 && m(1,3)==0.0 && m(2,3)==0.0; -} - } // namespace MWShadowTechnique::ComputeLightSpaceBounds::ComputeLightSpaceBounds(osg::Viewport* viewport, const osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix) : @@ -988,9 +981,6 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) return; } - osg::Matrix shadowProj; - cv.getCurrentCamera()->getUserValue("shadowProj", shadowProj); - ViewDependentData* vdd = getViewDependentData(&cv); if (!vdd) @@ -1006,41 +996,34 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode(); + osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix(); + + // check whether this main views projection is perspective or orthographic + bool orthographicViewFrustum = viewProjectionMatrix(0,3)==0.0 && + viewProjectionMatrix(1,3)==0.0 && + viewProjectionMatrix(2,3)==0.0; + double minZNear = 0.0; double maxZFar = dbl_max; - bool orthographicViewFrustum; - if (_reverseZ) + if (cachedNearFarMode==osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { - cv.getCurrentCamera()->getUserValue("near", minZNear); - cv.getCurrentCamera()->getUserValue("far", maxZFar); - orthographicViewFrustum = isOrthographicViewFrustum(shadowProj); + double left, right, top, bottom; + if (orthographicViewFrustum) + { + viewProjectionMatrix.getOrtho(left, right, bottom, top, minZNear, maxZFar); + } + else + { + viewProjectionMatrix.getFrustum(left, right, bottom, top, minZNear, maxZFar); + } + OSG_INFO<<"minZNear="<addUniform(new osg::Uniform("alphaTestShadows", false)); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(true); - osg::ref_ptr clipcontrol = new osg::ClipControl(osg::ClipControl::LOWER_LEFT, osg::ClipControl::NEGATIVE_ONE_TO_ONE); if (_reverseZ) + { + osg::ref_ptr clipcontrol = new osg::ClipControl(osg::ClipControl::LOWER_LEFT, osg::ClipControl::NEGATIVE_ONE_TO_ONE); _shadowCastingStateSet->setAttribute(clipcontrol, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); + } _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON); diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index 32c2edeccd..7ca15e612d 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -95,8 +95,8 @@ namespace SceneUtil } ShadowManager::ShadowManager(osg::ref_ptr sceneRoot, osg::ref_ptr rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager, bool reverseZ) - : mShadowedScene(new osgShadow::ShadowedScene) - , mReverseZ(reverseZ) + : mReverseZ(reverseZ) + , mShadowedScene(new osgShadow::ShadowedScene) , mShadowTechnique(new MWShadowTechnique) , mOutdoorShadowCastingMask(outdoorShadowCastingMask) , mIndoorShadowCastingMask(indoorShadowCastingMask) diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index 79fe5d13c5..1f79469a19 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -314,8 +315,7 @@ bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg:: osg::ref_ptr createDepth(bool reverseZ) { - static osg::Depth::Function func = reverseZ ? osg::Depth::GEQUAL : osg::Depth::LEQUAL; - return new osg::Depth(func); + return new osg::Depth(reverseZ ? osg::Depth::GEQUAL : osg::Depth::LEQUAL); } osg::Matrix getReversedZProjectionMatrixAsPerspectiveInf(double fov, double aspect, double near) @@ -350,5 +350,9 @@ osg::Matrix getReversedZProjectionMatrixAsOrtho(double left, double right, doubl ); } +bool isFloatingPointDepthFormat(GLenum format) +{ + return format == GL_DEPTH_COMPONENT32F || format == GL_DEPTH_COMPONENT32F_NV; +} } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index 69e66bb8b3..bd4df5441a 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -50,7 +50,7 @@ namespace SceneUtil }; // Allows camera to render to a color and floating point depth texture with a multisampled framebuffer. - // Must be set on a cameras cull callback. + // Must be set on a camera's cull callback. // When the depth texture isn't needed as a sampler, use osg::Camera::attach(osg::Camera::DEPTH_COMPONENT, GL_DEPTH_COMPONENT32F) instead. // If multisampling is not being used on the color buffer attachment, use the osg::Camera::attach() method. class AttachMultisampledDepthColorCallback : public osg::NodeCallback @@ -99,6 +99,8 @@ namespace SceneUtil // Returns an orthographic projection matrix for use with a reversed z-buffer. osg::Matrix getReversedZProjectionMatrixAsOrtho(double left, double right, double bottom, double top, double near, double far); + // Returns true if the GL format is a floating point depth format + bool isFloatingPointDepthFormat(GLenum format); } #endif diff --git a/components/terrain/cellborder.cpp b/components/terrain/cellborder.cpp index 47c567f544..c34ed9ab0a 100644 --- a/components/terrain/cellborder.cpp +++ b/components/terrain/cellborder.cpp @@ -8,13 +8,16 @@ #include "world.hpp" #include "../esm/loadland.hpp" +#include + namespace Terrain { -CellBorder::CellBorder(Terrain::World *world, osg::Group *root, int borderMask): - mWorld(world), - mRoot(root), - mBorderMask(borderMask) +CellBorder::CellBorder(Terrain::World *world, osg::Group *root, int borderMask, Resource::SceneManager* sceneManager) + : mWorld(world) + , mSceneManager(sceneManager) + , mRoot(root) + , mBorderMask(borderMask) { } @@ -73,6 +76,8 @@ void CellBorder::createCellBorderGeometry(int x, int y) polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); stateSet->setAttributeAndModes(polygonmode,osg::StateAttribute::ON); + mSceneManager->recreateShaders(borderGeode, "debug"); + borderGeode->setNodeMask(mBorderMask); mRoot->addChild(borderGeode); diff --git a/components/terrain/cellborder.hpp b/components/terrain/cellborder.hpp index 908cdea097..ee8fd72593 100644 --- a/components/terrain/cellborder.hpp +++ b/components/terrain/cellborder.hpp @@ -4,6 +4,11 @@ #include #include +namespace Resource +{ + class SceneManager; +} + namespace Terrain { class World; @@ -16,7 +21,7 @@ namespace Terrain public: typedef std::map, osg::ref_ptr > CellGrid; - CellBorder(Terrain::World *world, osg::Group *root, int borderMask); + CellBorder(Terrain::World *world, osg::Group *root, int borderMask, Resource::SceneManager* sceneManager); void createCellBorderGeometry(int x, int y); void destroyCellBorderGeometry(int x, int y); @@ -28,6 +33,7 @@ namespace Terrain protected: Terrain::World *mWorld; + Resource::SceneManager* mSceneManager; osg::Group *mRoot; CellGrid mCellBorderNodes; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index d1581724e0..17a51913e5 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "storage.hpp" #include "texturemanager.hpp" @@ -43,7 +44,7 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer)); mChunkManager->setNodeMask(nodeMask); - mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask)); + mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask,mResourceSystem->getSceneManager())); mResourceSystem->addResourceManager(mChunkManager.get()); mResourceSystem->addResourceManager(mTextureManager.get()); diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index a880a2b62d..fe6217b22b 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -5,8 +5,8 @@ near clip --------- :Type: floating point -:Range: > 0 -:Default: 3.0 +:Range: >= 0.005 +:Default: 1.0 This setting controls the distance to the near clipping plane. The value must be greater than zero. Values greater than approximately 18.0 will occasionally clip objects in the world in front of the character. @@ -235,3 +235,23 @@ Maximum roll angle in degrees. This setting can only be configured by editing the settings configuration file. +reverse z +--------- + +:Type: boolean +:Range: True/False +:Default: True + +Enables a reverse-z depth buffer in which the depth range is reversed. This +allows for small :ref:`near clip` values and removes almost all z-fighting with +terrain and even tightly coupled meshes at extreme view distances. For this to +be useful, a floating point depth buffer is required. These features require +driver and hardware support, but should work on any semi-modern desktop hardware +through OpenGL extensions. The exception is macOS, which has since dropped +development of OpenGL drivers. If unsupported, this setting has no effect. + +Note, this will force OpenMW to use shaders as if :ref:`force shaders` was enabled. +The performance impact of this feature should be negligible. + +This setting can only be configured by editing the settings configuration file. + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 36eca7f1ab..4943ce969b 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -15,7 +15,7 @@ [Camera] # Near clipping plane (>0.0, e.g. 0.01 to 18.0). -near clip = 3 +near clip = 1 # Cull objects that occupy less than 'small feature culling pixel size' on the screen. small feature culling = true @@ -67,6 +67,9 @@ head bobbing height = 3.0 # Maximum camera roll angle (degrees) head bobbing roll = 0.2 +# Reverse the depth range from [0,1] to [1,0]. +reverse z = true + [Cells] # Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled. diff --git a/files/shaders/CMakeLists.txt b/files/shaders/CMakeLists.txt index 5e049b8a3f..04446d2982 100644 --- a/files/shaders/CMakeLists.txt +++ b/files/shaders/CMakeLists.txt @@ -34,6 +34,8 @@ set(SHADER_FILES nv_nolighting_fragment.glsl gui_vertex.glsl gui_fragment.glsl + debug_vertex.glsl + debug_fragment.glsl ) copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}") diff --git a/files/shaders/debug_fragment.glsl b/files/shaders/debug_fragment.glsl new file mode 100644 index 0000000000..263d4e1f24 --- /dev/null +++ b/files/shaders/debug_fragment.glsl @@ -0,0 +1,8 @@ +#version 120 + +varying vec4 passColor; + +void main() +{ + gl_FragData[0] = passColor; +} diff --git a/files/shaders/debug_vertex.glsl b/files/shaders/debug_vertex.glsl new file mode 100644 index 0000000000..f68c3d6459 --- /dev/null +++ b/files/shaders/debug_vertex.glsl @@ -0,0 +1,12 @@ +#version 120 + +uniform mat4 projectionMatrix; + +varying vec4 passColor; + +void main() +{ + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); + + passColor = gl_Color; +} diff --git a/files/shaders/depth.glsl b/files/shaders/depth.glsl index 0adde7a2a7..aa6f54b99d 100644 --- a/files/shaders/depth.glsl +++ b/files/shaders/depth.glsl @@ -2,11 +2,11 @@ uniform float linearFac; #endif -float getLinearDepth(in vec4 viewPos) +float getLinearDepth(in float z, in float viewZ) { #if @reverseZ - return linearFac*viewPos.z; + return linearFac*viewZ; #else - return gl_Position.z; + return z; #endif } \ No newline at end of file diff --git a/files/shaders/groundcover_vertex.glsl b/files/shaders/groundcover_vertex.glsl index 94586734b6..c8db4be000 100644 --- a/files/shaders/groundcover_vertex.glsl +++ b/files/shaders/groundcover_vertex.glsl @@ -46,6 +46,7 @@ uniform mat4 osg_ViewMatrixInverse; uniform mat4 osg_ViewMatrix; uniform float windSpeed; uniform vec3 playerPos; +uniform mat4 projectionMatrix; #if @groundcoverStompMode == 0 #else @@ -142,9 +143,9 @@ void main(void) if (length(gl_ModelViewMatrix * vec4(position, 1.0)) > @groundcoverFadeEnd) gl_Position = vec4(0.0, 0.0, 0.0, 1.0); else - gl_Position = gl_ProjectionMatrix * viewPos; + gl_Position = projectionMatrix * viewPos; - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #if (!PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * rotation3(rotation) * gl_Normal).xyz); diff --git a/files/shaders/nv_default_vertex.glsl b/files/shaders/nv_default_vertex.glsl index 1c5483dcda..34d5415250 100644 --- a/files/shaders/nv_default_vertex.glsl +++ b/files/shaders/nv_default_vertex.glsl @@ -8,6 +8,8 @@ #extension GL_EXT_gpu_shader4: require #endif +uniform mat4 projectionMatrix; + #if @diffuseMap varying vec2 diffuseMapUV; #endif @@ -36,12 +38,12 @@ varying vec3 passNormal; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #if @diffuseMap diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy; diff --git a/files/shaders/nv_nolighting_vertex.glsl b/files/shaders/nv_nolighting_vertex.glsl index 02c28eb5e3..617f0489a4 100644 --- a/files/shaders/nv_nolighting_vertex.glsl +++ b/files/shaders/nv_nolighting_vertex.glsl @@ -1,5 +1,7 @@ #version 120 +uniform mat4 projectionMatrix; + #if @diffuseMap varying vec2 diffuseMapUV; #endif @@ -21,14 +23,14 @@ varying float passFalloff; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; #if @radialFog euclideanDepth = length(viewPos.xyz); #else - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #endif #if @diffuseMap diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index 34484ebd41..969fe5903e 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -8,6 +8,8 @@ #extension GL_EXT_gpu_shader4: require #endif +uniform mat4 projectionMatrix; + #if @diffuseMap varying vec2 diffuseMapUV; #endif @@ -66,12 +68,13 @@ varying vec3 passNormal; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); + gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #if (@envMap || !PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); diff --git a/files/shaders/terrain_vertex.glsl b/files/shaders/terrain_vertex.glsl index 221e0c27a9..b46b299e2b 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -8,6 +8,8 @@ #extension GL_EXT_gpu_shader4: require #endif +uniform mat4 projectionMatrix; + varying vec2 uv; varying float euclideanDepth; varying float linearDepth; @@ -29,12 +31,12 @@ varying vec3 passNormal; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #if (!PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); diff --git a/files/shaders/water_vertex.glsl b/files/shaders/water_vertex.glsl index 9513001847..8e506a57f8 100644 --- a/files/shaders/water_vertex.glsl +++ b/files/shaders/water_vertex.glsl @@ -1,5 +1,7 @@ #version 120 - + +uniform mat4 projectionMatrix; + varying vec3 screenCoordsPassthrough; varying vec4 position; varying float linearDepth; @@ -9,7 +11,7 @@ varying float linearDepth; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.0, @@ -22,7 +24,7 @@ void main(void) position = gl_Vertex; vec4 viewPos = gl_ModelViewMatrix * gl_Vertex; - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); setupShadowCoords(viewPos, normalize((gl_NormalMatrix * gl_Normal).xyz)); }