diff options
Diffstat (limited to 'indra/llrender/llrender.cpp')
-rw-r--r-- | indra/llrender/llrender.cpp | 1378 |
1 files changed, 1378 insertions, 0 deletions
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp new file mode 100644 index 0000000000..e26acd53a3 --- /dev/null +++ b/indra/llrender/llrender.cpp @@ -0,0 +1,1378 @@ +/** + * @file llrender.cpp + * @brief LLRender implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library 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; + * version 2.1 of the License only. + * + * 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 GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llrender.h" + +#include "llvertexbuffer.h" +#include "llcubemap.h" +#include "llimagegl.h" +#include "llrendertarget.h" +#include "lltexture.h" + +LLRender gGL; + +// Handy copies of last good GL matrices +F64 gGLModelView[16]; +F64 gGLLastModelView[16]; +F64 gGLLastProjection[16]; +F64 gGLProjection[16]; +S32 gGLViewport[4]; + +U32 LLRender::sUICalls = 0; +U32 LLRender::sUIVerts = 0; + +static const U32 LL_NUM_TEXTURE_LAYERS = 16; + +static GLenum sGLTextureType[] = +{ + GL_TEXTURE_2D, + GL_TEXTURE_RECTANGLE_ARB, + GL_TEXTURE_CUBE_MAP_ARB +}; + +static GLint sGLAddressMode[] = +{ + GL_REPEAT, + GL_MIRRORED_REPEAT, + GL_CLAMP_TO_EDGE +}; + +static GLenum sGLCompareFunc[] = +{ + GL_NEVER, + GL_ALWAYS, + GL_LESS, + GL_LEQUAL, + GL_EQUAL, + GL_NOTEQUAL, + GL_GEQUAL, + GL_GREATER +}; + +const U32 immediate_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXCOORD0; + +static GLenum sGLBlendFactor[] = +{ + GL_ONE, + GL_ZERO, + GL_DST_COLOR, + GL_SRC_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_DST_ALPHA, + GL_SRC_ALPHA, + GL_ONE_MINUS_DST_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + + GL_ZERO // 'BF_UNDEF' +}; + +LLTexUnit::LLTexUnit(S32 index) +: mCurrTexType(TT_NONE), mCurrBlendType(TB_MULT), +mCurrColorOp(TBO_MULT), mCurrAlphaOp(TBO_MULT), +mCurrColorSrc1(TBS_TEX_COLOR), mCurrColorSrc2(TBS_PREV_COLOR), +mCurrAlphaSrc1(TBS_TEX_ALPHA), mCurrAlphaSrc2(TBS_PREV_ALPHA), +mCurrColorScale(1), mCurrAlphaScale(1), mCurrTexture(0), +mHasMipMaps(false) +{ + llassert_always(index < (S32)LL_NUM_TEXTURE_LAYERS); + mIndex = index; +} + +//static +U32 LLTexUnit::getInternalType(eTextureType type) +{ + return sGLTextureType[type]; +} + +void LLTexUnit::refreshState(void) +{ + // We set dirty to true so that the tex unit knows to ignore caching + // and we reset the cached tex unit state + + gGL.flush(); + + glActiveTextureARB(GL_TEXTURE0_ARB + mIndex); + if (mCurrTexType != TT_NONE) + { + glEnable(sGLTextureType[mCurrTexType]); + glBindTexture(sGLTextureType[mCurrTexType], mCurrTexture); + } + else + { + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + } + + if (mCurrBlendType != TB_COMBINE) + { + setTextureBlendType(mCurrBlendType); + } + else + { + setTextureCombiner(mCurrColorOp, mCurrColorSrc1, mCurrColorSrc2, false); + setTextureCombiner(mCurrAlphaOp, mCurrAlphaSrc1, mCurrAlphaSrc2, true); + } +} + +void LLTexUnit::activate(void) +{ + if (mIndex < 0) return; + + if ((S32)gGL.mCurrTextureUnitIndex != mIndex || gGL.mDirty) + { + gGL.flush(); + glActiveTextureARB(GL_TEXTURE0_ARB + mIndex); + gGL.mCurrTextureUnitIndex = mIndex; + } +} + +void LLTexUnit::enable(eTextureType type) +{ + if (mIndex < 0) return; + + if ( (mCurrTexType != type || gGL.mDirty) && (type != TT_NONE) ) + { + activate(); + if (mCurrTexType != TT_NONE && !gGL.mDirty) + { + disable(); // Force a disable of a previous texture type if it's enabled. + } + mCurrTexType = type; + + gGL.flush(); + glEnable(sGLTextureType[type]); + } +} + +void LLTexUnit::disable(void) +{ + if (mIndex < 0) return; + + if (mCurrTexType != TT_NONE) + { + activate(); + unbind(mCurrTexType); + gGL.flush(); + glDisable(sGLTextureType[mCurrTexType]); + mCurrTexType = TT_NONE; + } +} + +bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind) +{ + stop_glerror(); + if (mIndex < 0) return false; + + gGL.flush(); + + LLImageGL* gl_tex = NULL ; + if (texture == NULL || !(gl_tex = texture->getGLTexture())) + { + llwarns << "NULL LLTexUnit::bind texture" << llendl; + return false; + } + + if (!gl_tex->getTexName()) //if texture does not exist + { + //if deleted, will re-generate it immediately + texture->forceImmediateUpdate() ; + + gl_tex->forceUpdateBindStats() ; + return texture->bindDefaultImage(mIndex); + } + + //in audit, replace the selected texture by the default one. + if(gAuditTexture && for_rendering && LLImageGL::sCurTexPickSize > 0) + { + if(texture->getWidth() * texture->getHeight() == LLImageGL::sCurTexPickSize) + { + gl_tex->updateBindStats(gl_tex->mTextureMemory); + return bind(LLImageGL::sHighlightTexturep.get()); + } + } + if ((mCurrTexture != gl_tex->getTexName()) || forceBind) + { + activate(); + enable(gl_tex->getTarget()); + mCurrTexture = gl_tex->getTexName(); + glBindTexture(sGLTextureType[gl_tex->getTarget()], mCurrTexture); + if(gl_tex->updateBindStats(gl_tex->mTextureMemory)) + { + texture->setActive() ; + texture->updateBindStatsForTester() ; + } + mHasMipMaps = gl_tex->mHasMipMaps; + if (gl_tex->mTexOptionsDirty) + { + gl_tex->mTexOptionsDirty = false; + setTextureAddressMode(gl_tex->mAddressMode); + setTextureFilteringOption(gl_tex->mFilterOption); + } + } + return true; +} + +bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind) +{ + stop_glerror(); + if (mIndex < 0) return false; + + if(!texture) + { + llwarns << "NULL LLTexUnit::bind texture" << llendl; + return false; + } + + if(!texture->getTexName()) + { + if(LLImageGL::sDefaultGLTexture && LLImageGL::sDefaultGLTexture->getTexName()) + { + return bind(LLImageGL::sDefaultGLTexture) ; + } + return false ; + } + + if ((mCurrTexture != texture->getTexName()) || forceBind) + { + gGL.flush(); + activate(); + enable(texture->getTarget()); + mCurrTexture = texture->getTexName(); + glBindTexture(sGLTextureType[texture->getTarget()], mCurrTexture); + texture->updateBindStats(texture->mTextureMemory); + mHasMipMaps = texture->mHasMipMaps; + if (texture->mTexOptionsDirty) + { + texture->mTexOptionsDirty = false; + setTextureAddressMode(texture->mAddressMode); + setTextureFilteringOption(texture->mFilterOption); + } + } + + return true; +} + +bool LLTexUnit::bind(LLCubeMap* cubeMap) +{ + if (mIndex < 0) return false; + + gGL.flush(); + + if (cubeMap == NULL) + { + llwarns << "NULL LLTexUnit::bind cubemap" << llendl; + return false; + } + + if (mCurrTexture != cubeMap->mImages[0]->getTexName()) + { + if (gGLManager.mHasCubeMap && LLCubeMap::sUseCubeMaps) + { + activate(); + enable(LLTexUnit::TT_CUBE_MAP); + mCurrTexture = cubeMap->mImages[0]->getTexName(); + glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, mCurrTexture); + mHasMipMaps = cubeMap->mImages[0]->mHasMipMaps; + cubeMap->mImages[0]->updateBindStats(cubeMap->mImages[0]->mTextureMemory); + if (cubeMap->mImages[0]->mTexOptionsDirty) + { + cubeMap->mImages[0]->mTexOptionsDirty = false; + setTextureAddressMode(cubeMap->mImages[0]->mAddressMode); + setTextureFilteringOption(cubeMap->mImages[0]->mFilterOption); + } + return true; + } + else + { + llwarns << "Using cube map without extension!" << llendl; + return false; + } + } + return true; +} + +// LLRenderTarget is unavailible on the mapserver since it uses FBOs. +#if !LL_MESA_HEADLESS +bool LLTexUnit::bind(LLRenderTarget* renderTarget, bool bindDepth) +{ + if (mIndex < 0) return false; + + gGL.flush(); + + if (bindDepth) + { + if (renderTarget->hasStencil()) + { + llerrs << "Cannot bind a render buffer for sampling. Allocate render target without a stencil buffer if sampling of depth buffer is required." << llendl; + } + + bindManual(renderTarget->getUsage(), renderTarget->getDepth()); + } + else + { + bindManual(renderTarget->getUsage(), renderTarget->getTexture()); + } + + return true; +} +#endif // LL_MESA_HEADLESS + +bool LLTexUnit::bindManual(eTextureType type, U32 texture, bool hasMips) +{ + if (mIndex < 0) + { + return false; + } + + if(mCurrTexture != texture) + { + gGL.flush(); + + activate(); + enable(type); + mCurrTexture = texture; + glBindTexture(sGLTextureType[type], texture); + mHasMipMaps = hasMips; + } + return true; +} + +void LLTexUnit::unbind(eTextureType type) +{ + stop_glerror(); + + if (mIndex < 0) return; + + // Disabled caching of binding state. + if (mCurrTexType == type) + { + gGL.flush(); + + activate(); + mCurrTexture = 0; + glBindTexture(sGLTextureType[type], 0); + } +} + +void LLTexUnit::setTextureAddressMode(eTextureAddressMode mode) +{ + if (mIndex < 0 || mCurrTexture == 0) return; + + gGL.flush(); + + activate(); + + glTexParameteri (sGLTextureType[mCurrTexType], GL_TEXTURE_WRAP_S, sGLAddressMode[mode]); + glTexParameteri (sGLTextureType[mCurrTexType], GL_TEXTURE_WRAP_T, sGLAddressMode[mode]); + if (mCurrTexType == TT_CUBE_MAP) + { + glTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, sGLAddressMode[mode]); + } +} + +void LLTexUnit::setTextureFilteringOption(LLTexUnit::eTextureFilterOptions option) +{ + if (mIndex < 0 || mCurrTexture == 0) return; + + gGL.flush(); + + if (option == TFO_POINT) + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + else + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + if (option >= TFO_TRILINEAR && mHasMipMaps) + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } + else if (option >= TFO_BILINEAR) + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + if (gGLManager.mHasAnisotropic) + { + if (LLImageGL::sGlobalUseAnisotropic && option == TFO_ANISOTROPIC) + { + if (gGL.mMaxAnisotropy < 1.f) + { + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gGL.mMaxAnisotropy); + } + glTexParameterf(sGLTextureType[mCurrTexType], GL_TEXTURE_MAX_ANISOTROPY_EXT, gGL.mMaxAnisotropy); + } + else + { + glTexParameterf(sGLTextureType[mCurrTexType], GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f); + } + } +} + +void LLTexUnit::setTextureBlendType(eTextureBlendType type) +{ + if (mIndex < 0) return; + + // Do nothing if it's already correctly set. + if (mCurrBlendType == type && !gGL.mDirty) + { + return; + } + + gGL.flush(); + + activate(); + mCurrBlendType = type; + S32 scale_amount = 1; + switch (type) + { + case TB_REPLACE: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + break; + case TB_ADD: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); + break; + case TB_MULT: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + break; + case TB_MULT_X2: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + scale_amount = 2; + break; + case TB_ALPHA_BLEND: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + break; + case TB_COMBINE: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + break; + default: + llerrs << "Unknown Texture Blend Type: " << type << llendl; + break; + } + setColorScale(scale_amount); + setAlphaScale(1); +} + +GLint LLTexUnit::getTextureSource(eTextureBlendSrc src) +{ + switch(src) + { + // All four cases should return the same value. + case TBS_PREV_COLOR: + case TBS_PREV_ALPHA: + case TBS_ONE_MINUS_PREV_COLOR: + case TBS_ONE_MINUS_PREV_ALPHA: + return GL_PREVIOUS_ARB; + + // All four cases should return the same value. + case TBS_TEX_COLOR: + case TBS_TEX_ALPHA: + case TBS_ONE_MINUS_TEX_COLOR: + case TBS_ONE_MINUS_TEX_ALPHA: + return GL_TEXTURE; + + // All four cases should return the same value. + case TBS_VERT_COLOR: + case TBS_VERT_ALPHA: + case TBS_ONE_MINUS_VERT_COLOR: + case TBS_ONE_MINUS_VERT_ALPHA: + return GL_PRIMARY_COLOR_ARB; + + // All four cases should return the same value. + case TBS_CONST_COLOR: + case TBS_CONST_ALPHA: + case TBS_ONE_MINUS_CONST_COLOR: + case TBS_ONE_MINUS_CONST_ALPHA: + return GL_CONSTANT_ARB; + + default: + llwarns << "Unknown eTextureBlendSrc: " << src << ". Using Vertex Color instead." << llendl; + return GL_PRIMARY_COLOR_ARB; + } +} + +GLint LLTexUnit::getTextureSourceType(eTextureBlendSrc src, bool isAlpha) +{ + switch(src) + { + // All four cases should return the same value. + case TBS_PREV_COLOR: + case TBS_TEX_COLOR: + case TBS_VERT_COLOR: + case TBS_CONST_COLOR: + return (isAlpha) ? GL_SRC_ALPHA: GL_SRC_COLOR; + + // All four cases should return the same value. + case TBS_PREV_ALPHA: + case TBS_TEX_ALPHA: + case TBS_VERT_ALPHA: + case TBS_CONST_ALPHA: + return GL_SRC_ALPHA; + + // All four cases should return the same value. + case TBS_ONE_MINUS_PREV_COLOR: + case TBS_ONE_MINUS_TEX_COLOR: + case TBS_ONE_MINUS_VERT_COLOR: + case TBS_ONE_MINUS_CONST_COLOR: + return (isAlpha) ? GL_ONE_MINUS_SRC_ALPHA : GL_ONE_MINUS_SRC_COLOR; + + // All four cases should return the same value. + case TBS_ONE_MINUS_PREV_ALPHA: + case TBS_ONE_MINUS_TEX_ALPHA: + case TBS_ONE_MINUS_VERT_ALPHA: + case TBS_ONE_MINUS_CONST_ALPHA: + return GL_ONE_MINUS_SRC_ALPHA; + + default: + llwarns << "Unknown eTextureBlendSrc: " << src << ". Using Source Color or Alpha instead." << llendl; + return (isAlpha) ? GL_SRC_ALPHA: GL_SRC_COLOR; + } +} + +void LLTexUnit::setTextureCombiner(eTextureBlendOp op, eTextureBlendSrc src1, eTextureBlendSrc src2, bool isAlpha) +{ + if (mIndex < 0) return; + + activate(); + if (mCurrBlendType != TB_COMBINE || gGL.mDirty) + { + mCurrBlendType = TB_COMBINE; + gGL.flush(); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + } + + // We want an early out, because this function does a LOT of stuff. + if ( ( (isAlpha && (mCurrAlphaOp == op) && (mCurrAlphaSrc1 == src1) && (mCurrAlphaSrc2 == src2)) + || (!isAlpha && (mCurrColorOp == op) && (mCurrColorSrc1 == src1) && (mCurrColorSrc2 == src2)) ) && !gGL.mDirty) + { + return; + } + + gGL.flush(); + + // Get the gl source enums according to the eTextureBlendSrc sources passed in + GLint source1 = getTextureSource(src1); + GLint source2 = getTextureSource(src2); + // Get the gl operand enums according to the eTextureBlendSrc sources passed in + GLint operand1 = getTextureSourceType(src1, isAlpha); + GLint operand2 = getTextureSourceType(src2, isAlpha); + // Default the scale amount to 1 + S32 scale_amount = 1; + GLenum comb_enum, src0_enum, src1_enum, src2_enum, operand0_enum, operand1_enum, operand2_enum; + + if (isAlpha) + { + // Set enums to ALPHA ones + comb_enum = GL_COMBINE_ALPHA_ARB; + src0_enum = GL_SOURCE0_ALPHA_ARB; + src1_enum = GL_SOURCE1_ALPHA_ARB; + src2_enum = GL_SOURCE2_ALPHA_ARB; + operand0_enum = GL_OPERAND0_ALPHA_ARB; + operand1_enum = GL_OPERAND1_ALPHA_ARB; + operand2_enum = GL_OPERAND2_ALPHA_ARB; + + // cache current combiner + mCurrAlphaOp = op; + mCurrAlphaSrc1 = src1; + mCurrAlphaSrc2 = src2; + } + else + { + // Set enums to RGB ones + comb_enum = GL_COMBINE_RGB_ARB; + src0_enum = GL_SOURCE0_RGB_ARB; + src1_enum = GL_SOURCE1_RGB_ARB; + src2_enum = GL_SOURCE2_RGB_ARB; + operand0_enum = GL_OPERAND0_RGB_ARB; + operand1_enum = GL_OPERAND1_RGB_ARB; + operand2_enum = GL_OPERAND2_RGB_ARB; + + // cache current combiner + mCurrColorOp = op; + mCurrColorSrc1 = src1; + mCurrColorSrc2 = src2; + } + + switch(op) + { + case TBO_REPLACE: + // Slightly special syntax (no second sources), just set all and return. + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, src0_enum, source1); + glTexEnvi(GL_TEXTURE_ENV, operand0_enum, operand1); + (isAlpha) ? setAlphaScale(1) : setColorScale(1); + return; + + case TBO_MULT: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_MODULATE); + break; + + case TBO_MULT_X2: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_MODULATE); + scale_amount = 2; + break; + + case TBO_MULT_X4: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_MODULATE); + scale_amount = 4; + break; + + case TBO_ADD: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_ADD); + break; + + case TBO_ADD_SIGNED: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_ADD_SIGNED_ARB); + break; + + case TBO_SUBTRACT: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_SUBTRACT_ARB); + break; + + case TBO_LERP_VERT_ALPHA: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_PRIMARY_COLOR_ARB); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, GL_SRC_ALPHA); + break; + + case TBO_LERP_TEX_ALPHA: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, GL_SRC_ALPHA); + break; + + case TBO_LERP_PREV_ALPHA: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, GL_SRC_ALPHA); + break; + + case TBO_LERP_CONST_ALPHA: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_CONSTANT_ARB); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, GL_SRC_ALPHA); + break; + + case TBO_LERP_VERT_COLOR: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_PRIMARY_COLOR_ARB); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, (isAlpha) ? GL_SRC_ALPHA : GL_SRC_COLOR); + break; + + default: + llwarns << "Unknown eTextureBlendOp: " << op << ". Setting op to replace." << llendl; + // Slightly special syntax (no second sources), just set all and return. + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, src0_enum, source1); + glTexEnvi(GL_TEXTURE_ENV, operand0_enum, operand1); + (isAlpha) ? setAlphaScale(1) : setColorScale(1); + return; + } + + // Set sources, operands, and scale accordingly + glTexEnvi(GL_TEXTURE_ENV, src0_enum, source1); + glTexEnvi(GL_TEXTURE_ENV, operand0_enum, operand1); + glTexEnvi(GL_TEXTURE_ENV, src1_enum, source2); + glTexEnvi(GL_TEXTURE_ENV, operand1_enum, operand2); + (isAlpha) ? setAlphaScale(scale_amount) : setColorScale(scale_amount); +} + +void LLTexUnit::setColorScale(S32 scale) +{ + if (mCurrColorScale != scale || gGL.mDirty) + { + mCurrColorScale = scale; + gGL.flush(); + glTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE, scale ); + } +} + +void LLTexUnit::setAlphaScale(S32 scale) +{ + if (mCurrAlphaScale != scale || gGL.mDirty) + { + mCurrAlphaScale = scale; + gGL.flush(); + glTexEnvi( GL_TEXTURE_ENV, GL_ALPHA_SCALE, scale ); + } +} + +// Useful for debugging that you've manually assigned a texture operation to the correct +// texture unit based on the currently set active texture in opengl. +void LLTexUnit::debugTextureUnit(void) +{ + if (mIndex < 0) return; + + GLint activeTexture; + glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &activeTexture); + if ((GL_TEXTURE0_ARB + mIndex) != activeTexture) + { + U32 set_unit = (activeTexture - GL_TEXTURE0_ARB); + llwarns << "Incorrect Texture Unit! Expected: " << set_unit << " Actual: " << mIndex << llendl; + } +} + + +LLRender::LLRender() + : mDirty(false), + mCount(0), + mMode(LLRender::TRIANGLES), + mCurrTextureUnitIndex(0), + mMaxAnisotropy(0.f) +{ + mBuffer = new LLVertexBuffer(immediate_mask, 0); + mBuffer->allocateBuffer(4096, 0, TRUE); + mBuffer->getVertexStrider(mVerticesp); + mBuffer->getTexCoord0Strider(mTexcoordsp); + mBuffer->getColorStrider(mColorsp); + + mTexUnits.reserve(LL_NUM_TEXTURE_LAYERS); + for (U32 i = 0; i < LL_NUM_TEXTURE_LAYERS; i++) + { + mTexUnits.push_back(new LLTexUnit(i)); + } + mDummyTexUnit = new LLTexUnit(-1); + + for (U32 i = 0; i < 4; i++) + { + mCurrColorMask[i] = true; + } + + mCurrAlphaFunc = CF_DEFAULT; + mCurrAlphaFuncVal = 0.01f; + mCurrBlendColorSFactor = BF_UNDEF; + mCurrBlendAlphaSFactor = BF_UNDEF; + mCurrBlendColorDFactor = BF_UNDEF; + mCurrBlendAlphaDFactor = BF_UNDEF; +} + +LLRender::~LLRender() +{ + shutdown(); +} + +void LLRender::shutdown() +{ + for (U32 i = 0; i < mTexUnits.size(); i++) + { + delete mTexUnits[i]; + } + mTexUnits.clear(); + delete mDummyTexUnit; + mDummyTexUnit = NULL; +} + +void LLRender::refreshState(void) +{ + mDirty = true; + + U32 active_unit = mCurrTextureUnitIndex; + + for (U32 i = 0; i < mTexUnits.size(); i++) + { + mTexUnits[i]->refreshState(); + } + + mTexUnits[active_unit]->activate(); + + setColorMask(mCurrColorMask[0], mCurrColorMask[1], mCurrColorMask[2], mCurrColorMask[3]); + + setAlphaRejectSettings(mCurrAlphaFunc, mCurrAlphaFuncVal); + + mDirty = false; +} + +void LLRender::translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z) +{ + flush(); + glTranslatef(x,y,z); +} + +void LLRender::scalef(const GLfloat& x, const GLfloat& y, const GLfloat& z) +{ + flush(); + glScalef(x,y,z); +} + +void LLRender::pushMatrix() +{ + flush(); + glPushMatrix(); +} + +void LLRender::popMatrix() +{ + flush(); + glPopMatrix(); +} + +void LLRender::translateUI(F32 x, F32 y, F32 z) +{ + if (mUIOffset.empty()) + { + llerrs << "Need to push a UI translation frame before offsetting" << llendl; + } + + mUIOffset.back().mV[0] += x; + mUIOffset.back().mV[1] += y; + mUIOffset.back().mV[2] += z; +} + +void LLRender::scaleUI(F32 x, F32 y, F32 z) +{ + if (mUIScale.empty()) + { + llerrs << "Need to push a UI transformation frame before scaling." << llendl; + } + + mUIScale.back().scaleVec(LLVector3(x,y,z)); +} + +void LLRender::pushUIMatrix() +{ + if (mUIOffset.empty()) + { + mUIOffset.push_back(LLVector3(0,0,0)); + } + else + { + mUIOffset.push_back(mUIOffset.back()); + } + + if (mUIScale.empty()) + { + mUIScale.push_back(LLVector3(1,1,1)); + } + else + { + mUIScale.push_back(mUIScale.back()); + } +} + +void LLRender::popUIMatrix() +{ + if (mUIOffset.empty()) + { + llerrs << "UI offset stack blown." << llendl; + } + mUIOffset.pop_back(); + mUIScale.pop_back(); +} + +LLVector3 LLRender::getUITranslation() +{ + if (mUIOffset.empty()) + { + return LLVector3::zero; + } + return mUIOffset.back(); +} + +LLVector3 LLRender::getUIScale() +{ + if (mUIScale.empty()) + { + return LLVector3(1.f, 1.f, 1.f); + } + return mUIScale.back(); +} + + +void LLRender::loadUIIdentity() +{ + if (mUIOffset.empty()) + { + llerrs << "Need to push UI translation frame before clearing offset." << llendl; + } + mUIOffset.back().setVec(0,0,0); + mUIScale.back().setVec(1,1,1); +} + +void LLRender::setColorMask(bool writeColor, bool writeAlpha) +{ + setColorMask(writeColor, writeColor, writeColor, writeAlpha); +} + +void LLRender::setColorMask(bool writeColorR, bool writeColorG, bool writeColorB, bool writeAlpha) +{ + flush(); + + mCurrColorMask[0] = writeColorR; + mCurrColorMask[1] = writeColorG; + mCurrColorMask[2] = writeColorB; + mCurrColorMask[3] = writeAlpha; + + glColorMask(writeColorR ? GL_TRUE : GL_FALSE, + writeColorG ? GL_TRUE : GL_FALSE, + writeColorB ? GL_TRUE : GL_FALSE, + writeAlpha ? GL_TRUE : GL_FALSE); +} + +void LLRender::setSceneBlendType(eBlendType type) +{ + switch (type) + { + case BT_ALPHA: + blendFunc(BF_SOURCE_ALPHA, BF_ONE_MINUS_SOURCE_ALPHA); + break; + case BT_ADD: + blendFunc(BF_ONE, BF_ONE); + break; + case BT_ADD_WITH_ALPHA: + blendFunc(BF_SOURCE_ALPHA, BF_ONE); + break; + case BT_MULT: + blendFunc(BF_DEST_COLOR, BF_ZERO); + break; + case BT_MULT_ALPHA: + blendFunc(BF_DEST_ALPHA, BF_ZERO); + break; + case BT_MULT_X2: + blendFunc(BF_DEST_COLOR, BF_SOURCE_COLOR); + break; + case BT_REPLACE: + blendFunc(BF_ONE, BF_ZERO); + break; + default: + llerrs << "Unknown Scene Blend Type: " << type << llendl; + break; + } +} + +void LLRender::setAlphaRejectSettings(eCompareFunc func, F32 value) +{ + flush(); + + mCurrAlphaFunc = func; + mCurrAlphaFuncVal = value; + if (func == CF_DEFAULT) + { + glAlphaFunc(GL_GREATER, 0.01f); + } + else + { + glAlphaFunc(sGLCompareFunc[func], value); + } +} + +void LLRender::blendFunc(eBlendFactor sfactor, eBlendFactor dfactor) +{ + llassert(sfactor < BF_UNDEF); + llassert(dfactor < BF_UNDEF); + if (mCurrBlendColorSFactor != sfactor || mCurrBlendColorDFactor != dfactor || + mCurrBlendAlphaSFactor != sfactor || mCurrBlendAlphaDFactor != dfactor) + { + mCurrBlendColorSFactor = sfactor; + mCurrBlendAlphaSFactor = sfactor; + mCurrBlendColorDFactor = dfactor; + mCurrBlendAlphaDFactor = dfactor; + flush(); + glBlendFunc(sGLBlendFactor[sfactor], sGLBlendFactor[dfactor]); + } +} + +void LLRender::blendFunc(eBlendFactor color_sfactor, eBlendFactor color_dfactor, + eBlendFactor alpha_sfactor, eBlendFactor alpha_dfactor) +{ + llassert(color_sfactor < BF_UNDEF); + llassert(color_dfactor < BF_UNDEF); + llassert(alpha_sfactor < BF_UNDEF); + llassert(alpha_dfactor < BF_UNDEF); + if (!gGLManager.mHasBlendFuncSeparate) + { + LL_WARNS_ONCE("render") << "no glBlendFuncSeparateEXT(), using color-only blend func" << llendl; + blendFunc(color_sfactor, color_dfactor); + return; + } + if (mCurrBlendColorSFactor != color_sfactor || mCurrBlendColorDFactor != color_dfactor || + mCurrBlendAlphaSFactor != alpha_sfactor || mCurrBlendAlphaDFactor != alpha_dfactor) + { + mCurrBlendColorSFactor = color_sfactor; + mCurrBlendAlphaSFactor = alpha_sfactor; + mCurrBlendColorDFactor = color_dfactor; + mCurrBlendAlphaDFactor = alpha_dfactor; + flush(); + glBlendFuncSeparateEXT(sGLBlendFactor[color_sfactor], sGLBlendFactor[color_dfactor], + sGLBlendFactor[alpha_sfactor], sGLBlendFactor[alpha_dfactor]); + } +} + +LLTexUnit* LLRender::getTexUnit(U32 index) +{ + if (index < mTexUnits.size()) + { + return mTexUnits[index]; + } + else + { + lldebugs << "Non-existing texture unit layer requested: " << index << llendl; + return mDummyTexUnit; + } +} + +bool LLRender::verifyTexUnitActive(U32 unitToVerify) +{ + if (mCurrTextureUnitIndex == unitToVerify) + { + return true; + } + else + { + llwarns << "TexUnit currently active: " << mCurrTextureUnitIndex << " (expecting " << unitToVerify << ")" << llendl; + return false; + } +} + +void LLRender::clearErrors() +{ + while (glGetError()) + { + //loop until no more error flags left + } +} + +void LLRender::begin(const GLuint& mode) +{ + if (mode != mMode) + { + if (mMode == LLRender::QUADS || + mMode == LLRender::LINES || + mMode == LLRender::TRIANGLES || + mMode == LLRender::POINTS) + { + flush(); + } + else if (mCount != 0) + { + llerrs << "gGL.begin() called redundantly." << llendl; + } + + mMode = mode; + } +} + +void LLRender::end() +{ + if (mCount == 0) + { + return; + //IMM_ERRS << "GL begin and end called with no vertices specified." << llendl; + } + + if ((mMode != LLRender::QUADS && + mMode != LLRender::LINES && + mMode != LLRender::TRIANGLES && + mMode != LLRender::POINTS) || + mCount > 2048) + { + flush(); + } +} +void LLRender::flush() +{ + if (mCount > 0) + { +#if 0 + if (!glIsEnabled(GL_VERTEX_ARRAY)) + { + llerrs << "foo 1" << llendl; + } + + if (!glIsEnabled(GL_COLOR_ARRAY)) + { + llerrs << "foo 2" << llendl; + } + + if (!glIsEnabled(GL_TEXTURE_COORD_ARRAY)) + { + llerrs << "foo 3" << llendl; + } + + if (glIsEnabled(GL_NORMAL_ARRAY)) + { + llerrs << "foo 7" << llendl; + } + + GLvoid* pointer; + + glGetPointerv(GL_VERTEX_ARRAY_POINTER, &pointer); + if (pointer != &(mBuffer[0].v)) + { + llerrs << "foo 4" << llendl; + } + + glGetPointerv(GL_COLOR_ARRAY_POINTER, &pointer); + if (pointer != &(mBuffer[0].c)) + { + llerrs << "foo 5" << llendl; + } + + glGetPointerv(GL_TEXTURE_COORD_ARRAY_POINTER, &pointer); + if (pointer != &(mBuffer[0].uv)) + { + llerrs << "foo 6" << llendl; + } +#endif + + if (!mUIOffset.empty()) + { + sUICalls++; + sUIVerts += mCount; + } + + if (gDebugGL) + { + if (mMode == LLRender::QUADS) + { + if (mCount%4 != 0) + { + llerrs << "Incomplete quad rendered." << llendl; + } + } + + if (mMode == LLRender::TRIANGLES) + { + if (mCount%3 != 0) + { + llerrs << "Incomplete triangle rendered." << llendl; + } + } + + if (mMode == LLRender::LINES) + { + if (mCount%2 != 0) + { + llerrs << "Incomplete line rendered." << llendl; + } + } + } + + mBuffer->setBuffer(immediate_mask); + mBuffer->drawArrays(mMode, 0, mCount); + + mVerticesp[0] = mVerticesp[mCount]; + mTexcoordsp[0] = mTexcoordsp[mCount]; + mColorsp[0] = mColorsp[mCount]; + mCount = 0; + } +} + +void LLRender::vertex3f(const GLfloat& x, const GLfloat& y, const GLfloat& z) +{ + //the range of mVerticesp, mColorsp and mTexcoordsp is [0, 4095] + if (mCount > 4094) + { + // llwarns << "GL immediate mode overflow. Some geometry not drawn." << llendl; + return; + } + + if (mUIOffset.empty()) + { + mVerticesp[mCount] = LLVector3(x,y,z); + } + else + { + LLVector3 vert = (LLVector3(x,y,z)+mUIOffset.back()).scaledVec(mUIScale.back()); + mVerticesp[mCount] = vert; + } + + mCount++; + mVerticesp[mCount] = mVerticesp[mCount-1]; + mColorsp[mCount] = mColorsp[mCount-1]; + mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; +} + +void LLRender::vertexBatchPreTransformed(LLVector3* verts, S32 vert_count) +{ + if (mCount + vert_count > 4094) + { + // llwarns << "GL immediate mode overflow. Some geometry not drawn." << llendl; + return; + } + + for (S32 i = 0; i < vert_count; i++) + { + mVerticesp[mCount] = verts[i]; + + mCount++; + mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; + mColorsp[mCount] = mColorsp[mCount-1]; + } + + mVerticesp[mCount] = mVerticesp[mCount-1]; +} + +void LLRender::vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, S32 vert_count) +{ + if (mCount + vert_count > 4094) + { + // llwarns << "GL immediate mode overflow. Some geometry not drawn." << llendl; + return; + } + + for (S32 i = 0; i < vert_count; i++) + { + mVerticesp[mCount] = verts[i]; + mTexcoordsp[mCount] = uvs[i]; + + mCount++; + mColorsp[mCount] = mColorsp[mCount-1]; + } + + mVerticesp[mCount] = mVerticesp[mCount-1]; + mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; +} + +void LLRender::vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, LLColor4U* colors, S32 vert_count) +{ + if (mCount + vert_count > 4094) + { + // llwarns << "GL immediate mode overflow. Some geometry not drawn." << llendl; + return; + } + + for (S32 i = 0; i < vert_count; i++) + { + mVerticesp[mCount] = verts[i]; + mTexcoordsp[mCount] = uvs[i]; + mColorsp[mCount] = colors[i]; + + mCount++; + } + + mVerticesp[mCount] = mVerticesp[mCount-1]; + mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; + mColorsp[mCount] = mColorsp[mCount-1]; +} + +void LLRender::vertex2i(const GLint& x, const GLint& y) +{ + vertex3f((GLfloat) x, (GLfloat) y, 0); +} + +void LLRender::vertex2f(const GLfloat& x, const GLfloat& y) +{ + vertex3f(x,y,0); +} + +void LLRender::vertex2fv(const GLfloat* v) +{ + vertex3f(v[0], v[1], 0); +} + +void LLRender::vertex3fv(const GLfloat* v) +{ + vertex3f(v[0], v[1], v[2]); +} + +void LLRender::texCoord2f(const GLfloat& x, const GLfloat& y) +{ + mTexcoordsp[mCount] = LLVector2(x,y); +} + +void LLRender::texCoord2i(const GLint& x, const GLint& y) +{ + texCoord2f((GLfloat) x, (GLfloat) y); +} + +void LLRender::texCoord2fv(const GLfloat* tc) +{ + texCoord2f(tc[0], tc[1]); +} + +void LLRender::color4ub(const GLubyte& r, const GLubyte& g, const GLubyte& b, const GLubyte& a) +{ + mColorsp[mCount] = LLColor4U(r,g,b,a); +} +void LLRender::color4ubv(const GLubyte* c) +{ + color4ub(c[0], c[1], c[2], c[3]); +} + +void LLRender::color4f(const GLfloat& r, const GLfloat& g, const GLfloat& b, const GLfloat& a) +{ + color4ub((GLubyte) (llclamp(r, 0.f, 1.f)*255), + (GLubyte) (llclamp(g, 0.f, 1.f)*255), + (GLubyte) (llclamp(b, 0.f, 1.f)*255), + (GLubyte) (llclamp(a, 0.f, 1.f)*255)); +} + +void LLRender::color4fv(const GLfloat* c) +{ + color4f(c[0],c[1],c[2],c[3]); +} + +void LLRender::color3f(const GLfloat& r, const GLfloat& g, const GLfloat& b) +{ + color4f(r,g,b,1); +} + +void LLRender::color3fv(const GLfloat* c) +{ + color4f(c[0],c[1],c[2],1); +} + +void LLRender::debugTexUnits(void) +{ + LL_INFOS("TextureUnit") << "Active TexUnit: " << mCurrTextureUnitIndex << LL_ENDL; + std::string active_enabled = "false"; + for (U32 i = 0; i < mTexUnits.size(); i++) + { + if (getTexUnit(i)->mCurrTexType != LLTexUnit::TT_NONE) + { + if (i == mCurrTextureUnitIndex) active_enabled = "true"; + LL_INFOS("TextureUnit") << "TexUnit: " << i << " Enabled" << LL_ENDL; + LL_INFOS("TextureUnit") << "Enabled As: " ; + switch (getTexUnit(i)->mCurrTexType) + { + case LLTexUnit::TT_TEXTURE: + LL_CONT << "Texture 2D"; + break; + case LLTexUnit::TT_RECT_TEXTURE: + LL_CONT << "Texture Rectangle"; + break; + case LLTexUnit::TT_CUBE_MAP: + LL_CONT << "Cube Map"; + break; + default: + LL_CONT << "ARGH!!! NONE!"; + break; + } + LL_CONT << ", Texture Bound: " << getTexUnit(i)->mCurrTexture << LL_ENDL; + } + } + LL_INFOS("TextureUnit") << "Active TexUnit Enabled : " << active_enabled << LL_ENDL; +} + |