diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 317786e96a..2e50daf542 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -42,6 +42,10 @@ list (APPEND COMPONENT_FILES "${OpenMW_BINARY_DIR}/${VERSION_CPP_FILE}") # source files +add_component_dir (bindlesstexture + bindlesstexture + ) + add_component_dir (lua luastate scriptscontainer asyncpackage utilpackage serialization configuration l10n storage utf8 shapes/box inputactions diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 787f2e8441..03c235c56c 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -22,6 +22,7 @@ #include #include +#include #include @@ -33,13 +34,16 @@ #include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -352,6 +356,139 @@ namespace Resource std::vector> mRigGeometryHolders; }; + class TextureAtlasVisitor : public osg::NodeVisitor + { + public: + /// default to traversing all children. + TextureAtlasVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { + } + + void apply(osg::Node& node) override + { + bool pushedStateState = false; + + osg::StateSet* ss = node.getStateSet(); + if (ss) + pushedStateState = pushStateSet(ss); + + traverse(node); + + if (pushedStateState) + popStateSet(); + } + + void apply(osg::Drawable& node) override + { + if (auto rig = dynamic_cast(&node)) + return apply(*rig->getSourceGeometry()); + if (auto morph = dynamic_cast(&node)) + return apply(*morph->getSourceGeometry()); + + if (dynamic_cast(&node)) + return; + //if (dynamic_cast(&node)) + // toskip = true; + if (dynamic_cast(&node)) + return; + if (dynamic_cast(&node)) + return; + + bool pushedStateState = false; + + osg::StateSet* ss = node.getStateSet(); + if (ss) + pushedStateState = pushStateSet(ss); + + [&] { + osg::Geometry* geom = node.asGeometry(); + if (!geom || _statesetStack.empty()) + return; + + const osg::StateSet::TextureAttributeList& tal = _statesetStack.back()->getTextureAttributeList(); + for (unsigned int unit = 0; unit < tal.size(); ++unit) + { + osg::ref_ptr texture = dynamic_cast( + _statesetStack.back()->getTextureAttribute(unit, osg::StateAttribute::TEXTURE)); + if (!texture) + continue; + + auto it + = std::find(mTexture->_textureList.begin(), mTexture->_textureList.end(), texture->getImage()); + if (it == mTexture->_textureList.end()) + continue; + unsigned int d = std::distance(mTexture->_textureList.begin(), it); + auto indices = new osg::IntArray(geom->getVertexArray()->getNumElements()); + for (unsigned int i = 0; i < geom->getVertexArray()->getNumElements(); ++i) + (*indices)[i] = d; + geom->setVertexAttribArray(7, indices, osg::Array::BIND_PER_VERTEX); + } + }(); + + + if (pushedStateState) + popStateSet(); + } + + osg::ref_ptr mTexture; + + protected: + bool pushStateSet(osg::StateSet* stateset) + { + const osg::StateSet::TextureAttributeList& tal = stateset->getTextureAttributeList(); + + // if no textures ignore + if (tal.empty()) + return false; + + bool pushStateState = false; + + // if already in stateset list ignore + if (_statesetMap.count(stateset) > 0) + { + pushStateState = true; + } + else + { + bool containsTexture2D = false; + for (unsigned int unit = 0; unit < tal.size(); ++unit) + { + osg::Texture2D* texture2D = dynamic_cast( + stateset->getTextureAttribute(unit, osg::StateAttribute::TEXTURE)); + if (texture2D) + { + containsTexture2D = true; + _textures.insert(texture2D); + } + } + + if (containsTexture2D) + { + _statesetMap[stateset]; + pushStateState = true; + } + } + + if (pushStateState) + { + _statesetStack.push_back(stateset); + } + + return pushStateState; + } + void popStateSet() { _statesetStack.pop_back(); } + + typedef std::set Drawables; + typedef std::map StateSetMap; + typedef std::set Textures; + typedef std::vector StateSetStack; + + StateSetMap _statesetMap; + StateSetStack _statesetStack; + Textures _textures; + }; + SceneManager::SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager, double expiryDelay) : ResourceManager(vfs, expiryDelay) @@ -374,6 +511,17 @@ namespace Resource , mUnRefImageDataAfterApply(false) , mParticleSystemMask(~0u) { + mBuffer = BindlessBuffer::Make(4800); + + BindlessTexture::TextureList images; + images.reserve(4800); + for (auto const& tex : vfs->getRecursiveDirectoryIterator("textures/")) + { + auto image = imageManager->getImage(tex); + if (image->s() > 16 || image->t() > 16) + images.push_back(image); + } + mTexture = new BindlessTexture(mBuffer, images); } void SceneManager::setForceShaders(bool force) @@ -900,6 +1048,11 @@ namespace Resource SceneUtil::ReplaceDepthVisitor replaceDepthVisitor; loaded->accept(replaceDepthVisitor); + TextureAtlasVisitor tav; + tav.mTexture = mTexture; + loaded->accept(tav); + shareState(loaded); + osg::ref_ptr shaderVisitor(createShaderVisitor()); loaded->accept(*shaderVisitor); @@ -1134,6 +1287,10 @@ namespace Resource shaderVisitor->setAdjustCoverageForAlphaTest(mAdjustCoverageForAlphaTest); shaderVisitor->setSupportsNormalsRT(mSupportsNormalsRT); shaderVisitor->setWeatherParticleOcclusion(mWeatherParticleOcclusion); + + shaderVisitor->mBuffer = mBuffer; + shaderVisitor->mTexture = mTexture; + return shaderVisitor; } } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 12900441de..57fdc028be 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -12,6 +12,7 @@ #include "resourcemanager.hpp" #include +#include #include namespace VFS @@ -230,6 +231,9 @@ namespace Resource void setWeatherParticleOcclusion(bool value) { mWeatherParticleOcclusion = value; } private: + osg::ref_ptr mBuffer; + osg::ref_ptr mTexture; + osg::ref_ptr createShaderVisitor(const std::string& shaderPrefix = "objects"); osg::ref_ptr loadErrorMarker(); osg::ref_ptr cloneErrorMarker(); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index e281f64448..d89ca2aedc 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -361,6 +361,10 @@ namespace Shader mRequirements.back().mTexStageRequiringTangents = unit; } diffuseMap = texture; + if (!writableStateSet) + writableStateSet = getWritableStateSet(node); + writableStateSet->setTextureAttribute(unit, mTexture, osg::StateAttribute::ON); + writableStateSet->setAttributeAndModes(mBuffer->Binding(), osg::StateAttribute::ON); } else if (texName == "specularMap") specularMap = texture; @@ -739,6 +743,7 @@ namespace Shader shaderPrefix = mDefaultShaderPrefix; auto program = mShaderManager.getProgram(shaderPrefix, defineMap, mProgramTemplate); + program->addBindAttribLocation("index", 7); writableStateSet->setAttributeAndModes(program, osg::StateAttribute::ON); addedState->setAttributeAndModes(std::move(program)); diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index a8e79ec995..2d8dcf9bb8 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace Resource { @@ -63,6 +64,9 @@ namespace Shader void pushRequirements(osg::Node& node); void popRequirements(); + osg::ref_ptr mBuffer; + osg::ref_ptr mTexture; + private: bool mForceShaders; bool mAllowedToModifyStateSets; diff --git a/files/shaders/compatibility/objects.frag b/files/shaders/compatibility/objects.frag index 56c7abf27c..b1767b6ef6 100644 --- a/files/shaders/compatibility/objects.frag +++ b/files/shaders/compatibility/objects.frag @@ -1,4 +1,6 @@ -#version 120 +#version 450 compatibility +#extension GL_ARB_bindless_texture : require +#extension GL_NV_gpu_shader5 : require // uint64_t #pragma import_defines(FORCE_OPAQUE, DISTORTION) #if @useUBO @@ -14,6 +16,13 @@ uniform sampler2D diffuseMap; varying vec2 diffuseMapUV; #endif +flat in int textureIndex; + +layout (binding = 0, std140) uniform TEXTURE_BLOCK +{ + uint64_t tex[4096]; +}; + #if @darkMap uniform sampler2D darkMap; varying vec2 darkMapUV; @@ -124,12 +133,15 @@ void main() // only offset diffuse and normal maps for now, other textures are more likely to be using a completely different UV set vec2 offset = vec2(0.0); + int tIndex = (int)(textureIndex); + sampler2D myText = sampler2D(tex[tIndex]); + #if @parallax || @diffuseParallax #if @parallax float height = texture2D(normalMap, normalMapUV).a; float flipY = (passTangent.w > 0.0) ? -1.f : 1.f; #else - float height = texture2D(diffuseMap, diffuseMapUV).a; + float height = texture2D(myText, diffuseMapUV).a; // FIXME: shouldn't be necessary, but in this path false-positives are common float flipY = -1.f; #endif @@ -139,7 +151,7 @@ void main() vec2 screenCoords = gl_FragCoord.xy / screenRes; #if @diffuseMap - gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV + offset); + gl_FragData[0] = texture2D(myText, diffuseMapUV + offset); #if defined(DISTORTION) && DISTORTION gl_FragData[0].a = getDiffuseColor().a; @@ -150,7 +162,7 @@ vec2 screenCoords = gl_FragCoord.xy / screenRes; #if @diffuseParallax gl_FragData[0].a = 1.0; #else - gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, diffuseMapUV + offset); + gl_FragData[0].a *= coveragePreservingAlphaScale(myText, diffuseMapUV + offset); #endif #else gl_FragData[0] = vec4(1.0); diff --git a/files/shaders/compatibility/objects.vert b/files/shaders/compatibility/objects.vert index 081ff909cf..2401f35a06 100644 --- a/files/shaders/compatibility/objects.vert +++ b/files/shaders/compatibility/objects.vert @@ -1,4 +1,6 @@ -#version 120 +#version 450 compatibility +#extension GL_ARB_bindless_texture : require +#extension GL_NV_gpu_shader5 : require // uint64_t #if @useUBO #extension GL_ARB_uniform_buffer_object : require @@ -8,6 +10,9 @@ #extension GL_EXT_gpu_shader4: require #endif +attribute float index; +flat out int textureIndex; + #include "lib/core/vertex.h.glsl" #if @diffuseMap varying vec2 diffuseMapUV; @@ -94,6 +99,7 @@ void main(void) passViewPos = viewPos.xyz; passNormal = gl_Normal.xyz; normalToViewMatrix = gl_NormalMatrix; + textureIndex = (int)(index + 0.5); #if @normalMap || @diffuseParallax passTangent = gl_MultiTexCoord7.xyzw;