//C++ header - Open Scene Graph - Copyright (C) 1998-2001 Robert Osfield //Distributed under the terms of the GNU Library General Public License (LGPL) //as published by the Free Software Foundation. #ifndef OSG_STATE #define OSG_STATE 1 #include #include #include #include #include #include #include #include #include namespace osg { /** macro for use with osg::StateAttrbiute::apply methods for detected and * reporting OpenGL error messages.*/ #define OSG_GL_DEBUG(message) \ if (state.getFineGrainedErrorDetection()) \ { \ GLenum errorNo = glGetError(); \ if (errorNo!=GL_NO_ERROR) \ { \ osg::notify(WARN)<<"Warning: detected OpenGL error '"<ptr()); } else { _projection=_identity; glLoadIdentity(); } glMatrixMode( GL_MODELVIEW ); } } const osg::Matrix& getProjectionMatrix() const { return *_projection; } inline void applyModelViewMatrix(const osg::Matrix* matrix) { if (_modelView!=matrix) { if (matrix) { _modelView=matrix; glLoadMatrixf(matrix->ptr()); } else { _modelView=_identity; glLoadIdentity(); } } } const osg::Matrix& getModelViewMatrix() const { return *_modelView; } Polytope getViewFrustum() const; /** Apply stateset.*/ void apply(const StateSet* dstate); /** Apply the state.*/ void apply(); /** Apply an OpenGL mode if required. */ inline const bool applyMode(const StateAttribute::GLMode mode,const bool enabled) { ModeStack& ms = _modeMap[mode]; ms.changed = true; return applyMode(mode,enabled,ms); } inline const bool applyTextureMode(unsigned int unit, const StateAttribute::GLMode mode,const bool enabled) { ModeMap& modeMap = getOrCreateTextureModeMap(unit); ModeStack& ms = modeMap[mode]; ms.changed = true; return applyMode(mode,enabled,ms); } /** Apply an attribute if required. */ inline const bool applyAttribute(const StateAttribute* attribute) { AttributeStack& as = _attributeMap[attribute->getType()]; as.changed = true; return applyAttribute(attribute,as); } inline const bool applyTextureAttribute(unsigned int unit, const StateAttribute* attribute) { AttributeMap& attributeMap = getOrCreateTextureAttributeMap(unit); AttributeStack& as = attributeMap[attribute->getType()]; as.changed = true; return applyAttribute(attribute,as); } /** Mode has been set externally, update state to reflect this setting.*/ void haveAppliedMode(const StateAttribute::GLMode mode,const StateAttribute::GLModeValue value); /** Mode has been set externally, therefore dirty the associated mode in osg::State * so it is applied on next call to osg::State::apply(..)*/ void haveAppliedMode(const StateAttribute::GLMode mode); /** Attribute has been applied externally, update state to reflect this setting.*/ void haveAppliedAttribute(const StateAttribute* attribute); /** Attribute has been applied externally, * and therefore this attribute type has been dirtied * and will need to be re-appplied on next osg::State.apply(..). * note, if you have an osg::StateAttribute which you have applied externally * then use the have_applied(attribute) method as this will the osg::State to * track the current state more accuratly and enable lazy state updating such * that only changed state will be applied.*/ void haveAppliedAttribute(const StateAttribute::Type type); /** Get whether the current specified mode is enabled (true) or disabled (false).*/ const bool getLastAppliedMode(const StateAttribute::GLMode mode) const; /** Get the current specified attribute, return NULL is one has not yet been applied.*/ const StateAttribute* getLastAppliedAttribute(const StateAttribute::Type type) const; /** texture Mode has been set externally, update state to reflect this setting.*/ void haveAppliedTextureMode(unsigned int unit, const StateAttribute::GLMode mode,const StateAttribute::GLModeValue value); /** texture Mode has been set externally, therefore dirty the associated mode in osg::State * so it is applied on next call to osg::State::apply(..)*/ void haveAppliedTextureMode(unsigned int unit, const StateAttribute::GLMode mode); /** texture Attribute has been applied externally, update state to reflect this setting.*/ void haveAppliedTextureAttribute(unsigned int unit, const StateAttribute* attribute); /** texture Attribute has been applied externally, * and therefore this attribute type has been dirtied * and will need to be re-appplied on next osg::State.apply(..). * note, if you have an osg::StateAttribute which you have applied externally * then use the have_applied(attribute) method as this will the osg::State to * track the current state more accuratly and enable lazy state updating such * that only changed state will be applied.*/ void haveAppliedTextureAttribute(unsigned int unit, const StateAttribute::Type type); /** Get whether the current specified texture mode is enabled (true) or disabled (false).*/ const bool getLastAppliedTextureMode(unsigned int unit, const StateAttribute::GLMode mode) const; /** Get the current specified texture attribute, return NULL is one has not yet been applied.*/ const StateAttribute* getLastAppliedTextureAttribute(unsigned int unit, const StateAttribute::Type type) const; /** wrapper around glEnableClientState(GL_VERTEX_ARRAY);glVertexPointer(..); * note, only updates values that change.*/ inline void setVertexPointer( GLint size, GLenum type, GLsizei stride, const GLvoid *ptr ) { if (!_vertexArray._enabled) { _vertexArray._enabled = true; glEnableClientState(GL_VERTEX_ARRAY); } if (_vertexArray._pointer!=ptr) { _vertexArray._pointer=ptr; glVertexPointer( size, type, stride, ptr ); } } /** wrapper glDisableClientState(GL_VERTEX_ARRAY). * note, only updates values that change.*/ inline void disableVertexPointer() { if (_vertexArray._enabled) { _vertexArray._enabled = false; glDisableClientState(GL_VERTEX_ARRAY); } } /** wrapper around glEnableClientState(GL_NORMAL_ARRAY);glNormalPointer(..); * note, only updates values that change.*/ inline void setNormalPointer( GLenum type, GLsizei stride, const GLvoid *ptr ) { if (!_normalArray._enabled) { _normalArray._enabled = true; glEnableClientState(GL_NORMAL_ARRAY); } if (_normalArray._pointer!=ptr) { _normalArray._pointer=ptr; glNormalPointer( type, stride, ptr ); } } /** wrapper around glDisableClientState(GL_NORMAL_ARRAY); * note, only updates values that change.*/ inline void disableNormalPointer() { if (_normalArray._enabled) { _normalArray._enabled = false; glDisableClientState(GL_NORMAL_ARRAY); } } /** wrapper around glEnableClientState(GL_COLOR_ARRAY);glColorPointer(..); * note, only updates values that change.*/ inline void setColorPointer( GLint size, GLenum type, GLsizei stride, const GLvoid *ptr ) { if (!_colorArray._enabled) { _colorArray._enabled = true; glEnableClientState(GL_COLOR_ARRAY); } if (_colorArray._pointer!=ptr) { _colorArray._pointer=ptr; glColorPointer( size, type, stride, ptr ); } } /** wrapper around glDisableClientState(GL_COLOR_ARRAY); * note, only updates values that change.*/ inline void disableColorPointer() { if (_colorArray._enabled) { _colorArray._enabled = false; glDisableClientState(GL_COLOR_ARRAY); } } /** wrapper around glEnableClientState(GL_INDEX_ARRAY);glIndexPointer(..); * note, only updates values that change.*/ inline void setIndexPointer( GLenum type, GLsizei stride, const GLvoid *ptr ) { if (!_indexArray._enabled) { _indexArray._enabled = true; glEnableClientState(GL_INDEX_ARRAY); } if (_indexArray._pointer!=ptr) { _indexArray._pointer=ptr; glIndexPointer( type, stride, ptr ); } } /** wrapper around glDisableClientState(GL_INDEX_ARRAY); * note, only updates values that change.*/ inline void disableIndexPointer() { if (_indexArray._enabled) { _indexArray._enabled = false; glDisableClientState(GL_INDEX_ARRAY); } } /** wrapper around glEnableClientState(GL_TEXTURE_COORD_ARRAY);glTexCoordPointer(..); * note, only updates values that change.*/ inline void setTexCoordPointer( unsigned int unit, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr ) { if (setClientActiveTextureUnit(unit)) { if ( unit >= _texCoordArrayList.size()) _texCoordArrayList.resize(unit+1); EnabledArrayPair& eap = _texCoordArrayList[unit]; if (!eap._enabled) { eap._enabled = true; glEnableClientState(GL_TEXTURE_COORD_ARRAY); } if (eap._pointer!=ptr) { glTexCoordPointer( size, type, stride, ptr ); eap._pointer = ptr; } } } /** wrapper around glDisableClientState(GL_TEXTURE_COORD_ARRAY); * note, only updates values that change.*/ inline void disableTexCoordPointer( unsigned int unit ) { if (setClientActiveTextureUnit(unit)) { if ( unit >= _texCoordArrayList.size()) _texCoordArrayList.resize(unit+1); EnabledArrayPair& eap = _texCoordArrayList[unit]; if (eap._enabled) { eap._enabled = false; glDisableClientState(GL_TEXTURE_COORD_ARRAY); } } } /** set the current tex coord array texture unit, return true if selected, false if selection failed such as when multitexturing is not supported. * note, only updates values that change.*/ inline bool setClientActiveTextureUnit( unsigned int unit ) { if (unit!=_currentClientActiveTextureUnit) { static ActiveTextureProc s_glClientActiveTexture = (ActiveTextureProc) osg::getGLExtensionFuncPtr("glClientActiveTexture","glClientActiveTextureARB"); if (s_glClientActiveTexture) { s_glClientActiveTexture(GL_TEXTURE0_ARB+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.*/ inline bool setActiveTextureUnit( unsigned int unit ) { if (unit!=_currentActiveTextureUnit) { static ActiveTextureProc s_glActiveTexture = (ActiveTextureProc) osg::getGLExtensionFuncPtr("glActiveTexture","glActiveTextureARB"); if (s_glActiveTexture) { s_glActiveTexture(GL_TEXTURE0_ARB+unit); _currentActiveTextureUnit = unit; } else { return unit==0; } } return true; } /** Set the current OpenGL context uniqueID. Note, it is the application developers responsibility to set up unique ID for each OpenGL context. This value is then used by osg::StateAttribure's and osg::Drawable's to help manage OpenGL display list and texture binds appropriate for each context.*/ inline void setContextID(unsigned int contextID) { _contextID=contextID; } /** Get the current OpenGL context unique ID.*/ inline const unsigned int getContextID() const { return _contextID; } /** Set the frame stamp for the current frame.*/ inline void setFrameStamp(FrameStamp* fs) { _frameStamp = fs; } /** Set the frame stamp for the current frame.*/ inline const FrameStamp* getFrameStamp() const { return _frameStamp.get(); } /** Set the DisplaySettings. Note, nothing is applied, the visual settings are just used * used in the State object to pass the current visual settings to Drawables * during rendering. */ inline void setDisplaySettings(DisplaySettings* vs) { _displaySettings = vs; } /** Get the DisplaySettings */ inline const DisplaySettings* getDisplaySettings() const { return _displaySettings.get(); } typedef std::pair AttributePair; typedef std::vector AttributeVec; typedef std::vector ValueVec; private: unsigned int _contextID; ref_ptr _frameStamp; ref_ptr _identity; ref_ptr _projection; ref_ptr _modelView; ref_ptr _displaySettings; struct ModeStack { ModeStack() { changed = false; last_applied_value = false; global_default_value = false; } bool changed; bool last_applied_value; bool global_default_value; ValueVec valueVec; }; struct AttributeStack { AttributeStack() { changed = false; last_applied_attribute = 0L; global_default_attribute = 0L; } /** apply an attribute if required, passing in attribute and appropriate attribute stack */ bool changed; const StateAttribute* last_applied_attribute; ref_ptr global_default_attribute; AttributeVec attributeVec; }; /** apply an OpenGL mode if required, passing in mode, enable flag and appropriate mode stack */ inline const bool applyMode(const StateAttribute::GLMode mode,const bool enabled,ModeStack& ms) { if (ms.last_applied_value != enabled) { ms.last_applied_value = enabled; if (enabled) glEnable(mode); else glDisable(mode); return true; } else return false; } /** apply an attribute if required, passing in attribute and appropriate attribute stack */ inline const bool applyAttribute(const StateAttribute* attribute,AttributeStack& as) { if (as.last_applied_attribute != attribute) { if (!as.global_default_attribute.valid()) as.global_default_attribute = dynamic_cast(attribute->cloneType()); as.last_applied_attribute = attribute; attribute->apply(*this); return true; } else return false; } inline const bool applyGlobalDefaultAttribute(AttributeStack& as) { if (as.last_applied_attribute != as.global_default_attribute.get()) { as.last_applied_attribute = as.global_default_attribute.get(); if (as.global_default_attribute.valid()) as.global_default_attribute->apply(*this); return true; } else return false; } typedef void (APIENTRY * ActiveTextureProc) (GLenum texture); typedef std::map ModeMap; typedef std::vector TextureModeMapList; typedef std::map AttributeMap; typedef std::vector TextureAttributeMapList; typedef std::vector > StateSetStack; typedef std::vector > MatrixStack; ModeMap _modeMap; AttributeMap _attributeMap; TextureModeMapList _textureModeMapList; TextureAttributeMapList _textureAttributeMapList; StateSetStack _drawStateStack; struct EnabledArrayPair { EnabledArrayPair():_enabled(false),_pointer(0) {} EnabledArrayPair(const EnabledArrayPair& eap):_enabled(eap._enabled),_pointer(eap._pointer) {} EnabledArrayPair& operator = (const EnabledArrayPair& eap) { _enabled=eap._enabled; _pointer=eap._pointer; return *this; } bool _enabled; const GLvoid* _pointer; }; typedef std::vector EnabledTexCoordArrayList; EnabledArrayPair _vertexArray; EnabledArrayPair _colorArray; EnabledArrayPair _indexArray; EnabledArrayPair _normalArray; EnabledTexCoordArrayList _texCoordArrayList; unsigned int _currentActiveTextureUnit; unsigned int _currentClientActiveTextureUnit; inline ModeMap& getOrCreateTextureModeMap(unsigned int unit) { if (unit>=_textureModeMapList.size()) _textureModeMapList.resize(unit+1); return _textureModeMapList[unit]; } inline AttributeMap& getOrCreateTextureAttributeMap(unsigned int unit) { if (unit>=_textureAttributeMapList.size()) _textureAttributeMapList.resize(unit+1); return _textureAttributeMapList[unit]; } inline void pushModeList(ModeMap& modeMap,const StateSet::ModeList& modeList); inline void pushAttributeList(AttributeMap& attributeMap,const StateSet::AttributeList& attributeList); inline void popModeList(ModeMap& modeMap,const StateSet::ModeList& modeList); inline void popAttributeList(AttributeMap& attributeMap,const StateSet::AttributeList& attributeList); inline void applyModeList(ModeMap& modeMap,const StateSet::ModeList& modeList); inline void applyAttributeList(AttributeMap& attributeMap,const StateSet::AttributeList& attributeList); inline void applyModeMap(ModeMap& modeMap); inline void applyAttributeMap(AttributeMap& attributeMap); void haveAppliedMode(ModeMap& modeMap,const StateAttribute::GLMode mode,const StateAttribute::GLModeValue value); void haveAppliedMode(ModeMap& modeMap,const StateAttribute::GLMode mode); void haveAppliedAttribute(AttributeMap& attributeMap,const StateAttribute* attribute); void haveAppliedAttribute(AttributeMap& attributeMap,const StateAttribute::Type type); const bool getLastAppliedMode(const ModeMap& modeMap,const StateAttribute::GLMode mode) const; const StateAttribute* getLastAppliedAttribute(const AttributeMap& attributeMap,const StateAttribute::Type type) const; }; inline void State::pushModeList(ModeMap& modeMap,const StateSet::ModeList& modeList) { for(StateSet::ModeList::const_iterator mitr=modeList.begin(); mitr!=modeList.end(); ++mitr) { // get the mode stack for incomming GLmode {mitr->first}. ModeStack& ms = modeMap[mitr->first]; if (ms.valueVec.empty()) { // first pair so simply push incomming pair to back. ms.valueVec.push_back(mitr->second); } else if (ms.valueVec.back() & StateAttribute::OVERRIDE) // check the existing override flag { // push existing back since override keeps the previoius value. ms.valueVec.push_back(ms.valueVec.back()); } else { // no override on so simply push incomming pair to back. ms.valueVec.push_back(mitr->second); } ms.changed = true; } } inline void State::pushAttributeList(AttributeMap& attributeMap,const StateSet::AttributeList& attributeList) { for(StateSet::AttributeList::const_iterator aitr=attributeList.begin(); aitr!=attributeList.end(); ++aitr) { // get the attribute stack for incomming type {aitr->first}. AttributeStack& as = attributeMap[aitr->first]; if (as.attributeVec.empty()) { // first pair so simply push incomming pair to back. as.attributeVec.push_back( AttributePair(aitr->second.first.get(),aitr->second.second)); } else if (as.attributeVec.back().second & StateAttribute::OVERRIDE) // check the existing override flag { // push existing back since override keeps the previoius value. as.attributeVec.push_back(as.attributeVec.back()); } else { // no override on so simply push incomming pair to back. as.attributeVec.push_back( AttributePair(aitr->second.first.get(),aitr->second.second)); } as.changed = true; } } inline void State::popModeList(ModeMap& modeMap,const StateSet::ModeList& modeList) { for(StateSet::ModeList::const_iterator mitr=modeList.begin(); mitr!=modeList.end(); ++mitr) { // get the mode stack for incomming GLmode {mitr->first}. ModeStack& ms = modeMap[mitr->first]; if (!ms.valueVec.empty()) { ms.valueVec.pop_back(); } ms.changed = true; } } inline void State::popAttributeList(AttributeMap& attributeMap,const StateSet::AttributeList& attributeList) { for(StateSet::AttributeList::const_iterator aitr=attributeList.begin(); aitr!=attributeList.end(); ++aitr) { // get the attribute stack for incomming type {aitr->first}. AttributeStack& as = attributeMap[aitr->first]; if (!as.attributeVec.empty()) { as.attributeVec.pop_back(); } as.changed = true; } } inline void State::applyModeList(ModeMap& modeMap,const StateSet::ModeList& modeList) { StateSet::ModeList::const_iterator ds_mitr = modeList.begin(); ModeMap::iterator this_mitr=modeMap.begin(); while (this_mitr!=modeMap.end() && ds_mitr!=modeList.end()) { if (this_mitr->firstfirst) { // note GLMode = this_mitr->first ModeStack& ms = this_mitr->second; if (ms.changed) { ms.changed = false; if (!ms.valueVec.empty()) { bool new_value = ms.valueVec.back() & StateAttribute::ON; applyMode(this_mitr->first,new_value,ms); } else { // assume default of disabled. applyMode(this_mitr->first,ms.global_default_value,ms); } } ++this_mitr; } else if (ds_mitr->firstfirst) { // ds_mitr->first is a new mode, therefore // need to insert a new mode entry for ds_mistr->first. ModeStack& ms = modeMap[ds_mitr->first]; bool new_value = ds_mitr->second & StateAttribute::ON; applyMode(ds_mitr->first,new_value,ms); // will need to disable this mode on next apply so set it to changed. ms.changed = true; ++ds_mitr; } else { // this_mitr & ds_mitr refer to the same mode, check the overide // if any otherwise just apply the incomming mode. ModeStack& ms = this_mitr->second; if (!ms.valueVec.empty() && ms.valueVec.back() & StateAttribute::OVERRIDE) { // override is on, there just treat as a normal apply on modes. if (ms.changed) { ms.changed = false; bool new_value = ms.valueVec.back() & StateAttribute::ON; applyMode(this_mitr->first,new_value,ms); } } else { // no override on or no previous entry, therefore consider incomming mode. bool new_value = ds_mitr->second & StateAttribute::ON; if (applyMode(ds_mitr->first,new_value,ms)) { ms.changed = true; } } ++this_mitr; ++ds_mitr; } } // iterator over the remaining state modes to apply any previous changes. for(; this_mitr!=modeMap.end(); ++this_mitr) { // note GLMode = this_mitr->first ModeStack& ms = this_mitr->second; if (ms.changed) { ms.changed = false; if (!ms.valueVec.empty()) { bool new_value = ms.valueVec.back() & StateAttribute::ON; applyMode(this_mitr->first,new_value,ms); } else { // assume default of disabled. applyMode(this_mitr->first,ms.global_default_value,ms); } } } // iterator over the remaining incomming modes to apply any new mode. for(; ds_mitr!=modeList.end(); ++ds_mitr) { ModeStack& ms = modeMap[ds_mitr->first]; bool new_value = ds_mitr->second & StateAttribute::ON; applyMode(ds_mitr->first,new_value,ms); // will need to disable this mode on next apply so set it to changed. ms.changed = true; } } inline void State::applyAttributeList(AttributeMap& attributeMap,const StateSet::AttributeList& attributeList) { StateSet::AttributeList::const_iterator ds_aitr=attributeList.begin(); AttributeMap::iterator this_aitr=attributeMap.begin(); while (this_aitr!=attributeMap.end() && ds_aitr!=attributeList.end()) { if (this_aitr->firstfirst) { // note attribute type = this_aitr->first AttributeStack& as = this_aitr->second; if (as.changed) { as.changed = false; if (!as.attributeVec.empty()) { const StateAttribute* new_attr = as.attributeVec.back().first; applyAttribute(new_attr,as); } else { applyGlobalDefaultAttribute(as); } } ++this_aitr; } else if (ds_aitr->firstfirst) { // ds_mitr->first is a new attribute, therefore // need to insert a new attribute entry for ds_aistr->first. AttributeStack& as = attributeMap[ds_aitr->first]; const StateAttribute* new_attr = ds_aitr->second.first.get(); applyAttribute(new_attr,as); // will need to disable this mode on next apply so set it to changed. as.changed = true; ++ds_aitr; } else { // this_mitr & ds_mitr refer to the same mode, check the overide // if any otherwise just apply the incomming mode. AttributeStack& as = this_aitr->second; if (!as.attributeVec.empty() && as.attributeVec.back().second) { // override is os, there just treat as a normal apply on modes. if (as.changed) { as.changed = false; const StateAttribute* new_attr = as.attributeVec.back().first; applyAttribute(new_attr,as); } } else { // no override on or no previous entry, therefore consider incomming mode. const StateAttribute* new_attr = ds_aitr->second.first.get(); if (applyAttribute(new_attr,as)) { as.changed = true; } } ++this_aitr; ++ds_aitr; } } // iterator over the remaining state modes to apply any previous changes. for(; this_aitr!=attributeMap.end(); ++this_aitr) { // note attribute type = this_aitr->first AttributeStack& as = this_aitr->second; if (as.changed) { as.changed = false; if (!as.attributeVec.empty()) { const StateAttribute* new_attr = as.attributeVec.back().first; applyAttribute(new_attr,as); } else { applyGlobalDefaultAttribute(as); } } } // iterator over the remaining incomming modes to apply any new mode. for(; ds_aitr!=attributeList.end(); ++ds_aitr) { // ds_mitr->first is a new attribute, therefore // need to insert a new attribute entry for ds_aistr->first. AttributeStack& as = attributeMap[ds_aitr->first]; const StateAttribute* new_attr = ds_aitr->second.first.get(); applyAttribute(new_attr,as); // will need to update this attribute on next apply so set it to changed. as.changed = true; } } inline void State::applyModeMap(ModeMap& modeMap) { for(ModeMap::iterator mitr=modeMap.begin(); mitr!=modeMap.end(); ++mitr) { // note GLMode = mitr->first ModeStack& ms = mitr->second; if (ms.changed) { ms.changed = false; if (!ms.valueVec.empty()) { bool new_value = ms.valueVec.back() & StateAttribute::ON; applyMode(mitr->first,new_value,ms); } else { // assume default of disabled. applyMode(mitr->first,ms.global_default_value,ms); } } } } inline void State::applyAttributeMap(AttributeMap& attributeMap) { for(AttributeMap::iterator aitr=attributeMap.begin(); aitr!=attributeMap.end(); ++aitr) { AttributeStack& as = aitr->second; if (as.changed) { as.changed = false; if (!as.attributeVec.empty()) { const StateAttribute* new_attr = as.attributeVec.back().first; applyAttribute(new_attr,as); } else { applyGlobalDefaultAttribute(as); } } } } } #endif