/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library 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 * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #ifndef GL_MAX_TEXTURE_COORDS #define GL_MAX_TEXTURE_COORDS 0x8871 #endif #ifndef GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D #endif #ifndef GL_MAX_TEXTURE_UNITS #define GL_MAX_TEXTURE_UNITS 0x84E2 #endif using namespace std; using namespace osg; static ApplicationUsageProxy State_e0(ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_GL_ERROR_CHECKING ","ONCE_PER_ATTRIBUTE | ON | on enables fine grained checking, ONCE_PER_FRAME enables coarse grained checking"); State::State(): Referenced(true) { _graphicsContext = 0; _contextID = 0; _identity = new osg::RefMatrix(); // default RefMatrix constructs to identity. _initialViewMatrix = _identity; _projection = _identity; _modelView = _identity; _useModelViewAndProjectionUniforms = false; _modelViewMatrixUniform = new Uniform(Uniform::FLOAT_MAT4,"osg_ModelViewMatrix"); _projectionMatrixUniform = new Uniform(Uniform::FLOAT_MAT4,"osg_ProjectionMatrix"); _modelViewProjectionMatrixUniform = new Uniform(Uniform::FLOAT_MAT4,"osg_ModelViewProjectionMatrix"); _normalMatrixUniform = new Uniform(Uniform::FLOAT_MAT3,"osg_NormalMatrix"); { _useVertexAttributeAliasing = false; setUpVertexAttribAlias(_vertexAlias,0, "gl_Vertex","osg_Vertex","attribute vec4 "); setUpVertexAttribAlias(_normalAlias, 2, "gl_Normal","osg_Normal","attribute vec3 "); setUpVertexAttribAlias(_colorAlias, 3, "gl_Color","osg_Color","attribute vec4 "); setUpVertexAttribAlias(_secondaryColorAlias, 4, "gl_SecondaryColor","osg_SecondaryColor","attribute vec4 "); setUpVertexAttribAlias(_fogCoordAlias, 5, "gl_FogCoord","osg_FogCoord","attribute float "); _texCoordAliasList.resize(8); for(unsigned int i=0; i<_texCoordAliasList.size(); i++) { std::stringstream gl_MultiTexCoord; std::stringstream osg_MultiTexCoord; gl_MultiTexCoord<<"gl_MultiTexCoord"<removeObserver(this); } } void State::objectDeleted(void* object) { const Program::PerContextProgram* ppcp = reinterpret_cast(object); AppliedProgramObjectSet::iterator itr = _appliedProgramObjectSet.find(ppcp); if (itr != _appliedProgramObjectSet.end()) { // osg::notify(osg::NOTICE)<<"Removing _appliedProgramObjectSet entry "<second; ms.valueVec.clear(); ms.last_applied_value = !ms.global_default_value; ms.changed = true; } #else _modeMap.clear(); #endif _modeMap[GL_DEPTH_TEST].global_default_value = true; _modeMap[GL_DEPTH_TEST].changed = true; // go through all active StateAttribute's, setting to change to force update, // the idea is to leave only the global defaults left. for(AttributeMap::iterator aitr=_attributeMap.begin(); aitr!=_attributeMap.end(); ++aitr) { AttributeStack& as = aitr->second; as.attributeVec.clear(); as.last_applied_attribute = NULL; as.changed = true; } // we can do a straight clear, we arn't interested in GL_DEPTH_TEST defaults in texture modes. for(TextureModeMapList::iterator tmmItr=_textureModeMapList.begin(); tmmItr!=_textureModeMapList.end(); ++tmmItr) { tmmItr->clear(); } // empty all the texture attributes as per normal attributes, leaving only the global defaults left. for(TextureAttributeMapList::iterator tamItr=_textureAttributeMapList.begin(); tamItr!=_textureAttributeMapList.end(); ++tamItr) { AttributeMap& attributeMap = *tamItr; // go through all active StateAttribute's, setting to change to force update. for(AttributeMap::iterator aitr=attributeMap.begin(); aitr!=attributeMap.end(); ++aitr) { AttributeStack& as = aitr->second; as.attributeVec.clear(); as.last_applied_attribute = NULL; as.changed = true; } } _stateStateStack.clear(); _modelView = _identity; _projection = _identity; dirtyAllVertexArrays(); #if 0 // reset active texture unit values and call OpenGL // note, this OpenGL op precludes the use of State::reset() without a // valid graphics context, therefore the new implementation below // is preferred. setActiveTextureUnit(0); #else // reset active texture unit values without calling OpenGL _currentActiveTextureUnit = 0; _currentClientActiveTextureUnit = 0; #endif _lastAppliedProgramObject = 0; for(AppliedProgramObjectSet::iterator apitr=_appliedProgramObjectSet.begin(); apitr!=_appliedProgramObjectSet.end(); ++apitr) { (*apitr)->resetAppliedUniforms(); (*apitr)->removeObserver(this); } _appliedProgramObjectSet.clear(); // what about uniforms??? need to clear them too... // go through all active Unfirom's, setting to change to force update, // the idea is to leave only the global defaults left. for(UniformMap::iterator uitr=_uniformMap.begin(); uitr!=_uniformMap.end(); ++uitr) { UniformStack& us = uitr->second; us.uniformVec.clear(); } } void State::setInitialViewMatrix(const osg::RefMatrix* matrix) { if (matrix) _initialViewMatrix = matrix; else _initialViewMatrix = _identity; _initialInverseViewMatrix.invert(*_initialViewMatrix); } void State::setMaxTexturePoolSize(unsigned int size) { _maxTexturePoolSize = size; osg::Texture::getTextureObjectManager(getContextID())->setMaxTexturePoolSize(size); osg::notify(osg::INFO)<<"osg::State::_maxTexturePoolSize="<<_maxTexturePoolSize<setMaxGLBufferObjectPoolSize(_maxBufferObjectPoolSize); osg::notify(osg::INFO)<<"osg::State::_maxBufferObjectPoolSize="<<_maxBufferObjectPoolSize<getModeList()); // iterator through texture modes. unsigned int unit; const StateSet::TextureModeList& ds_textureModeList = dstate->getTextureModeList(); for(unit=0;unitgetAttributeList()); // iterator through texture attributes. const StateSet::TextureAttributeList& ds_textureAttributeList = dstate->getTextureAttributeList(); for(unit=0;unitgetUniformList()); } // osg::notify(osg::NOTICE)<<"State::pushStateSet()"<<_stateStateStack.size()<getModeList()); // iterator through texture modes. unsigned int unit; const StateSet::TextureModeList& ds_textureModeList = dstate->getTextureModeList(); for(unit=0;unitgetAttributeList()); // iterator through texture attributes. const StateSet::TextureAttributeList& ds_textureAttributeList = dstate->getTextureAttributeList(); for(unit=0;unitgetUniformList()); } // remove the top draw state from the stack. _stateStateStack.pop_back(); } void State::insertStateSet(unsigned int pos,const StateSet* dstate) { StateSetStack tempStack; // first pop the StateSet above the position we need to insert at while (_stateStateStack.size()>pos) { tempStack.push_back(_stateStateStack.back()); popStateSet(); } // push our new stateset pushStateSet(dstate); // push back the original ones for(StateSetStack::reverse_iterator itr = tempStack.rbegin(); itr != tempStack.rend(); ++itr) { pushStateSet(*itr); } } void State::removeStateSet(unsigned int pos) { if (pos >= _stateStateStack.size()) { osg::notify(osg::NOTICE)<<"Warning: State::removeStateSet("<pos) { tempStack.push_back(_stateStateStack.back()); popStateSet(); } // remove the intended StateSet as well popStateSet(); // push back the original ones that were above the remove StateSet for(StateSetStack::reverse_iterator itr = tempStack.rbegin(); itr != tempStack.rend(); ++itr) { pushStateSet(*itr); } } void State::captureCurrentState(StateSet& stateset) const { // empty the stateset first. stateset.clear(); for(ModeMap::const_iterator mitr=_modeMap.begin(); mitr!=_modeMap.end(); ++mitr) { // note GLMode = mitr->first const ModeStack& ms = mitr->second; if (!ms.valueVec.empty()) { stateset.setMode(mitr->first,ms.valueVec.back()); } } for(AttributeMap::const_iterator aitr=_attributeMap.begin(); aitr!=_attributeMap.end(); ++aitr) { const AttributeStack& as = aitr->second; if (!as.attributeVec.empty()) { stateset.setAttribute(const_cast(as.attributeVec.back().first)); } } } // revert to using maximum for consistency, maximum should be defined by STLport on VS. // // visual studio 6.0 doesn't appear to define maximum?!? So do our own here.. // template // T mymax(const T& a,const T& b) // { // return (((a) > (b)) ? (a) : (b)); // } void State::apply(const StateSet* dstate) { if (_checkGLErrors==ONCE_PER_ATTRIBUTE) checkGLErrors("start of State::apply(StateSet*)"); // equivalent to: //pushStateSet(dstate); //apply(); //popStateSet(); //return; if (dstate) { applyModeList(_modeMap,dstate->getModeList()); applyAttributeList(_attributeMap,dstate->getAttributeList()); const StateSet::TextureModeList& ds_textureModeList = dstate->getTextureModeList(); const StateSet::TextureAttributeList& ds_textureAttributeList = dstate->getTextureAttributeList(); unsigned int unit; unsigned int unitMax = maximum(static_cast(ds_textureModeList.size()),static_cast(ds_textureAttributeList.size())); unitMax = maximum(static_cast(unitMax),static_cast(_textureModeMapList.size())); unitMax = maximum(static_cast(unitMax),static_cast(_textureAttributeMapList.size())); for(unit=0;unitgetUniformList()); } else { // no incoming stateset, so simply apply state. apply(); } if (_checkGLErrors==ONCE_PER_ATTRIBUTE) checkGLErrors("end of State::apply(StateSet*)"); } void State::apply() { if (_checkGLErrors==ONCE_PER_ATTRIBUTE) checkGLErrors("start of State::apply()"); // go through all active OpenGL modes, enabling/disable where // appropriate. applyModeMap(_modeMap); // go through all active StateAttribute's, applying where appropriate. applyAttributeMap(_attributeMap); unsigned int unit; unsigned int unitMax = maximum(_textureModeMapList.size(),_textureAttributeMapList.size()); for(unit=0;unit=_textureModeMapList.size()) return false; return getLastAppliedMode(_textureModeMapList[unit],mode); } const StateAttribute* State::getLastAppliedTextureAttribute(unsigned int unit,StateAttribute::Type type, unsigned int member) const { if (unit>=_textureAttributeMapList.size()) return false; return getLastAppliedAttribute(_textureAttributeMapList[unit],type,member); } void State::haveAppliedMode(ModeMap& modeMap,StateAttribute::GLMode mode,StateAttribute::GLModeValue value) { ModeStack& ms = modeMap[mode]; ms.last_applied_value = value & StateAttribute::ON; // will need to disable this mode on next apply so set it to changed. ms.changed = true; } /** mode has been set externally, update state to reflect this setting.*/ void State::haveAppliedMode(ModeMap& modeMap,StateAttribute::GLMode mode) { ModeStack& ms = modeMap[mode]; // don't know what last applied value is can't apply it. // assume that it has changed by toggle the value of last_applied_value. ms.last_applied_value = !ms.last_applied_value; // will need to disable this mode on next apply so set it to changed. ms.changed = true; } /** attribute has been applied externally, update state to reflect this setting.*/ void State::haveAppliedAttribute(AttributeMap& attributeMap,const StateAttribute* attribute) { if (attribute) { AttributeStack& as = attributeMap[attribute->getTypeMemberPair()]; as.last_applied_attribute = attribute; // will need to update this attribute on next apply so set it to changed. as.changed = true; } } void State::haveAppliedAttribute(AttributeMap& attributeMap,StateAttribute::Type type, unsigned int member) { AttributeMap::iterator itr = attributeMap.find(StateAttribute::TypeMemberPair(type,member)); if (itr!=attributeMap.end()) { AttributeStack& as = itr->second; as.last_applied_attribute = 0L; // will need to update this attribute on next apply so set it to changed. as.changed = true; } } bool State::getLastAppliedMode(const ModeMap& modeMap,StateAttribute::GLMode mode) const { ModeMap::const_iterator itr = modeMap.find(mode); if (itr!=modeMap.end()) { const ModeStack& ms = itr->second; return ms.last_applied_value; } else { return false; } } const StateAttribute* State::getLastAppliedAttribute(const AttributeMap& attributeMap,StateAttribute::Type type, unsigned int member) const { AttributeMap::const_iterator itr = attributeMap.find(StateAttribute::TypeMemberPair(type,member)); if (itr!=attributeMap.end()) { const AttributeStack& as = itr->second; return as.last_applied_attribute; } else { return NULL; } } void State::dirtyAllModes() { for(ModeMap::iterator mitr=_modeMap.begin(); mitr!=_modeMap.end(); ++mitr) { ModeStack& ms = mitr->second; ms.last_applied_value = !ms.last_applied_value; ms.changed = true; } for(TextureModeMapList::iterator tmmItr=_textureModeMapList.begin(); tmmItr!=_textureModeMapList.end(); ++tmmItr) { for(ModeMap::iterator mitr=tmmItr->begin(); mitr!=tmmItr->end(); ++mitr) { ModeStack& ms = mitr->second; ms.last_applied_value = !ms.last_applied_value; ms.changed = true; } } } void State::dirtyAllAttributes() { for(AttributeMap::iterator aitr=_attributeMap.begin(); aitr!=_attributeMap.end(); ++aitr) { AttributeStack& as = aitr->second; as.last_applied_attribute = 0; as.changed = true; } for(TextureAttributeMapList::iterator tamItr=_textureAttributeMapList.begin(); tamItr!=_textureAttributeMapList.end(); ++tamItr) { AttributeMap& attributeMap = *tamItr; for(AttributeMap::iterator aitr=attributeMap.begin(); aitr!=attributeMap.end(); ++aitr) { AttributeStack& as = aitr->second; as.last_applied_attribute = 0; as.changed = true; } } } Polytope State::getViewFrustum() const { Polytope cv; cv.setToUnitFrustum(); cv.transformProvidingInverse((*_modelView)*(*_projection)); return cv; } void State::disableAllVertexArrays() { disableVertexPointer(); disableTexCoordPointersAboveAndIncluding(0); disableVertexAttribPointersAboveAndIncluding(0); disableColorPointer(); disableFogCoordPointer(); disableNormalPointer(); disableSecondaryColorPointer(); } void State::dirtyAllVertexArrays() { dirtyVertexPointer(); dirtyTexCoordPointersAboveAndIncluding(0); dirtyVertexAttribPointersAboveAndIncluding(0); dirtyColorPointer(); dirtyFogCoordPointer(); dirtyNormalPointer(); dirtySecondaryColorPointer(); } void State::setInterleavedArrays( GLenum format, GLsizei stride, const GLvoid* pointer) { disableAllVertexArrays(); #ifdef OSG_GL_VERTEX_ARRAY_FUNCS_AVAILABLE glInterleavedArrays( format, stride, pointer); #endif // the crude way, assume that all arrays have been effected so dirty them and // disable them... dirtyAllVertexArrays(); } void State::initializeExtensionProcs() { if (_extensionProcsInitialized) return; setGLExtensionFuncPtr(_glClientActiveTexture,"glClientActiveTexture","glClientActiveTextureARB"); setGLExtensionFuncPtr(_glActiveTexture, "glActiveTexture","glActiveTextureARB"); setGLExtensionFuncPtr(_glFogCoordPointer, "glFogCoordPointer","glFogCoordPointerEXT"); setGLExtensionFuncPtr(_glSecondaryColorPointer, "glSecondaryColorPointer","glSecondaryColorPointerEXT"); setGLExtensionFuncPtr(_glVertexAttribPointer, "glVertexAttribPointer","glVertexAttribPointerARB"); setGLExtensionFuncPtr(_glEnableVertexAttribArray, "glEnableVertexAttribArray","glEnableVertexAttribArrayARB"); setGLExtensionFuncPtr(_glMultiTexCoord4f, "glMultiTexCoord4f","glMultiTexCoord4fARB"); setGLExtensionFuncPtr(_glVertexAttrib4f, "glVertexAttrib4f"); setGLExtensionFuncPtr(_glVertexAttrib4fv, "glVertexAttrib4fv"); setGLExtensionFuncPtr(_glDisableVertexAttribArray, "glDisableVertexAttribArray","glDisableVertexAttribArrayARB"); setGLExtensionFuncPtr(_glBindBuffer, "glBindBuffer","glBindBufferARB"); setGLExtensionFuncPtr(_glDrawArraysInstanced, "glDrawArraysInstanced","glDrawArraysInstancedARB","glDrawArraysInstancedEXT"); setGLExtensionFuncPtr(_glDrawElementsInstanced, "glDrawElementsInstanced","glDrawElementsInstancedARB","glDrawElementsInstancedEXT"); if ( osg::getGLVersionNumber() >= 2.0 || osg::isGLExtensionSupported(_contextID,"GL_ARB_vertex_shader") ) { glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,&_glMaxTextureUnits); glGetIntegerv(GL_MAX_TEXTURE_COORDS,&_glMaxTextureCoords); } else if ( osg::getGLVersionNumber() >= 1.3 || osg::isGLExtensionSupported(_contextID,"GL_ARB_multitexture") || osg::isGLExtensionSupported(_contextID,"GL_EXT_multitexture") ) { GLint maxTextureUnits; glGetIntegerv(GL_MAX_TEXTURE_UNITS,&maxTextureUnits); _glMaxTextureUnits = maxTextureUnits; _glMaxTextureCoords = maxTextureUnits; } else { _glMaxTextureUnits = 1; _glMaxTextureCoords = 1; } _extensionProcsInitialized = true; } bool State::setClientActiveTextureUnit( unsigned int unit ) { if (unit!=_currentClientActiveTextureUnit) { if (_glClientActiveTexture && unit < (unsigned int)_glMaxTextureCoords) { _glClientActiveTexture(GL_TEXTURE0+unit); _currentClientActiveTextureUnit = unit; } else { return unit==0; } } return true; } /** set the current texture unit, return true if selected, false if selection failed such as when multitexturing is not supported. * note, only updates values that change.*/ bool State::setActiveTextureUnit( unsigned int unit ) { if (unit!=_currentActiveTextureUnit) { if (_glActiveTexture && unit < (unsigned int)(maximum(_glMaxTextureCoords,_glMaxTextureUnits)) ) { _glActiveTexture(GL_TEXTURE0+unit); _currentActiveTextureUnit = unit; } else { return unit==0; } } return true; } void State::setFogCoordPointer(GLenum type, GLsizei stride, const GLvoid *ptr) { #ifdef OSG_GL_VERTEX_ARRAY_FUNCS_AVAILABLE if (_useVertexAttributeAliasing) { setVertexAttribPointer(_fogCoordAlias._location, 1, type, GL_FALSE, stride, ptr); } else { if (_glFogCoordPointer) { if (!_fogArray._enabled || _fogArray._dirty) { _fogArray._enabled = true; glEnableClientState(GL_FOG_COORDINATE_ARRAY); } //if (_fogArray._pointer!=ptr || _fogArray._dirty) { _fogArray._pointer=ptr; _glFogCoordPointer( type, stride, ptr ); } _fogArray._lazy_disable = false; _fogArray._dirty = false; } } #else setVertexAttribPointer(_fogCoordAlias._location, 1, type, GL_FALSE, stride, ptr); #endif } void State::setSecondaryColorPointer( GLint size, GLenum type, GLsizei stride, const GLvoid *ptr ) { #ifdef OSG_GL_VERTEX_ARRAY_FUNCS_AVAILABLE if (_useVertexAttributeAliasing) { setVertexAttribPointer(_secondaryColorAlias._location, size, type, GL_FALSE, stride, ptr); } else { if (_glSecondaryColorPointer) { if (!_secondaryColorArray._enabled || _secondaryColorArray._dirty) { _secondaryColorArray._enabled = true; glEnableClientState(GL_SECONDARY_COLOR_ARRAY); } //if (_secondaryColorArray._pointer!=ptr || _secondaryColorArray._dirty) { _secondaryColorArray._pointer=ptr; _glSecondaryColorPointer( size, type, stride, ptr ); } _secondaryColorArray._lazy_disable = false; _secondaryColorArray._dirty = false; } } #else setVertexAttribPointer(_secondaryColorAlias._location, size, type, GL_FALSE, stride, ptr); #endif } /** wrapper around glEnableVertexAttribArrayARB(index);glVertexAttribPointerARB(..); * note, only updates values that change.*/ void State::setVertexAttribPointer( unsigned int index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *ptr ) { if (_glVertexAttribPointer) { // osg::notify(osg::NOTICE)<<"State::setVertexAttribPointer("<= _vertexAttribArrayList.size()) _vertexAttribArrayList.resize(index+1); EnabledArrayPair& eap = _vertexAttribArrayList[index]; if (!eap._enabled || eap._dirty) { eap._enabled = true; // osg::notify(osg::NOTICE)<<" _glEnableVertexAttribArray( "<= _vertexAttribArrayList.size()) _vertexAttribArrayList.resize(index+1); EnabledArrayPair& eap = _vertexAttribArrayList[index]; if (eap._enabled || eap._dirty) { eap._enabled = false; eap._dirty = false; // osg::notify(osg::NOTICE)<<" _glDisableVertexAttribArray( "<_lazy_disable = true; } } for(EnabledVertexAttribArrayList::iterator itr = _vertexAttribArrayList.begin(); itr != _vertexAttribArrayList.end(); ++itr) { itr->_lazy_disable = true; } } void State::applyDisablingOfVertexAttributes() { //osg::notify(osg::NOTICE)<<"start of applyDisablingOfVertexAttributes()"<className()<<" "<className()<<" "<className()<<" "<apply(*_modelViewMatrixUniform); if (_projectionMatrixUniform) _lastAppliedProgramObject->apply(*_projectionMatrixUniform); if (_modelViewProjectionMatrixUniform) _lastAppliedProgramObject->apply(*_modelViewProjectionMatrixUniform); if (_normalMatrixUniform) _lastAppliedProgramObject->apply(*_normalMatrixUniform); } namespace State_Utils { bool replace(std::string& str, const std::string& original_phrase, const std::string& new_phrase) { bool replacedStr = false; std::string::size_type pos = 0; while((pos=str.find(original_phrase, pos))!=std::string::npos) { std::string::size_type endOfPhrasePos = pos+original_phrase.size(); if (endOfPhrasePos='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z')) { pos = endOfPhrasePos; continue; } } replacedStr = true; str.replace(pos, original_phrase.size(), new_phrase); } return replacedStr; } void replaceAndInsertDeclaration(std::string& source, const std::string& originalStr, const std::string& newStr, const std::string& declarationPrefix) { if (replace(source, originalStr, newStr)) { source.insert(0, declarationPrefix + newStr + std::string(";\n")); } } } bool State::convertVertexShaderSourceToOsgBuiltIns(std::string& source) const { osg::notify(osg::NOTICE)<<"State::convertShaderSourceToOsgBuiltIns()"<set(*_projection); updateModelViewAndProjectionMatrixUniforms(); } #ifdef OSG_GL_MATRICES_AVAILABLE glMatrixMode( GL_PROJECTION ); glLoadMatrix(_projection->ptr()); glMatrixMode( GL_MODELVIEW ); #endif } } void State::applyModelViewMatrix(const osg::RefMatrix* matrix) { if (_modelView!=matrix) { if (matrix) { _modelView=matrix; } else { _modelView=_identity; } if (_useModelViewAndProjectionUniforms) { if (_modelViewMatrixUniform.valid()) _modelViewMatrixUniform->set(*_modelView); updateModelViewAndProjectionMatrixUniforms(); } #ifdef OSG_GL_MATRICES_AVAILABLE glLoadMatrix(_modelView->ptr()); #endif } } #include void State::updateModelViewAndProjectionMatrixUniforms() { if (_modelViewProjectionMatrixUniform.valid()) _modelViewProjectionMatrixUniform->set((*_modelView) * (*_projection)); if (_normalMatrixUniform.valid()) { Matrix mv(*_modelView); mv.setTrans(0.0, 0.0, 0.0); Matrix matrix; matrix.invert(mv); Matrix3 normalMatrix(matrix(0,0), matrix(1,0), matrix(2,0), matrix(0,1), matrix(1,1), matrix(2,1), matrix(0,2), matrix(1,2), matrix(2,2)); _normalMatrixUniform->set(normalMatrix); } }