/* -*-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_NATIVE_PROGRAM_ADAPTER #define OSGEARTH_NATIVE_PROGRAM_ADAPTER #include "Common" #include "GLUtils" #include #include #include #include #include #include #include #undef LC #define LC "[NativeProgramAdapter] " #define OE_LOCAL OE_DEBUG //#undef OE_DEBUG //#define OE_DEBUG OE_INFO namespace osgEarth { namespace Util { /** * Wraps a native glProgram handle so we can earch it form uniforms and * apply values to them. */ class NativeProgramAdapter : public osg::Referenced { public: /** Create a program adapter under the current state that wraps the provided glProgram handle. */ NativeProgramAdapter(const osg::State* state, GLint handle, const std::vector prefixes, const std::string& name) { OE_LOCAL << LC << "Create adapter for glProgram " << name << " (handle=" << handle << ")" << std::endl; _handle = handle; if (state) _ext = osg::GL2Extensions::Get(GLUtils::getSharedContextID(*state), true); GLint numUniforms = 0; GLsizei maxLen = 0; _ext->glGetProgramiv( _handle, GL_ACTIVE_UNIFORMS, &numUniforms ); _ext->glGetProgramiv( _handle, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen ); std::vector uniformNames; if ((numUniforms > 0) && (maxLen > 1)) { GLint size = 0; GLenum type = 0; GLchar* name = new GLchar[maxLen]; for (GLint i = 0; i < numUniforms; ++i) { _ext->glGetActiveUniform(_handle, i, maxLen, 0, &size, &type, name); uniformNames.push_back(std::string(name)); } } for(auto& uniformName : uniformNames) { bool use_uniform = true; if (!prefixes.empty()) { use_uniform = false; for (auto& prefix : prefixes) { if (Strings::startsWith(uniformName, prefix)) { use_uniform = true; break; } } } if (use_uniform) { GLint loc = _ext->glGetUniformLocation(_handle, uniformName.c_str()); if ( loc != -1 ) { _uniformLocations[osg::Uniform::getNameID(uniformName)] = loc; OE_LOCAL << LC << " Uniform = \"" << uniformName << "\", location = " << loc << std::endl; } } } } void apply(const osg::State* state) { bool useProgram = true; for (osg::State::UniformMap::const_iterator i = state->getUniformMap().begin(); i != state->getUniformMap().end(); ++i) { const osg::State::UniformStack& as = i->second; if (!as.uniformVec.empty()) { const osg::Uniform* uniform = static_cast(as.uniformVec.back().first); if (apply(uniform, useProgram)) useProgram = false; } } if (state->getUseModelViewAndProjectionUniforms()) { // Apply each of the osg_*Matrix uniforms, which may be used by the native program osg::State* ncState = const_cast(state); if (apply(ncState->getModelViewMatrixUniform(), useProgram)) useProgram = false; if (apply(ncState->getProjectionMatrixUniform(), useProgram)) useProgram = false; if (apply(ncState->getModelViewProjectionMatrixUniform(), useProgram)) useProgram = false; if (apply(ncState->getNormalMatrixUniform(), useProgram)) useProgram = false; } } private: GLint _handle; using UniformMap = std::unordered_map; UniformMap _uniformLocations; const osg::GL2Extensions* _ext; /** Apply the uniform to this program, optionally calling glUseProgram if necessary. */ bool apply(const osg::Uniform* uniform, bool useProgram) const { #if OSG_VERSION_GREATER_OR_EQUAL(3,7,0) UniformMap::const_iterator location = _uniformLocations.find( ((osg::UniformBase*)uniform)->getNameID() ); #else UniformMap::const_iterator location = _uniformLocations.find(uniform->getNameID()); #endif if ( location != _uniformLocations.end() ) { if ( useProgram ) _ext->glUseProgram( _handle ); uniform->apply( _ext, location->second ); return true; } else return false; } }; /** * Collection of program adapters. */ class NativeProgramAdapterCollection: public osg::MixinVector< osg::ref_ptr > { public: void apply(const osg::State* state) const { for(const_iterator i = begin(); i != end(); ++i ) i->get()->apply( state ); } }; } } #undef LC #endif // OSGEARTH_NATIVE_PROGRAM_ADAPTER