| |
| |
| #include "tstencilcontrol.h" |
| #include "tgl.h" |
| #include "tthreadmessage.h" |
| #include <stack> |
| |
| #include <QThreadStorage> |
| |
| |
| |
| namespace |
| { |
| |
| |
| class StencilControlManager |
| { |
| |
| QThreadStorage<TStencilControl *> m_storage; |
| |
| StencilControlManager() {} |
| |
| public: |
| static StencilControlManager *instance() |
| { |
| static StencilControlManager theInstance; |
| return &theInstance; |
| } |
| |
| TStencilControl *getCurrentStencilControl() |
| { |
| if (!m_storage.hasLocalData()) { |
| m_storage.setLocalData(new TStencilControl); |
| } |
| |
| return m_storage.localData(); |
| } |
| |
| ~StencilControlManager() |
| { |
| } |
| }; |
| |
| } |
| |
| |
| |
| class TStencilControl::Imp |
| { |
| public: |
| int m_stencilBitCount; |
| int m_pushCount; |
| int m_currentWriting; |
| |
| |
| int m_virtualState; |
| |
| |
| |
| |
| #ifdef _DEBUG |
| std::stack<bool> fullState; |
| |
| |
| |
| #endif |
| |
| unsigned char m_writingMask; |
| unsigned char m_drawOnScreenMask; |
| unsigned char m_enabledMask; |
| unsigned char m_inOrOutMask; |
| unsigned char m_drawOnlyOnceMask; |
| |
| Imp(); |
| |
| void updateOpenGlState(); |
| |
| void pushMask(); |
| |
| |
| |
| |
| void popMask(); |
| |
| |
| void beginMask(DrawMode drawMode); |
| |
| |
| |
| |
| void endMask(); |
| |
| |
| void enableMask(MaskType maskType); |
| void disableMask(); |
| |
| |
| |
| |
| }; |
| |
| |
| |
| TStencilControl::Imp::Imp() |
| : m_stencilBitCount(0), m_pushCount(1), m_currentWriting(-1), m_enabledMask(0), m_writingMask(0), m_inOrOutMask(0), m_drawOnScreenMask(0), m_drawOnlyOnceMask(0), m_virtualState(0) |
| { |
| glGetIntegerv(GL_STENCIL_BITS, (GLint *)&m_stencilBitCount); |
| |
| glStencilMask(0xFFFFFFFF); |
| |
| glClear(GL_STENCIL_BUFFER_BIT); |
| } |
| |
| |
| |
| TStencilControl *TStencilControl::instance() |
| { |
| StencilControlManager *instance = StencilControlManager::instance(); |
| return instance->getCurrentStencilControl(); |
| } |
| |
| |
| |
| TStencilControl::TStencilControl() |
| : m_imp(new Imp) |
| { |
| } |
| |
| |
| |
| TStencilControl::~TStencilControl() |
| { |
| } |
| |
| |
| |
| void TStencilControl::Imp::updateOpenGlState() |
| { |
| if (m_currentWriting >= 0) { |
| unsigned char currentWritingMask = 1 << m_currentWriting; |
| |
| bool drawOnlyOnce = (currentWritingMask & m_drawOnlyOnceMask) != 0; |
| |
| if (currentWritingMask & m_drawOnScreenMask) { |
| unsigned char lastWritingMask; |
| int lastWriting = m_currentWriting - 1; |
| for (; lastWriting >= 0; lastWriting--) { |
| lastWritingMask = 1 << lastWriting; |
| if ((lastWritingMask & m_writingMask) == lastWritingMask) |
| break; |
| } |
| if (lastWriting < 0) { |
| |
| |
| if (drawOnlyOnce) |
| m_enabledMask |= currentWritingMask; |
| else |
| m_enabledMask &= ~currentWritingMask; |
| } else { |
| tglMultColorMask(0, 0, 0, 0); |
| |
| |
| drawOnlyOnce = false; |
| currentWritingMask |= lastWritingMask; |
| } |
| } else |
| tglMultColorMask(0, 0, 0, 0); |
| |
| |
| glStencilMask(currentWritingMask); |
| |
| if (drawOnlyOnce) { |
| glStencilFunc(GL_EQUAL, m_inOrOutMask, m_enabledMask); |
| glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); |
| } else { |
| glStencilFunc(GL_EQUAL, currentWritingMask | m_inOrOutMask, m_enabledMask); |
| glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); |
| } |
| |
| } else { |
| glStencilMask(0xFFFFFFFF); |
| glStencilFunc(GL_EQUAL, m_inOrOutMask, m_enabledMask); |
| glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); |
| |
| } |
| |
| if (!m_enabledMask && m_currentWriting < 0) |
| glDisable(GL_STENCIL_TEST); |
| else |
| glEnable(GL_STENCIL_TEST); |
| } |
| |
| |
| |
| void TStencilControl::Imp::pushMask() |
| { |
| m_pushCount++; |
| } |
| |
| |
| |
| void TStencilControl::Imp::beginMask(DrawMode drawMode) |
| { |
| m_currentWriting = m_pushCount - 1; |
| unsigned char currentMask = 1 << m_currentWriting; |
| |
| m_writingMask |= currentMask; |
| |
| if (drawMode == DRAW_ALSO_ON_SCREEN) { |
| m_drawOnScreenMask |= currentMask; |
| } else if (drawMode == DRAW_ON_SCREEN_ONLY_ONCE) { |
| m_drawOnScreenMask |= currentMask; |
| m_drawOnlyOnceMask |= currentMask; |
| } else { |
| m_drawOnScreenMask &= ~currentMask; |
| m_drawOnlyOnceMask &= ~currentMask; |
| } |
| |
| glEnable(GL_STENCIL_TEST); |
| glStencilMask(currentMask); |
| glClear(GL_STENCIL_BUFFER_BIT); |
| updateOpenGlState(); |
| } |
| |
| |
| |
| void TStencilControl::Imp::endMask() |
| { |
| assert(m_pushCount - 1 == m_currentWriting); |
| |
| unsigned char currentMask = 1 << (m_pushCount - 1); |
| |
| m_writingMask &= ~currentMask; |
| m_enabledMask &= ~currentMask; |
| m_drawOnScreenMask &= ~currentMask; |
| m_drawOnlyOnceMask &= ~currentMask; |
| |
| |
| |
| m_currentWriting--; |
| for (; m_currentWriting >= 0; m_currentWriting--) { |
| unsigned char currentWritingMask = 1 << m_currentWriting; |
| if ((currentWritingMask & m_writingMask) == currentWritingMask) |
| break; |
| } |
| |
| updateOpenGlState(); |
| } |
| |
| |
| |
| void TStencilControl::Imp::enableMask(MaskType maskType) |
| { |
| unsigned char currentMask = 1 << (m_pushCount - 1); |
| |
| if ((m_enabledMask & currentMask) == 0) |
| glPushAttrib(GL_ALL_ATTRIB_BITS); |
| |
| m_enabledMask |= currentMask; |
| |
| assert(maskType == SHOW_INSIDE || maskType == SHOW_OUTSIDE); |
| |
| if (maskType == SHOW_INSIDE) |
| m_inOrOutMask |= currentMask; |
| else |
| m_inOrOutMask &= ~currentMask; |
| |
| updateOpenGlState(); |
| } |
| |
| |
| |
| void TStencilControl::Imp::disableMask() |
| { |
| unsigned char currentMask = 1 << (m_pushCount - 1); |
| |
| m_enabledMask &= ~currentMask; |
| m_inOrOutMask &= ~currentMask; |
| |
| updateOpenGlState(); |
| glPopAttrib(); |
| } |
| |
| |
| |
| void TStencilControl::Imp::popMask() |
| { |
| --m_pushCount; |
| assert(m_pushCount > 0); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void TStencilControl::beginMask(DrawMode drawMode) |
| { |
| glPushAttrib(GL_ALL_ATTRIB_BITS); |
| |
| if (m_imp->m_virtualState) |
| { |
| #ifdef _DEBUG |
| m_imp->fullState.push(m_imp->m_virtualState == 2); |
| #endif |
| |
| m_imp->pushMask(); |
| } |
| |
| m_imp->m_virtualState = 2; |
| |
| if (m_imp->m_pushCount <= m_imp->m_stencilBitCount) |
| m_imp->beginMask(drawMode); |
| } |
| |
| |
| |
| void TStencilControl::endMask() |
| { |
| if (!m_imp->m_virtualState) |
| { |
| m_imp->popMask(); |
| |
| #ifdef _DEBUG |
| assert(m_imp->fullState.top()); |
| m_imp->fullState.pop(); |
| #endif |
| } |
| |
| assert(m_imp->m_virtualState != 1); |
| |
| m_imp->m_virtualState = 0; |
| |
| if (m_imp->m_pushCount <= m_imp->m_stencilBitCount) |
| m_imp->endMask(); |
| |
| glPopAttrib(); |
| } |
| |
| |
| |
| void TStencilControl::enableMask(MaskType maskType) |
| { |
| assert(m_imp->m_virtualState != 2); |
| |
| m_imp->m_virtualState = 1; |
| |
| if (m_imp->m_pushCount <= m_imp->m_stencilBitCount) |
| m_imp->enableMask(maskType); |
| } |
| |
| |
| |
| void TStencilControl::disableMask() |
| { |
| assert(m_imp->m_virtualState != 2); |
| |
| if (!m_imp->m_virtualState) |
| { |
| m_imp->popMask(); |
| |
| #ifdef _DEBUG |
| assert(!m_imp->fullState.top()); |
| m_imp->fullState.pop(); |
| #endif |
| } |
| |
| m_imp->m_virtualState = 0; |
| |
| if (m_imp->m_pushCount <= m_imp->m_stencilBitCount) |
| m_imp->disableMask(); |
| } |
| |
| |
| |