summaryrefslogtreecommitdiff
path: root/indra/llrender/llrender.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llrender/llrender.cpp')
-rw-r--r--indra/llrender/llrender.cpp1378
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;
+}
+