mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 01:56:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			419 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			419 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "util.hpp"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <array>
 | |
| #include <iomanip>
 | |
| #include <sstream>
 | |
| 
 | |
| #include <osg/FrameBufferObject>
 | |
| #include <osg/Node>
 | |
| #include <osg/NodeVisitor>
 | |
| #include <osg/TexEnvCombine>
 | |
| #include <osg/TexGen>
 | |
| #include <osgUtil/CullVisitor>
 | |
| #include <osgUtil/RenderStage>
 | |
| 
 | |
| #include <components/resource/imagemanager.hpp>
 | |
| #include <components/resource/scenemanager.hpp>
 | |
| #include <components/sceneutil/texturetype.hpp>
 | |
| 
 | |
| namespace SceneUtil
 | |
| {
 | |
|     namespace
 | |
|     {
 | |
|         std::array<std::string, 32> generateGlowTextureNames()
 | |
|         {
 | |
|             std::array<std::string, 32> result;
 | |
|             for (std::size_t i = 0; i < result.size(); ++i)
 | |
|             {
 | |
|                 std::stringstream stream;
 | |
|                 stream << "textures/magicitem/caust";
 | |
|                 stream << std::setw(2);
 | |
|                 stream << std::setfill('0');
 | |
|                 stream << i;
 | |
|                 stream << ".dds";
 | |
|                 result[i] = std::move(stream).str();
 | |
|             }
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         const std::array<std::string, 32> glowTextureNames = generateGlowTextureNames();
 | |
| 
 | |
|         struct FindLowestUnusedTexUnitVisitor : public osg::NodeVisitor
 | |
|         {
 | |
|             FindLowestUnusedTexUnitVisitor()
 | |
|                 : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
 | |
|             {
 | |
|             }
 | |
| 
 | |
|             void apply(osg::Node& node) override
 | |
|             {
 | |
|                 if (osg::StateSet* stateset = node.getStateSet())
 | |
|                     mLowestUnusedTexUnit
 | |
|                         = std::max(mLowestUnusedTexUnit, int(stateset->getTextureAttributeList().size()));
 | |
| 
 | |
|                 traverse(node);
 | |
|             }
 | |
| 
 | |
|             int mLowestUnusedTexUnit = 0;
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     GlowUpdater::GlowUpdater(int texUnit, const osg::Vec4f& color,
 | |
|         const std::vector<osg::ref_ptr<osg::Texture2D>>& textures, osg::Node* node, float duration,
 | |
|         Resource::ResourceSystem* resourcesystem)
 | |
|         : mTexUnit(texUnit)
 | |
|         , mColor(color)
 | |
|         , mOriginalColor(color)
 | |
|         , mTextures(textures)
 | |
|         , mNode(node)
 | |
|         , mDuration(duration)
 | |
|         , mOriginalDuration(duration)
 | |
|         , mStartingTime(0)
 | |
|         , mResourceSystem(resourcesystem)
 | |
|         , mColorChanged(false)
 | |
|         , mDone(false)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     void GlowUpdater::setDefaults(osg::StateSet* stateset)
 | |
|     {
 | |
|         if (mDone)
 | |
|             removeTexture(stateset);
 | |
|         else
 | |
|         {
 | |
|             stateset->setTextureMode(mTexUnit, GL_TEXTURE_2D, osg::StateAttribute::ON);
 | |
|             osg::TexGen* texGen = new osg::TexGen;
 | |
|             texGen->setMode(osg::TexGen::SPHERE_MAP);
 | |
| 
 | |
|             stateset->setTextureAttributeAndModes(
 | |
|                 mTexUnit, texGen, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
 | |
| 
 | |
|             osg::TexEnvCombine* texEnv = new osg::TexEnvCombine;
 | |
|             texEnv->setSource0_RGB(osg::TexEnvCombine::CONSTANT);
 | |
|             texEnv->setConstantColor(mColor);
 | |
|             texEnv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE);
 | |
|             texEnv->setSource2_RGB(osg::TexEnvCombine::TEXTURE);
 | |
|             texEnv->setOperand2_RGB(osg::TexEnvCombine::SRC_COLOR);
 | |
| 
 | |
|             stateset->setTextureAttributeAndModes(mTexUnit, texEnv, osg::StateAttribute::ON);
 | |
|             stateset->addUniform(new osg::Uniform("envMapColor", mColor));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void GlowUpdater::removeTexture(osg::StateSet* stateset)
 | |
|     {
 | |
|         stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXTURE);
 | |
|         stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXGEN);
 | |
|         stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXENV);
 | |
|         stateset->removeTextureMode(mTexUnit, GL_TEXTURE_2D);
 | |
|         stateset->removeUniform("envMapColor");
 | |
| 
 | |
|         osg::StateSet::TextureAttributeList& list = stateset->getTextureAttributeList();
 | |
|         while (list.size() && list.rbegin()->empty())
 | |
|             list.pop_back();
 | |
|     }
 | |
| 
 | |
|     void GlowUpdater::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
 | |
|     {
 | |
|         if (mColorChanged)
 | |
|         {
 | |
|             this->reset();
 | |
|             setDefaults(stateset);
 | |
|             mColorChanged = false;
 | |
|         }
 | |
|         if (mDone)
 | |
|             return;
 | |
| 
 | |
|         // Set the starting time to measure glow duration from if this is a temporary glow
 | |
|         if ((mDuration >= 0) && mStartingTime == 0)
 | |
|             mStartingTime = nv->getFrameStamp()->getSimulationTime();
 | |
| 
 | |
|         float time = nv->getFrameStamp()->getSimulationTime();
 | |
|         int index = (int)(time * 16) % mTextures.size();
 | |
|         stateset->setTextureAttribute(
 | |
|             mTexUnit, mTextures[index], osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
 | |
| 
 | |
|         if ((mDuration >= 0)
 | |
|             && (time - mStartingTime > mDuration)) // If this is a temporary glow and it has finished its duration
 | |
|         {
 | |
|             if (mOriginalDuration >= 0) // if this glowupdater was a temporary glow since its creation
 | |
|             {
 | |
|                 removeTexture(stateset);
 | |
|                 this->reset();
 | |
|                 mDone = true;
 | |
|                 // normally done in StateSetUpdater::operator(), but needs doing here so the shader visitor sees the
 | |
|                 // right StateSet
 | |
|                 mNode->setStateSet(stateset);
 | |
|                 mResourceSystem->getSceneManager()->recreateShaders(mNode);
 | |
|             }
 | |
|             if (mOriginalDuration < 0) // if this glowupdater was originally a permanent glow
 | |
|             {
 | |
|                 mDuration = mOriginalDuration;
 | |
|                 mStartingTime = 0;
 | |
|                 mColor = mOriginalColor;
 | |
|                 this->reset();
 | |
|                 setDefaults(stateset);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool GlowUpdater::isPermanentGlowUpdater()
 | |
|     {
 | |
|         return (mDuration < 0);
 | |
|     }
 | |
| 
 | |
|     bool GlowUpdater::isDone()
 | |
|     {
 | |
|         return mDone;
 | |
|     }
 | |
| 
 | |
|     void GlowUpdater::setColor(const osg::Vec4f& color)
 | |
|     {
 | |
|         mColor = color;
 | |
|         mColorChanged = true;
 | |
|     }
 | |
| 
 | |
|     void GlowUpdater::setDuration(float duration)
 | |
|     {
 | |
|         mDuration = duration;
 | |
|     }
 | |
| 
 | |
|     osg::Vec4f colourFromRGB(unsigned int clr)
 | |
|     {
 | |
|         osg::Vec4f colour(
 | |
|             ((clr >> 0) & 0xFF) / 255.0f, ((clr >> 8) & 0xFF) / 255.0f, ((clr >> 16) & 0xFF) / 255.0f, 1.f);
 | |
|         return colour;
 | |
|     }
 | |
| 
 | |
|     osg::Vec4f colourFromRGBA(unsigned int value)
 | |
|     {
 | |
|         return osg::Vec4f(makeOsgColorComponent(value, 0), makeOsgColorComponent(value, 8),
 | |
|             makeOsgColorComponent(value, 16), makeOsgColorComponent(value, 24));
 | |
|     }
 | |
| 
 | |
|     float makeOsgColorComponent(unsigned int value, unsigned int shift)
 | |
|     {
 | |
|         return float((value >> shift) & 0xFFu) / 255.0f;
 | |
|     }
 | |
| 
 | |
|     bool hasUserDescription(const osg::Node* node, std::string_view pattern)
 | |
|     {
 | |
|         if (node == nullptr)
 | |
|             return false;
 | |
| 
 | |
|         const osg::UserDataContainer* udc = node->getUserDataContainer();
 | |
|         if (udc && udc->getNumDescriptions() > 0)
 | |
|         {
 | |
|             for (auto& descr : udc->getDescriptions())
 | |
|             {
 | |
|                 if (descr == pattern)
 | |
|                     return true;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     osg::ref_ptr<GlowUpdater> addEnchantedGlow(osg::ref_ptr<osg::Node> node, Resource::ResourceSystem* resourceSystem,
 | |
|         const osg::Vec4f& glowColor, float glowDuration)
 | |
|     {
 | |
|         std::vector<osg::ref_ptr<osg::Texture2D>> textures;
 | |
|         for (const std::string& name : glowTextureNames)
 | |
|         {
 | |
|             osg::ref_ptr<osg::Image> image = resourceSystem->getImageManager()->getImage(name);
 | |
|             osg::ref_ptr<osg::Texture2D> tex(new osg::Texture2D(image));
 | |
|             tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT);
 | |
|             tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT);
 | |
|             resourceSystem->getSceneManager()->applyFilterSettings(tex);
 | |
|             textures.push_back(tex);
 | |
|         }
 | |
| 
 | |
|         FindLowestUnusedTexUnitVisitor findLowestUnusedTexUnitVisitor;
 | |
|         node->accept(findLowestUnusedTexUnitVisitor);
 | |
|         int texUnit = findLowestUnusedTexUnitVisitor.mLowestUnusedTexUnit;
 | |
| 
 | |
|         osg::ref_ptr<GlowUpdater> glowUpdater
 | |
|             = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, resourceSystem);
 | |
|         node->addUpdateCallback(glowUpdater);
 | |
| 
 | |
|         // set a texture now so that the ShaderVisitor can find it
 | |
|         osg::ref_ptr<osg::StateSet> writableStateSet = nullptr;
 | |
|         if (!node->getStateSet())
 | |
|             writableStateSet = node->getOrCreateStateSet();
 | |
|         else
 | |
|         {
 | |
|             writableStateSet = new osg::StateSet(*node->getStateSet(), osg::CopyOp::SHALLOW_COPY);
 | |
|             node->setStateSet(writableStateSet);
 | |
|         }
 | |
|         writableStateSet->setTextureAttributeAndModes(texUnit, textures.front(), osg::StateAttribute::ON);
 | |
|         writableStateSet->setTextureAttributeAndModes(texUnit, new TextureType("envMap"), osg::StateAttribute::ON);
 | |
|         writableStateSet->addUniform(new osg::Uniform("envMapColor", glowColor));
 | |
|         resourceSystem->getSceneManager()->recreateShaders(std::move(node));
 | |
| 
 | |
|         return glowUpdater;
 | |
|     }
 | |
| 
 | |
|     void attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg::Camera::BufferComponent buffer,
 | |
|         osg::Texture* texture, unsigned int level, unsigned int face, bool mipMapGeneration,
 | |
|         bool addMSAAIntermediateTarget)
 | |
|     {
 | |
|         unsigned int samples = 0;
 | |
|         unsigned int colourSamples = 0;
 | |
|         if (addMSAAIntermediateTarget)
 | |
|         {
 | |
|             // Alpha-to-coverage requires a multisampled framebuffer.
 | |
|             // OSG will set that up automatically and resolve it to the specified single-sample texture for us.
 | |
|             // For some reason, two samples are needed, at least with some drivers.
 | |
|             samples = 2;
 | |
|             colourSamples = 1;
 | |
|         }
 | |
|         camera->attach(buffer, texture, level, face, mipMapGeneration, samples, colourSamples);
 | |
|     }
 | |
| 
 | |
|     OperationSequence::OperationSequence(bool keep)
 | |
|         : Operation("OperationSequence", keep)
 | |
|         , mOperationQueue(new osg::OperationQueue())
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     void OperationSequence::operator()(osg::Object* object)
 | |
|     {
 | |
|         mOperationQueue->runOperations(object);
 | |
|     }
 | |
| 
 | |
|     void OperationSequence::add(osg::Operation* operation)
 | |
|     {
 | |
|         mOperationQueue->add(operation);
 | |
|     }
 | |
| 
 | |
|     GLenum computeUnsizedPixelFormat(GLenum format)
 | |
|     {
 | |
|         switch (format)
 | |
|         {
 | |
|             // Try compressed formats first, they're more likely to be used
 | |
| 
 | |
|             // Generic
 | |
|             case GL_COMPRESSED_ALPHA_ARB:
 | |
|                 return GL_ALPHA;
 | |
|             case GL_COMPRESSED_INTENSITY_ARB:
 | |
|                 return GL_INTENSITY;
 | |
|             case GL_COMPRESSED_LUMINANCE_ALPHA_ARB:
 | |
|                 return GL_LUMINANCE_ALPHA;
 | |
|             case GL_COMPRESSED_LUMINANCE_ARB:
 | |
|                 return GL_LUMINANCE;
 | |
|             case GL_COMPRESSED_RGB_ARB:
 | |
|                 return GL_RGB;
 | |
|             case GL_COMPRESSED_RGBA_ARB:
 | |
|                 return GL_RGBA;
 | |
| 
 | |
|             // S3TC
 | |
|             case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
 | |
|             case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
 | |
|                 return GL_RGB;
 | |
|             case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
 | |
|             case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
 | |
|             case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
 | |
|             case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
 | |
|             case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
 | |
|             case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
 | |
|                 return GL_RGBA;
 | |
| 
 | |
|             // RGTC
 | |
|             case GL_COMPRESSED_RED_RGTC1_EXT:
 | |
|             case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT:
 | |
|                 return GL_RED;
 | |
|             case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
 | |
|             case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
 | |
|                 return GL_RG;
 | |
| 
 | |
|             // PVRTC
 | |
|             case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
 | |
|             case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
 | |
|                 return GL_RGB;
 | |
|             case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
 | |
|             case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
 | |
|                 return GL_RGBA;
 | |
| 
 | |
|             // ETC
 | |
|             case GL_COMPRESSED_R11_EAC:
 | |
|             case GL_COMPRESSED_SIGNED_R11_EAC:
 | |
|                 return GL_RED;
 | |
|             case GL_COMPRESSED_RG11_EAC:
 | |
|             case GL_COMPRESSED_SIGNED_RG11_EAC:
 | |
|                 return GL_RG;
 | |
|             case GL_ETC1_RGB8_OES:
 | |
|             case GL_COMPRESSED_RGB8_ETC2:
 | |
|             case GL_COMPRESSED_SRGB8_ETC2:
 | |
|                 return GL_RGB;
 | |
|             case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
 | |
|             case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
 | |
|             case GL_COMPRESSED_RGBA8_ETC2_EAC:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
 | |
|                 return GL_RGBA;
 | |
| 
 | |
|             // ASTC
 | |
|             case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
 | |
|             case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
 | |
|             case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
 | |
|                 return GL_RGBA;
 | |
| 
 | |
|             // Plug in some holes computePixelFormat has, you never know when these could come in handy
 | |
|             case GL_INTENSITY4:
 | |
|             case GL_INTENSITY8:
 | |
|             case GL_INTENSITY12:
 | |
|             case GL_INTENSITY16:
 | |
|                 return GL_INTENSITY;
 | |
| 
 | |
|             case GL_LUMINANCE4:
 | |
|             case GL_LUMINANCE8:
 | |
|             case GL_LUMINANCE12:
 | |
|             case GL_LUMINANCE16:
 | |
|                 return GL_LUMINANCE;
 | |
| 
 | |
|             case GL_LUMINANCE4_ALPHA4:
 | |
|             case GL_LUMINANCE6_ALPHA2:
 | |
|             case GL_LUMINANCE8_ALPHA8:
 | |
|             case GL_LUMINANCE12_ALPHA4:
 | |
|             case GL_LUMINANCE12_ALPHA12:
 | |
|             case GL_LUMINANCE16_ALPHA16:
 | |
|                 return GL_LUMINANCE_ALPHA;
 | |
|         }
 | |
| 
 | |
|         return osg::Image::computePixelFormat(format);
 | |
|     }
 | |
| 
 | |
|     const std::string& getTextureType(const osg::StateSet& stateset, const osg::Texture& texture, unsigned int texUnit)
 | |
|     {
 | |
|         const osg::StateAttribute* type = stateset.getTextureAttribute(texUnit, SceneUtil::TextureType::AttributeType);
 | |
|         if (type)
 | |
|             return static_cast<const SceneUtil::TextureType*>(type)->getName();
 | |
| 
 | |
|         return texture.getName();
 | |
|     }
 | |
| }
 |