/** * @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 "llglslshader.h" #include "llimagegl.h" #include "llrendertarget.h" #include "lltexture.h" #include "llshadermgr.h" #include "llmatrix4a.h" LLRender gGL; // Handy copies of last good GL matrices //Would be best to migrate these to LLMatrix4a and LLVector4a, but that's too divergent right now. LLMatrix4a gGLModelView; LLMatrix4a gGLLastModelView; LLMatrix4a gGLPreviousModelView; LLMatrix4a gGLLastProjection; LLMatrix4a gGLProjection; LLRect gGLViewport; U32 LLRender::sUICalls = 0; U32 LLRender::sUIVerts = 0; U32 LLTexUnit::sWhiteTexture = 0; bool LLRender::sGLCoreProfile = false; static const U32 LL_NUM_TEXTURE_LAYERS = 32; static const GLenum sGLTextureType[] = { GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP_ARB }; static const GLint sGLAddressMode[] = { GL_REPEAT, GL_MIRRORED_REPEAT, GL_CLAMP_TO_EDGE }; static const 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 const 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' }; static const GLenum sGLPolygonFaceType[] = { GL_FRONT, GL_BACK, GL_FRONT_AND_BACK }; static const GLenum sGLPolygonMode[] = { GL_POINT, GL_LINE, GL_FILL }; 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), mIndex(index) { llassert_always(index < (S32)LL_NUM_TEXTURE_LAYERS); } //static U32 LLTexUnit::getInternalType(eTextureType type) { return sGLTextureType[type]; } //void validate_bind_texture(U32 name); 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); // // Per apple spec, don't call glEnable/glDisable when index exceeds max texture units // http://www.mailinglistarchive.com/html/mac-opengl@lists.apple.com/2008-07/msg00653.html // bool enableDisable = !LLGLSLShader::sNoFixedFunction && (mIndex < gGLManager.mNumTextureUnits) /*&& mCurrTexType != LLTexUnit::TT_MULTISAMPLE_TEXTURE*/; if (mCurrTexType != TT_NONE) { if (enableDisable) { glEnable(sGLTextureType[mCurrTexType]); } //if (mCurrTexture) validate_bind_texture(mCurrTexture); glBindTexture(sGLTextureType[mCurrTexType], mCurrTexture); } else { if (enableDisable) { 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.getCurrentTexUnitIndex() != mIndex || gGL.mDirty) { //gGL.flush(); // Apply immediately. glActiveTextureARB(GL_TEXTURE0_ARB + mIndex); gGL.mContext.texUnit = gGL.mNewContext.texUnit = mIndex; } } void LLTexUnit::enable(eTextureType type) { if (mIndex < 0) return; if ( (mCurrTexType != type || gGL.mDirty) && (type != TT_NONE) ) { stop_glerror(); activate(); stop_glerror(); if (mCurrTexType != TT_NONE && !gGL.mDirty) { disable(); // Force a disable of a previous texture type if it's enabled. stop_glerror(); } mCurrTexType = type; gGL.flush(); if (!LLGLSLShader::sNoFixedFunction && //type != LLTexUnit::TT_MULTISAMPLE_TEXTURE && mIndex < gGLManager.mNumTextureUnits) { stop_glerror(); glEnable(sGLTextureType[type]); stop_glerror(); } } } void LLTexUnit::disable(void) { if (mIndex < 0) return; if (mCurrTexType != TT_NONE) { activate(); unbind(mCurrTexType); gGL.flush(); if (!LLGLSLShader::sNoFixedFunction && //mCurrTexType != LLTexUnit::TT_MULTISAMPLE_TEXTURE && mIndex < gGLManager.mNumTextureUnits) { glDisable(sGLTextureType[mCurrTexType]); } mCurrTexType = TT_NONE; } } bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind) { stop_glerror(); if (mIndex >= 0) { //gGL.flush(); LLImageGL* gl_tex = NULL ; if (texture != NULL && (gl_tex = texture->getGLTexture())) { if (gl_tex->getTexName()) //if texture exists { //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) { gGL.flush(); activate(); enable(gl_tex->getTarget()); mCurrTexture = gl_tex->getTexName(); //validate_bind_texture(mCurrTexture); 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); } } } else { //if deleted, will re-generate it immediately texture->forceImmediateUpdate() ; gl_tex->forceUpdateBindStats() ; return texture->bindDefaultImage(mIndex); } } else { LL_WARNS() << "NULL LLTexUnit::bind texture" << LL_ENDL; return false; } } else { // mIndex < 0 return false; } return true; } bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind) { if (mIndex < 0) return false; if(!texture) { LL_WARNS() << "NULL LLTexUnit::bind texture" << LL_ENDL; 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(); //validate_bind_texture(mCurrTexture); 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; if (cubeMap == NULL) { LL_WARNS() << "NULL LLTexUnit::bind cubemap" << LL_ENDL; return false; } if (cubeMap->mImages[0].isNull()) { LL_WARNS() << "NULL LLTexUnit::bind cubeMap->mImages[0]" << LL_ENDL; return false; } if (mCurrTexture != cubeMap->mImages[0]->getTexName()) { if (gGLManager.mHasCubeMap && LLCubeMap::sUseCubeMaps) { gGL.flush(); activate(); enable(LLTexUnit::TT_CUBE_MAP); mCurrTexture = cubeMap->mImages[0]->getTexName(); //validate_bind_texture(mCurrTexture); 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 { LL_WARNS() << "Using cube map without extension!" << LL_ENDL; 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()) { LL_ERRS() << "Cannot bind a render buffer for sampling. Allocate render target without a stencil buffer if sampling of depth buffer is required." << LL_ENDL; } 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; //validate_bind_texture(texture); glBindTexture(sGLTextureType[type], texture); mHasMipMaps = hasMips; } return true; } void LLTexUnit::unbind(eTextureType type) { if (mIndex < 0) return; //always flush and activate for consistency // some code paths assume unbind always flushes and sets the active texture if (gGL.getCurrentTexUnitIndex() != mIndex || gGL.mDirty) { gGL.flush(); activate(); } // Disabled caching of binding state. if (mCurrTexType == type && mCurrTexture != 0) { gGL.flush(); mCurrTexture = 0; if (LLGLSLShader::sNoFixedFunction && type == LLTexUnit::TT_TEXTURE) { //if (sWhiteTexture) // validate_bind_texture(sWhiteTexture); glBindTexture(sGLTextureType[type], sWhiteTexture); } else { 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 /*|| mCurrTexType == LLTexUnit::TT_MULTISAMPLE_TEXTURE*/) 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) { if (mHasMipMaps) { glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); } else { glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR); } } else { if (mHasMipMaps) { glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); } 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); LL_INFOS() << "gGL.mMaxAnisotropy: " << gGL.mMaxAnisotropy << LL_ENDL ; gGL.mMaxAnisotropy = llmax(1.f, 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 (LLGLSLShader::sNoFixedFunction) { //texture blend type means nothing when using shaders return; } if (mIndex < 0 || mIndex >= gGLManager.mNumTextureUnits) 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: LL_ERRS() << "Unknown Texture Blend Type: " << type << LL_ENDL; 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: LL_WARNS() << "Unknown eTextureBlendSrc: " << src << ". Using Vertex Color instead." << LL_ENDL; 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: LL_WARNS() << "Unknown eTextureBlendSrc: " << src << ". Using Source Color or Alpha instead." << LL_ENDL; return (isAlpha) ? GL_SRC_ALPHA: GL_SRC_COLOR; } } void LLTexUnit::setTextureCombiner(eTextureBlendOp op, eTextureBlendSrc src1, eTextureBlendSrc src2, bool isAlpha) { if (LLGLSLShader::sNoFixedFunction) { //register combiners do nothing when not using fixed function return; } if (mIndex < 0 || mIndex >= gGLManager.mNumTextureUnits) 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: LL_WARNS() << "Unknown eTextureBlendOp: " << op << ". Setting op to replace." << LL_ENDL; // 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) { gGL.flush(); mCurrColorScale = scale; glTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE, scale ); } } void LLTexUnit::setAlphaScale(S32 scale) { if (mCurrAlphaScale != scale || gGL.mDirty) { gGL.flush(); mCurrAlphaScale = scale; 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); LL_WARNS() << "Incorrect Texture Unit! Expected: " << set_unit << " Actual: " << mIndex << LL_ENDL; } } LLLightState::LLLightState(S32 index) : mState(index), mIndex(index) { mPosMatrix.setIdentity(); mSpotMatrix.setIdentity(); } #define UPDATE_LIGHTSTATE(state, value) \ if (mState.state != value) { \ mState.state = value; \ ++gGL.mLightHash; \ } #define UPDATE_LIGHTSTATE_AND_TRANSFORM(state, value, matrix, transformhash) \ if (mState.state != value || memcmp(matrix.getF32ptr(), gGL.getModelviewMatrix().getF32ptr(), sizeof(LLMatrix4a))) { \ mState.state = value; \ ++gGL.mLightHash; \ ++gGL.transformhash[mIndex]; \ matrix = gGL.getModelviewMatrix(); \ } void LLLightState::setDiffuse(const LLColor4& diffuse) { UPDATE_LIGHTSTATE(mDiffuse, diffuse); } void LLLightState::setSpecular(const LLColor4& specular) { UPDATE_LIGHTSTATE(mSpecular, specular); } void LLLightState::setPosition(const LLVector4& position) { UPDATE_LIGHTSTATE_AND_TRANSFORM(mPosition, position, mPosMatrix, mLightPositionTransformHash); } void LLLightState::setConstantAttenuation(const F32& atten) { UPDATE_LIGHTSTATE(mConstantAtten, atten); } void LLLightState::setLinearAttenuation(const F32& atten) { UPDATE_LIGHTSTATE(mLinearAtten, atten); } void LLLightState::setQuadraticAttenuation(const F32& atten) { UPDATE_LIGHTSTATE(mQuadraticAtten, atten); } void LLLightState::setSpotExponent(const F32& exponent) { UPDATE_LIGHTSTATE(mSpotExponent, exponent); } void LLLightState::setSpotCutoff(const F32& cutoff) { UPDATE_LIGHTSTATE(mSpotCutoff, cutoff); } void LLLightState::setSpotDirection(const LLVector3& direction) { UPDATE_LIGHTSTATE_AND_TRANSFORM(mSpotDirection, direction, mSpotMatrix, mLightSpotTransformHash); } void LLLightState::setEnabled(const bool enabled) { if (mEnabled != enabled) { mEnabled = enabled; ++gGL.mLightHash; } } LLRender::LLRender() : mDirty(false), mCount(0), mMode(LLRender::TRIANGLES), mMatrixMode(LLRender::MM_MODELVIEW), mMatIdx{ 0 }, mMaxAnisotropy(0.f), mPrimitiveReset(false) { 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 < NUM_LIGHTS; ++i) { mLightState.push_back(new LLLightState(i)); } resetSyncHashes(); //Init base matrix for each mode for(S32 i = 0; i < NUM_MATRIX_MODES; ++i) { mMatrix[i][0].setIdentity(); } gGLModelView.setIdentity(); gGLLastModelView.setIdentity(); gGLPreviousModelView.setIdentity(); gGLLastProjection.setIdentity(); gGLProjection.setIdentity(); } LLRender::~LLRender() { shutdown(); } void LLRender::init() { if (sGLCoreProfile && !LLVertexBuffer::sUseVAO) { //bind a dummy vertex array object so we're core profile compliant #ifdef GL_ARB_vertex_array_object U32 ret; glGenVertexArrays(1, &ret); glBindVertexArray(ret); #endif } stop_glerror(); restoreVertexBuffers(); } void LLRender::shutdown() { for (U32 i = 0; i < mTexUnits.size(); i++) { delete mTexUnits[i]; } mTexUnits.clear(); delete mDummyTexUnit; mDummyTexUnit = NULL; for (U32 i = 0; i < mLightState.size(); ++i) { delete mLightState[i]; } mLightState.clear(); mBuffer = NULL ; } void LLRender::destroyGL() { // Reset gl state cache mCurShader = 0; mContext = Context(); resetSyncHashes(); LLTexUnit::sWhiteTexture = 0; // Also done in LLImageGL::destroyGL. for (auto unit : mTexUnits) { if (unit->getCurrTexture() > 0) { unit->unbind(unit->getCurrType()); } } resetVertexBuffers(); } void LLRender::refreshState(void) { mDirty = true; U32 active_unit = getCurrentTexUnitIndex(); for (U32 i = 0; i < mTexUnits.size(); i++) { mTexUnits[i]->refreshState(); stop_glerror(); } mTexUnits[active_unit]->activate(); stop_glerror(); /*setColorMask(mCurrColorMask[0], mCurrColorMask[1], mCurrColorMask[2], mCurrColorMask[3]); stop_glerror(); setAlphaRejectSettings(mCurrAlphaFunc, mCurrAlphaFuncVal); stop_glerror(); //Singu note: Also reset glBlendFunc blendFunc(mCurrBlendColorSFactor,mCurrBlendColorDFactor,mCurrBlendAlphaSFactor,mCurrBlendAlphaDFactor); stop_glerror();*/ mDirty = false; } void LLRender::resetVertexBuffers() { mBuffer = NULL; } void LLRender::restoreVertexBuffers() { if (!mBuffer.isNull()) return; stop_glerror(); mBuffer = new LLVertexBuffer(immediate_mask, 0); stop_glerror(); mBuffer->allocateBuffer(4096, 0, TRUE); stop_glerror(); mBuffer->getVertexStrider(mVerticesp); stop_glerror(); mBuffer->getTexCoord0Strider(mTexcoordsp); stop_glerror(); mBuffer->getColorStrider(mColorsp); stop_glerror(); } void LLRender::syncShaders() { if (mCurShader != mNextShader) { glUseProgramObjectARB(mNextShader); mCurShader = mNextShader; } } void LLRender::syncContextState() { if (mContext.color != mNewContext.color) { mContext.color = mNewContext.color; glColor4fv(mContext.color.mV); } if (mContext.colorMask != mNewContext.colorMask) { mContext.colorMask = mNewContext.colorMask; glColorMask( mContext.colorMask & (1 << 0), mContext.colorMask & (1 << 1), mContext.colorMask & (1 << 2), mContext.colorMask & (1 << 3)); } if (mContext.alphaFunc != mNewContext.alphaFunc || mContext.alphaVal != mNewContext.alphaVal) { mContext.alphaFunc = mNewContext.alphaFunc; mContext.alphaVal = mNewContext.alphaVal; if (mContext.alphaFunc == CF_DEFAULT) { glAlphaFunc(GL_GREATER, 0.01f); } else { glAlphaFunc(sGLCompareFunc[mContext.alphaFunc], mContext.alphaVal); } } if (LLGLState::isEnabled() && ( mContext.blendColorSFactor != mNewContext.blendColorSFactor || mContext.blendAlphaSFactor != mNewContext.blendAlphaSFactor || mContext.blendColorDFactor != mNewContext.blendColorDFactor || mContext.blendAlphaDFactor != mNewContext.blendAlphaDFactor)) { mContext.blendColorSFactor = mNewContext.blendColorSFactor; mContext.blendAlphaSFactor = mNewContext.blendAlphaSFactor; mContext.blendColorDFactor = mNewContext.blendColorDFactor; mContext.blendAlphaDFactor = mNewContext.blendAlphaDFactor; if (mContext.blendColorSFactor == mContext.blendAlphaSFactor && mContext.blendColorDFactor == mContext.blendAlphaDFactor) { glBlendFunc(sGLBlendFactor[mContext.blendColorSFactor], sGLBlendFactor[mContext.blendColorDFactor]); } else { glBlendFuncSeparateEXT(sGLBlendFactor[mContext.blendColorSFactor], sGLBlendFactor[mContext.blendColorDFactor], sGLBlendFactor[mContext.blendAlphaSFactor], sGLBlendFactor[mContext.blendAlphaDFactor]); } } if (mContext.lineWidth != mNewContext.lineWidth) { mContext.lineWidth = mNewContext.lineWidth; glLineWidth(mContext.lineWidth); } if (mContext.pointSize != mNewContext.pointSize) { mContext.pointSize = mNewContext.pointSize; glPointSize(mContext.pointSize); } if (mContext.polygonMode[0] != mNewContext.polygonMode[0] || mContext.polygonMode[1] != mNewContext.polygonMode[1]) { if (mNewContext.polygonMode[0] == mNewContext.polygonMode[1]) { glPolygonMode(GL_FRONT_AND_BACK, sGLPolygonMode[mNewContext.polygonMode[0]]); } else { if (mContext.polygonMode[0] != mNewContext.polygonMode[0]) { glPolygonMode(GL_FRONT, sGLPolygonMode[mNewContext.polygonMode[0]]); } if (mContext.polygonMode[1] != mNewContext.polygonMode[1]) { glPolygonMode(GL_BACK, sGLPolygonMode[mNewContext.polygonMode[1]]); } } mContext.polygonMode[0] = mNewContext.polygonMode[0]; mContext.polygonMode[1] = mNewContext.polygonMode[1]; } if (mContext.polygonOffset[0] != mNewContext.polygonOffset[0] || mContext.polygonOffset[1] != mNewContext.polygonOffset[1]) { mContext.polygonOffset[0] = mNewContext.polygonOffset[0]; mContext.polygonOffset[1] = mNewContext.polygonOffset[1]; glPolygonOffset(mContext.polygonOffset[0], mContext.polygonOffset[1]); } if (mContext.viewPort != mNewContext.viewPort) { mContext.viewPort = mNewContext.viewPort; glViewport(mContext.viewPort.mLeft, mContext.viewPort.mBottom, mContext.viewPort.getWidth(), mContext.viewPort.getHeight()); } if (LLGLState::isEnabled() && mContext.scissor != mNewContext.scissor) { mContext.scissor = mNewContext.scissor; glScissor(mContext.scissor.mLeft, mContext.scissor.mBottom, mContext.scissor.getWidth(), mContext.scissor.getHeight()); } } U32 sLightMask = 0xFFFFFFFF; void LLRender::syncLightState() { if (!LLGLSLShader::sNoFixedFunction) { // Legacy if (mCurLegacyLightHash != mLightHash) { mCurLegacyLightHash = mLightHash; for (U32 i = 0; i < NUM_LIGHTS; i++) { const LLLightState* light = mLightState[i]; const U32 idx = GL_LIGHT0 + i; const LLLightStateData& state = light->mState; if (light->mEnabled && (1 << i) & sLightMask) { glEnable(idx); if (mLightSpotTransformHash[i] != mCurLightSpotTransformHash[i] || mLightPositionTransformHash[i] != mCurLightPositionTransformHash[i]) { glPushAttrib(GL_TRANSFORM_BIT); glMatrixMode(GL_MODELVIEW); glPushMatrix(); if (mLightPositionTransformHash[i] != mCurLightPositionTransformHash[i]) { glLoadMatrixf(light->mPosMatrix.getF32ptr()); glLightfv(idx, GL_POSITION, state.mPosition.mV); } if (mLightSpotTransformHash[i] != mCurLightSpotTransformHash[i]) { glLoadMatrixf(light->mSpotMatrix.getF32ptr()); glLightfv(idx, GL_SPOT_DIRECTION, state.mSpotDirection.mV); } mCurLightPositionTransformHash[i] = mLightPositionTransformHash[i]; mCurLightSpotTransformHash[i] = mLightSpotTransformHash[i]; glPopMatrix(); glPopAttrib(); } glLightfv(idx, GL_DIFFUSE, state.mDiffuse.mV); glLightfv(idx, GL_SPECULAR, state.mSpecular.mV); glLightf(idx, GL_CONSTANT_ATTENUATION, state.mConstantAtten); glLightf(idx, GL_LINEAR_ATTENUATION, state.mLinearAtten); glLightf(idx, GL_QUADRATIC_ATTENUATION, state.mQuadraticAtten); glLightf(idx, GL_SPOT_EXPONENT, state.mSpotExponent); glLightf(idx, GL_SPOT_CUTOFF, state.mSpotCutoff); } else { glDisable(idx); } } glLightModelfv(GL_LIGHT_MODEL_AMBIENT, mAmbientLightColor.mV); } return; } LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; if (!shader || (!shader->mFeatures.hasLighting && !shader->mFeatures.calculatesLighting)) { return; } if (shader->mLightHash != mLightHash) { shader->mLightHash = mLightHash; LLVector3 attenuation[8]; LLVector3 diffuse[8]; for (U32 i = 0; i < NUM_LIGHTS; i++) { const LLLightState* light = mLightState[i]; const LLLightStateData& state = light->mState; attenuation[i].set(state.mLinearAtten, state.mQuadraticAtten, state.mSpecular.mV[3]); diffuse[i].set((light->mEnabled && (1 << i) & sLightMask) ? state.mDiffuse.mV : LLVector3::zero.mV); if (mLightPositionTransformHash[i] != mCurLightPositionTransformHash[i]) { LLVector4a pos; pos.loadua(state.mPosition.mV); light->mPosMatrix.rotate4(pos, pos); mCurLightPosition[i].set(pos.getF32ptr()); mCurLightPositionTransformHash[i] = mLightPositionTransformHash[i]; } // If state.mSpecular.mV[3] == 0.f then this light is a spotlight, thus update the direction... // Otherwise don't bother and leave the hash stale in case it turns into a spotlight later. if (state.mSpecular.mV[3] == 0.f && mLightSpotTransformHash[i] != mCurLightSpotTransformHash[i]) { LLVector4a dir; dir.load3(state.mSpotDirection.mV); light->mSpotMatrix.rotate(dir, dir); mCurSpotDirection[i].set(dir.getF32ptr()); mCurLightSpotTransformHash[i] = mLightSpotTransformHash[i]; } } shader->uniform4fv(LLShaderMgr::LIGHT_POSITION, NUM_LIGHTS, mCurLightPosition[0].mV); shader->uniform3fv(LLShaderMgr::LIGHT_DIRECTION, NUM_LIGHTS, mCurSpotDirection[0].mV); shader->uniform3fv(LLShaderMgr::LIGHT_ATTENUATION, NUM_LIGHTS, attenuation[0].mV); shader->uniform3fv(LLShaderMgr::LIGHT_DIFFUSE, NUM_LIGHTS, diffuse[0].mV); shader->uniform4fv(LLShaderMgr::LIGHT_AMBIENT, 1, mAmbientLightColor.mV); //HACK -- duplicate sunlight color for compatibility with drivers that can't deal with multiple shader objects referencing the same uniform shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, 1, diffuse[0].mV); } } void LLRender::syncMatrices() { stop_glerror(); syncShaders(); static const U32 name[] = { LLShaderMgr::MODELVIEW_MATRIX, LLShaderMgr::PROJECTION_MATRIX, LLShaderMgr::TEXTURE_MATRIX0, LLShaderMgr::TEXTURE_MATRIX1, LLShaderMgr::TEXTURE_MATRIX2, LLShaderMgr::TEXTURE_MATRIX3, }; LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; static LLMatrix4a cached_mvp; static U32 cached_mvp_mdv_hash = 0xFFFFFFFF; static U32 cached_mvp_proj_hash = 0xFFFFFFFF; static LLMatrix4a cached_normal; static U32 cached_normal_hash = 0xFFFFFFFF; if (shader) { llassert(shader); bool mvp_done = false; U32 i = MM_MODELVIEW; if (mMatHash[i] != shader->mMatHash[i]) { //update modelview, normal, and MVP const LLMatrix4a& mat = mMatrix[i][mMatIdx[i]]; shader->uniformMatrix4fv(name[i], 1, GL_FALSE, mat.getF32ptr()); shader->mMatHash[i] = mMatHash[i]; //update normal matrix S32 loc = shader->getUniformLocation(LLShaderMgr::NORMAL_MATRIX); if (loc > -1) { if (cached_normal_hash != mMatHash[i]) { cached_normal = mat; cached_normal.invert(); cached_normal.transpose(); cached_normal_hash = mMatHash[i]; } const LLMatrix4a& norm = cached_normal; LLVector3 norms[3]; norms[0].set(norm.getRow<0>().getF32ptr()); norms[1].set(norm.getRow<1>().getF32ptr()); norms[2].set(norm.getRow<2>().getF32ptr()); shader->uniformMatrix3fv(LLShaderMgr::NORMAL_MATRIX, 1, GL_FALSE, norms[0].mV); } //update MVP matrix mvp_done = true; loc = shader->getUniformLocation(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX); if (loc > -1) { U32 proj = MM_PROJECTION; if (cached_mvp_mdv_hash != mMatHash[i] || cached_mvp_proj_hash != mMatHash[MM_PROJECTION]) { cached_mvp.setMul(mMatrix[proj][mMatIdx[proj]], mat); cached_mvp_mdv_hash = mMatHash[i]; cached_mvp_proj_hash = mMatHash[MM_PROJECTION]; } shader->uniformMatrix4fv(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX, 1, GL_FALSE, cached_mvp.getF32ptr()); } } i = MM_PROJECTION; if (mMatHash[i] != shader->mMatHash[i]) { //update projection matrix, normal, and MVP const LLMatrix4a& mat = mMatrix[i][mMatIdx[i]]; shader->uniformMatrix4fv(name[i], 1, GL_FALSE, mat.getF32ptr()); shader->mMatHash[i] = mMatHash[i]; if (!mvp_done) { //update MVP matrix S32 loc = shader->getUniformLocation(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX); if (loc > -1) { if (cached_mvp_mdv_hash != mMatHash[i] || cached_mvp_proj_hash != mMatHash[MM_PROJECTION]) { U32 mdv = MM_MODELVIEW; cached_mvp.setMul(mat,mMatrix[mdv][mMatIdx[mdv]]); cached_mvp_mdv_hash = mMatHash[MM_MODELVIEW]; cached_mvp_proj_hash = mMatHash[MM_PROJECTION]; } shader->uniformMatrix4fv(LLShaderMgr::MODELVIEW_PROJECTION_MATRIX, 1, GL_FALSE, cached_mvp.getF32ptr()); } } } for (i = MM_TEXTURE0; i < NUM_MATRIX_MODES; ++i) { if (mMatHash[i] != shader->mMatHash[i]) { shader->uniformMatrix4fv(name[i], 1, GL_FALSE, mMatrix[i][mMatIdx[i]].getF32ptr()); shader->mMatHash[i] = mMatHash[i]; } } } else if (!LLGLSLShader::sNoFixedFunction) { GLenum mode[] = { GL_MODELVIEW, GL_PROJECTION, GL_TEXTURE, GL_TEXTURE, GL_TEXTURE, GL_TEXTURE, }; for (U32 i = 0; i < 2; ++i) { if (mMatHash[i] != mCurLegacyMatHash[i]) { glMatrixMode(mode[i]); glLoadMatrixf(mMatrix[i][mMatIdx[i]].getF32ptr()); mCurLegacyMatHash[i] = mMatHash[i]; } } for (U32 i = 2; i < NUM_MATRIX_MODES; ++i) { if (mMatHash[i] != mCurLegacyMatHash[i]) { gGL.getTexUnit(i-2)->activate(); glMatrixMode(mode[i]); glLoadMatrixf(mMatrix[i][mMatIdx[i]].getF32ptr()); mCurLegacyMatHash[i] = mMatHash[i]; } } } //also sync light state syncLightState(); //sync context. syncContextState(); stop_glerror(); } LLMatrix4a LLRender::genRot(const GLfloat& a, const LLVector4a& axis) const { F32 r = a * DEG_TO_RAD; F32 c = cosf(r); F32 s = sinf(r); F32 ic = 1.f-c; const LLVector4a add1(c,axis[VZ]*s,-axis[VY]*s); //1,z,-y const LLVector4a add2(-axis[VZ]*s,c,axis[VX]*s); //-z,1,x const LLVector4a add3(axis[VY]*s,-axis[VX]*s,c); //y,-x,1 LLVector4a axis_x; axis_x.splat<0>(axis); LLVector4a axis_y; axis_y.splat<1>(axis); LLVector4a axis_z; axis_z.splat<2>(axis); LLVector4a c_axis; c_axis.setMul(axis,ic); LLMatrix4a rot_mat; rot_mat.getRow<0>().setMul(c_axis,axis_x); rot_mat.getRow<0>().add(add1); rot_mat.getRow<1>().setMul(c_axis,axis_y); rot_mat.getRow<1>().add(add2); rot_mat.getRow<2>().setMul(c_axis,axis_z); rot_mat.getRow<2>().add(add3); rot_mat.setRow<3>(LLVector4a(0,0,0,1)); return rot_mat; } LLMatrix4a LLRender::genOrtho(const GLfloat& left, const GLfloat& right, const GLfloat& bottom, const GLfloat& top, const GLfloat& zNear, const GLfloat& zFar) const { LLMatrix4a ortho_mat; ortho_mat.setRow<0>(LLVector4a(2.f/(right-left),0,0)); ortho_mat.setRow<1>(LLVector4a(0,2.f/(top-bottom),0)); ortho_mat.setRow<2>(LLVector4a(0,0,-2.f/(zFar-zNear))); ortho_mat.setRow<3>(LLVector4a(-(right+left)/(right-left),-(top+bottom)/(top-bottom),-(zFar+zNear)/(zFar-zNear),1)); return ortho_mat; } LLMatrix4a LLRender::genPersp(const GLfloat& fovy, const GLfloat& aspect, const GLfloat& zNear, const GLfloat& zFar) const { GLfloat f = 1.f/tanf(DEG_TO_RAD*fovy/2.f); LLMatrix4a persp_mat; persp_mat.setRow<0>(LLVector4a(f/aspect,0,0)); persp_mat.setRow<1>(LLVector4a(0,f,0)); persp_mat.setRow<2>(LLVector4a(0,0,(zFar+zNear)/(zNear-zFar),-1.f)); persp_mat.setRow<3>(LLVector4a(0,0,(2.f*zFar*zNear)/(zNear-zFar),0)); return persp_mat; } LLMatrix4a LLRender::genLook(const LLVector3& pos_in, const LLVector3& dir_in, const LLVector3& up_in) const { const LLVector4a pos(pos_in.mV[VX],pos_in.mV[VY],pos_in.mV[VZ],1.f); LLVector4a dir(dir_in.mV[VX],dir_in.mV[VY],dir_in.mV[VZ]); const LLVector4a up(up_in.mV[VX],up_in.mV[VY],up_in.mV[VZ]); LLVector4a left_norm; left_norm.setCross3(dir,up); left_norm.normalize3fast(); LLVector4a up_norm; up_norm.setCross3(left_norm,dir); up_norm.normalize3fast(); LLVector4a& dir_norm = dir; dir.normalize3fast(); LLVector4a left_dot; left_dot.setAllDot3(left_norm,pos); left_dot.negate(); LLVector4a up_dot; up_dot.setAllDot3(up_norm,pos); up_dot.negate(); LLVector4a dir_dot; dir_dot.setAllDot3(dir_norm,pos); dir_norm.negate(); LLMatrix4a lookat_mat; lookat_mat.setRow<0>(left_norm); lookat_mat.setRow<1>(up_norm); lookat_mat.setRow<2>(dir_norm); lookat_mat.setRow<3>(LLVector4a(0,0,0,1)); lookat_mat.getRow<0>().copyComponent<3>(left_dot); lookat_mat.getRow<1>().copyComponent<3>(up_dot); lookat_mat.getRow<2>().copyComponent<3>(dir_dot); lookat_mat.transpose(); return lookat_mat; } const LLMatrix4a& LLRender::genNDCtoWC() const { static LLMatrix4a mat( LLVector4a(.5f,0,0,0), LLVector4a(0,.5f,0,0), LLVector4a(0,0,.5f,0), LLVector4a(.5f,.5f,.5f,1.f)); return mat; } void LLRender::translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z) { if( llabs(x) < F_APPROXIMATELY_ZERO && llabs(y) < F_APPROXIMATELY_ZERO && llabs(z) < F_APPROXIMATELY_ZERO) { return; } flush(); mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].applyTranslation_affine(x,y,z); mMatHash[mMatrixMode]++; } void LLRender::scalef(const GLfloat& x, const GLfloat& y, const GLfloat& z) { if( (llabs(x-1.f)) < F_APPROXIMATELY_ZERO && (llabs(y-1.f)) < F_APPROXIMATELY_ZERO && (llabs(z-1.f)) < F_APPROXIMATELY_ZERO) { return; } flush(); { mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].applyScale_affine(x,y,z); mMatHash[mMatrixMode]++; } } void LLRender::ortho(F32 left, F32 right, F32 bottom, F32 top, F32 zNear, F32 zFar) { flush(); LLMatrix4a ortho_mat; ortho_mat.setRow<0>(LLVector4a(2.f/(right-left),0,0)); ortho_mat.setRow<1>(LLVector4a(0,2.f/(top-bottom),0)); ortho_mat.setRow<2>(LLVector4a(0,0,-2.f/(zFar-zNear))); ortho_mat.setRow<3>(LLVector4a(-(right+left)/(right-left),-(top+bottom)/(top-bottom),-(zFar+zNear)/(zFar-zNear),1)); mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mul_affine(ortho_mat); mMatHash[mMatrixMode]++; } void LLRender::rotatef(const LLMatrix4a& rot) { flush(); mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mul_affine(rot); mMatHash[mMatrixMode]++; } void LLRender::rotatef(const GLfloat& a, const GLfloat& x, const GLfloat& y, const GLfloat& z) { if( llabs(a) < F_APPROXIMATELY_ZERO || llabs(a-360.f) < F_APPROXIMATELY_ZERO) { return; } flush(); rotatef(genRot(a,x,y,z)); } //LLRender::projectf & LLRender::unprojectf adapted from gluProject & gluUnproject in Mesa's GLU 9.0 library. // License/Copyright Statement: /* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ bool LLRender::projectf(const LLVector3& object, const LLMatrix4a& modelview, const LLMatrix4a& projection, const LLRect& viewport, LLVector3& windowCoordinate) { //Begin SSE intrinsics // Declare locals const LLVector4a obj_vector(object.mV[VX],object.mV[VY],object.mV[VZ]); const LLVector4a one(1.f); LLVector4a temp_vec; //Scratch vector LLVector4a w; //Splatted W-component. modelview.affineTransform(obj_vector, temp_vec); //temp_vec = modelview * obj_vector; //Passing temp_matrix as v and res is safe. res not altered until after all other calculations projection.rotate4(temp_vec, temp_vec); //temp_vec = projection * temp_vec w.splat<3>(temp_vec); //w = temp_vec.wwww //If w == 0.f, use 1.f instead. LLVector4a div; div.setSelectWithMask( w.equal( _mm_setzero_ps() ), one, w ); //float div = (w[N] == 0.f ? 1.f : w[N]); temp_vec.div(div); //temp_vec /= div; //Map x, y to range 0-1 temp_vec.mul(.5f); temp_vec.add(.5f); LLVector4Logical mask = temp_vec.equal(_mm_setzero_ps()); if(mask.areAllSet(LLVector4Logical::MASK_W)) return false; //End SSE intrinsics //Window coordinates windowCoordinate[0]=temp_vec[VX]*viewport.getWidth()+viewport.mLeft; windowCoordinate[1]=temp_vec[VY]*viewport.getHeight()+viewport.mBottom; //This is only correct when glDepthRange(0.0, 1.0) windowCoordinate[2]=temp_vec[VZ]; return true; } bool LLRender::unprojectf(const LLVector3& windowCoordinate, const LLMatrix4a& modelview, const LLMatrix4a& projection, const LLRect& viewport, LLVector3& object) { //Begin SSE intrinsics // Declare locals static const LLVector4a one(1.f); static const LLVector4a two(2.f); LLVector4a norm_view( ((windowCoordinate.mV[VX] - (F32)viewport.mLeft) / (F32)viewport.getWidth()), ((windowCoordinate.mV[VY] - (F32)viewport.mBottom) / (F32)viewport.getHeight()), windowCoordinate.mV[VZ], 1.f); LLMatrix4a inv_mat; //Inverse transformation matrix LLVector4a temp_vec; //Scratch vector LLVector4a w; //Splatted W-component. inv_mat.setMul(projection,modelview); //inv_mat = projection*modelview float det = inv_mat.invert(); //Normalize. -1.0 : +1.0 norm_view.mul(two); // norm_view *= vec4(.2f) norm_view.sub(one); // norm_view -= vec4(1.f) inv_mat.rotate4(norm_view,temp_vec); //inv_mat * norm_view w.splat<3>(temp_vec); //w = temp_vec.wwww //If w == 0.f, use 1.f instead. Defer return if temp_vec.w == 0.f until after all SSE intrinsics. LLVector4a div; div.setSelectWithMask( w.equal( _mm_setzero_ps() ), one, w ); //float div = (w[N] == 0.f ? 1.f : w[N]); temp_vec.div(div); //temp_vec /= div; LLVector4Logical mask = temp_vec.equal(_mm_setzero_ps()); if(mask.areAllSet(LLVector4Logical::MASK_W)) return false; //End SSE intrinsics if(det == 0.f) return false; object.set(temp_vec.getF32ptr()); return true; } void LLRender::pushMatrix() { { if (mMatIdx[mMatrixMode] < LL_MATRIX_STACK_DEPTH-1) { mMatrix[mMatrixMode][mMatIdx[mMatrixMode]+1] = mMatrix[mMatrixMode][mMatIdx[mMatrixMode]]; ++mMatIdx[mMatrixMode]; } else { LL_WARNS() << "Matrix stack overflow." << LL_ENDL; } } } void LLRender::popMatrix() { { if (mMatIdx[mMatrixMode] > 0) { if ( memcmp(mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].getF32ptr(), mMatrix[mMatrixMode][mMatIdx[mMatrixMode] - 1].getF32ptr(), sizeof(LLMatrix4a)) ) { flush(); } --mMatIdx[mMatrixMode]; mMatHash[mMatrixMode]++; } else { flush(); LL_WARNS() << "Matrix stack underflow." << LL_ENDL; } } } void LLRender::loadMatrix(const LLMatrix4a& mat) { flush(); mMatrix[mMatrixMode][mMatIdx[mMatrixMode]] = mat; mMatHash[mMatrixMode]++; } void LLRender::multMatrix(const LLMatrix4a& mat) { flush(); mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].mul_affine(mat); mMatHash[mMatrixMode]++; } void LLRender::matrixMode(U32 mode) { if (mode == MM_TEXTURE) { mode = MM_TEXTURE0 + gGL.getCurrentTexUnitIndex(); } llassert(mode < NUM_MATRIX_MODES); mMatrixMode = mode; } U32 LLRender::getMatrixMode() { if (mMatrixMode >= MM_TEXTURE0 && mMatrixMode <= MM_TEXTURE3) { //always return MM_TEXTURE if current matrix mode points at any texture matrix return MM_TEXTURE; } return mMatrixMode; } void LLRender::loadIdentity() { flush(); mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].setIdentity(); mMatHash[mMatrixMode]++; } const LLMatrix4a& LLRender::getModelviewMatrix() { return mMatrix[MM_MODELVIEW][mMatIdx[MM_MODELVIEW]]; } const LLMatrix4a& LLRender::getProjectionMatrix() { return mMatrix[MM_PROJECTION][mMatIdx[MM_PROJECTION]]; } void LLRender::translateUI(F32 x, F32 y, F32 z) { if (mUIOffset.empty()) { LL_ERRS() << "Need to push a UI translation frame before offsetting" << LL_ENDL; } LLVector4a add(x,y,z); mUIOffset.back().add(add); } void LLRender::scaleUI(F32 x, F32 y, F32 z) { if (mUIScale.empty()) { LL_ERRS() << "Need to push a UI transformation frame before scaling." << LL_ENDL; } LLVector4a scale(x,y,z); mUIScale.back().mul(scale); } void LLRender::rotateUI(LLQuaternion& rot) { if (mUIRotation.empty()) { mUIRotation.push_back(rot); } else { mUIRotation.push_back(mUIRotation.back()*rot); } } void LLRender::pushUIMatrix() { if (mUIOffset.empty()) { mUIOffset.emplace_back(LLVector4a(0.f)); } else { mUIOffset.push_back(mUIOffset.back()); } if (mUIScale.empty()) { mUIScale.emplace_back(LLVector4a(1.f)); } else { mUIScale.push_back(mUIScale.back()); } if (!mUIRotation.empty()) { mUIRotation.push_back(mUIRotation.back()); } } void LLRender::popUIMatrix() { if (mUIOffset.empty() || mUIScale.empty()) { LL_ERRS() << "UI offset or scale stack blown." << LL_ENDL; } mUIOffset.pop_back(); mUIScale.pop_back(); if (!mUIRotation.empty()) { mUIRotation.pop_back(); } } LLVector3 LLRender::getUITranslation() { if (mUIOffset.empty()) { return LLVector3(0,0,0); } return LLVector3(mUIOffset.back().getF32ptr()); } LLVector3 LLRender::getUIScale() { if (mUIScale.empty()) { return LLVector3(1,1,1); } return LLVector3(mUIScale.back().getF32ptr()); } void LLRender::loadUIIdentity() { if (mUIOffset.empty() || mUIScale.empty()) { LL_ERRS() << "Need to push UI translation frame before clearing offset." << LL_ENDL; } mUIOffset.back().splat(0.f); mUIScale.back().splat(1.f); if (!mUIRotation.empty()) mUIRotation.push_back(LLQuaternion()); } void LLRender::setColorMask(bool writeColor, bool writeAlpha) { setColorMask(writeColor, writeColor, writeColor, writeAlpha); } void LLRender::setColorMask(bool writeColorR, bool writeColorG, bool writeColorB, bool writeAlpha) { const U8 mask = (U8)writeColorR | ((U8)writeColorG << 1) | ((U8)writeColorB << 2) | ((U8)writeAlpha << 3); if (mNewContext.colorMask != mask || mDirty) { flush(); mNewContext.colorMask = mask; } } 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: LL_ERRS() << "Unknown Scene Blend Type: " << type << LL_ENDL; break; } } void LLRender::setAlphaRejectSettings(eCompareFunc func, F32 value) { if (LLGLSLShader::sNoFixedFunction) { //glAlphaFunc is deprecated in OpenGL 3.3 return; } if (mNewContext.alphaFunc != func || mNewContext.alphaVal != value || mDirty) { flush(); mNewContext.alphaFunc = func; mNewContext.alphaVal = value; } /*if (gDebugGL) { //make sure cached state is correct GLint cur_func = 0; glGetIntegerv(GL_ALPHA_TEST_FUNC, &cur_func); if (func == CF_DEFAULT) { func = CF_GREATER; } if (cur_func != sGLCompareFunc[func]) { LL_ERRS() << "Alpha test function corrupted!" << LL_ENDL; } F32 ref = 0.f; glGetFloatv(GL_ALPHA_TEST_REF, &ref); if (ref != value) { LL_ERRS() << "Alpha test value corrupted!" << LL_ENDL; } }*/ } void LLRender::setViewport(const LLRect& rect) { if (mNewContext.viewPort != rect || mDirty) { flush(); mNewContext.viewPort = rect; } } void LLRender::setScissor(const LLRect& rect) { if (mNewContext.scissor != rect || mDirty) { if (LLGLState::isEnabled()) { flush(); } mNewContext.scissor = rect; } } void check_blend_funcs() { llassert_always(gGL.mNewContext.blendColorSFactor == LLRender::BF_SOURCE_ALPHA ); llassert_always(gGL.mNewContext.blendAlphaSFactor == LLRender::BF_SOURCE_ALPHA ); llassert_always(gGL.mNewContext.blendColorDFactor == LLRender::BF_ONE_MINUS_SOURCE_ALPHA); llassert_always(gGL.mNewContext.blendAlphaDFactor == LLRender::BF_ONE_MINUS_SOURCE_ALPHA ); } void LLRender::blendFunc(eBlendFactor sfactor, eBlendFactor dfactor) { llassert(sfactor < BF_UNDEF); llassert(dfactor < BF_UNDEF); if (mNewContext.blendColorSFactor != sfactor || mNewContext.blendColorDFactor != dfactor || mNewContext.blendAlphaSFactor != sfactor || mNewContext.blendAlphaDFactor != dfactor || mDirty) { if (LLGLState::isEnabled()) { flush(); } mNewContext.blendColorSFactor = sfactor; mNewContext.blendAlphaSFactor = sfactor; mNewContext.blendColorDFactor = dfactor; mNewContext.blendAlphaDFactor = 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" << LL_ENDL; blendFunc(color_sfactor, color_dfactor); return; } if (mNewContext.blendColorSFactor != color_sfactor || mNewContext.blendColorDFactor != color_dfactor || mNewContext.blendAlphaSFactor != alpha_sfactor || mNewContext.blendAlphaDFactor != alpha_dfactor || mDirty) { if (LLGLState::isEnabled()) { flush(); } mNewContext.blendColorSFactor = color_sfactor; mNewContext.blendAlphaSFactor = alpha_sfactor; mNewContext.blendColorDFactor = color_dfactor; mNewContext.blendAlphaDFactor = alpha_dfactor; } } LLTexUnit* LLRender::getTexUnit(U32 index) { if (index < mTexUnits.size()) { return mTexUnits[index]; } else { LL_DEBUGS() << "Non-existing texture unit layer requested: " << index << LL_ENDL; return mDummyTexUnit; } } LLLightState* LLRender::getLight(U32 index) { if (index < mLightState.size()) { return mLightState[index]; } return NULL; } void LLRender::setAmbientLightColor(const LLColor4& color) { if (color != mAmbientLightColor || mDirty) { ++mLightHash; mAmbientLightColor = color; } } void LLRender::setLineWidth(F32 line_width) { if (LLRender::sGLCoreProfile) { mNewContext.lineWidth = 1.f; return; } if (mNewContext.lineWidth != line_width || mDirty) { if (mMode == LLRender::LINES || LLRender::LINE_STRIP) { flush(); } mNewContext.lineWidth = line_width; } } void LLRender::setPointSize(F32 point_size) { if (mNewContext.pointSize != point_size || mDirty) { if (mMode == LLRender::POINTS) { flush(); } mNewContext.pointSize = point_size; } } void LLRender::setPolygonMode(ePolygonFaceType type, ePolygonMode mode) { ePolygonMode newMode[] = { (type == PF_FRONT_AND_BACK || type == PF_FRONT) ? mode : mNewContext.polygonMode[0], (type == PF_FRONT_AND_BACK || type == PF_BACK) ? mode : mNewContext.polygonMode[1] }; if (newMode[0] != mNewContext.polygonMode[0] || newMode[1] != mNewContext.polygonMode[1] || mDirty) { flush(); mNewContext.polygonMode[0] = newMode[0]; mNewContext.polygonMode[1] = newMode[1]; } } void LLRender::setPolygonOffset(F32 factor, F32 bias) { if (factor != mNewContext.polygonOffset[0] || bias != mNewContext.polygonOffset[1] || mDirty) { if (LLGLState::isEnabled() || LLGLState::isEnabled() /*|| Unused: LLGLState::isEnabled()*/ ) { flush(); } mNewContext.polygonOffset[0] = factor; mNewContext.polygonOffset[1] = bias; } } bool LLRender::verifyTexUnitActive(U32 unitToVerify) { if (getCurrentTexUnitIndex() == unitToVerify) { return true; } else { LL_WARNS() << "TexUnit currently active: " << getCurrentTexUnitIndex() << " (expecting " << unitToVerify << ")" << LL_ENDL; return false; } } void LLRender::clearErrors() { while (glGetError()) { //loop until no more error flags left } } void LLRender::resetSyncHashes() { memset(&mLightHash, 0, sizeof(mLightHash)); memset(&mCurLegacyLightHash, 0xFF, sizeof(mCurLegacyLightHash)); memset(mMatHash, 0, sizeof(mMatHash)); memset(mCurLegacyMatHash, 0xFF, sizeof(mCurLegacyMatHash)); memset(mLightPositionTransformHash, 0, sizeof(mLightPositionTransformHash)); memset(mCurLightPositionTransformHash, 0xFF, sizeof(mCurLightPositionTransformHash)); memset(mLightSpotTransformHash, 0, sizeof(mLightSpotTransformHash)); memset(mCurLightSpotTransformHash, 0xFF, sizeof(mLightSpotTransformHash)); } void LLRender::begin(const GLuint& mode) { if (mode != mMode) { if (mMode == LLRender::LINES || mMode == LLRender::TRIANGLES || mMode == LLRender::POINTS || mMode == LLRender::TRIANGLE_STRIP ) { flush(); } else if (mCount != 0) { LL_ERRS() << "gGL.begin() called redundantly." << LL_ENDL; } mMode = mode; } } void LLRender::end() { if (mCount == 0) { return; //IMM_ERRS << "GL begin and end called with no vertices specified." << LL_ENDL; } if ((mMode != LLRender::LINES && mMode != LLRender::TRIANGLES && mMode != LLRender::POINTS && mMode != LLRender::TRIANGLE_STRIP) || mCount > 2048) { flush(); } else if (mMode == LLRender::TRIANGLE_STRIP) { mPrimitiveReset = true; } } void LLRender::flush() { if (mCount > 0) { #if 0 if (!glIsEnabled(GL_VERTEX_ARRAY)) { LL_ERRS() << "foo 1" << LL_ENDL; } if (!glIsEnabled(GL_COLOR_ARRAY)) { LL_ERRS() << "foo 2" << LL_ENDL; } if (!glIsEnabled(GL_TEXTURE_COORD_ARRAY)) { LL_ERRS() << "foo 3" << LL_ENDL; } if (glIsEnabled(GL_NORMAL_ARRAY)) { LL_ERRS() << "foo 7" << LL_ENDL; } GLvoid* pointer; glGetPointerv(GL_VERTEX_ARRAY_POINTER, &pointer); if (pointer != &(mBuffer[0].v)) { LL_ERRS() << "foo 4" << LL_ENDL; } glGetPointerv(GL_COLOR_ARRAY_POINTER, &pointer); if (pointer != &(mBuffer[0].c)) { LL_ERRS() << "foo 5" << LL_ENDL; } glGetPointerv(GL_TEXTURE_COORD_ARRAY_POINTER, &pointer); if (pointer != &(mBuffer[0].uv)) { LL_ERRS() << "foo 6" << LL_ENDL; } #endif if (!mUIOffset.empty()) { sUICalls++; sUIVerts += mCount; } if (gDebugGL) { if (mMode == LLRender::TRIANGLES) { if (mCount%3 != 0) { LL_ERRS() << "Incomplete triangle rendered." << LL_ENDL; } } if (mMode == LLRender::LINES) { if (mCount%2 != 0) { LL_ERRS() << "Incomplete line rendered." << LL_ENDL; } } } //store mCount in a local variable to avoid re-entrance (drawArrays may call flush) U32 count = mCount; mCount = 0; if (mBuffer->useVBOs() && !mBuffer->isLocked()) { //hack to only flush the part of the buffer that was updated (relies on stream draw using buffersubdata) mBuffer->getVertexStrider(mVerticesp, 0, count); mBuffer->getTexCoord0Strider(mTexcoordsp, 0, count); mBuffer->getColorStrider(mColorsp, 0, count); } mBuffer->flush(); mBuffer->setBuffer(immediate_mask); mBuffer->drawArrays(mMode, 0, count); mVerticesp[0] = mVerticesp[count]; mTexcoordsp[0] = mTexcoordsp[count]; mColorsp[0] = mColorsp[count]; mCount = 0; mPrimitiveReset = false; } } void LLRender::vertex4a(const LLVector4a& vertex) { //the range of mVerticesp, mColorsp and mTexcoordsp is [0, 4095] if (mCount > 2048) { //break when buffer gets reasonably full to keep GL command buffers happy and avoid overflow below switch (mMode) { case LLRender::POINTS: flush(); break; case LLRender::TRIANGLES: if (mCount%3==0) flush(); break; case LLRender::LINES: if (mCount%2 == 0) flush(); break; case LLRender::TRIANGLE_STRIP: { LLVector4a vert[] = { mVerticesp[mCount - 2], mVerticesp[mCount - 1], mVerticesp[mCount] }; LLColor4U col[] = { mColorsp[mCount - 2], mColorsp[mCount - 1], mColorsp[mCount] }; LLVector2 tc[] = { mTexcoordsp[mCount - 2], mTexcoordsp[mCount - 1], mTexcoordsp[mCount] }; flush(); for (int i = 0; i < LL_ARRAY_SIZE(vert); ++i) { mVerticesp[i] = vert[i]; mColorsp[i] = col[i]; mTexcoordsp[i] = tc[i]; } mCount = 2; break; } } } if (mCount > 4094) { // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL; return; } if (mPrimitiveReset && mCount) { // Insert degenerate ++mCount; mVerticesp[mCount] = mVerticesp[mCount - 1]; mColorsp[mCount] = mColorsp[mCount - 1]; mTexcoordsp[mCount] = mTexcoordsp[mCount - 1]; mVerticesp[mCount - 1] = mVerticesp[mCount - 2]; mColorsp[mCount - 1] = mColorsp[mCount - 2]; mTexcoordsp[mCount - 1] = mTexcoordsp[mCount - 2]; } if (mUIOffset.empty()) { if (!mUIRotation.empty() && mUIRotation.back().isNotIdentity()) { LLVector4 vert(vertex.getF32ptr()); mVerticesp[mCount].loadua((vert*mUIRotation.back()).mV); } else { mVerticesp[mCount] = vertex; } } else { if (!mUIRotation.empty() && mUIRotation.back().isNotIdentity()) { LLVector4 vert(vertex.getF32ptr()); vert = vert * mUIRotation.back(); LLVector4a postrot_vert; postrot_vert.loadua(vert.mV); mVerticesp[mCount].setAdd(postrot_vert, mUIOffset.back()); mVerticesp[mCount].mul(mUIScale.back()); } else { //LLVector3 vert = (LLVector3(x,y,z)+mUIOffset.back()).scaledVec(mUIScale.back()); mVerticesp[mCount].setAdd(vertex, mUIOffset.back()); mVerticesp[mCount].mul(mUIScale.back()); } } mCount++; mVerticesp[mCount] = mVerticesp[mCount-1]; mColorsp[mCount] = mColorsp[mCount-1]; mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; if (mPrimitiveReset && mCount) { mCount++; mVerticesp[mCount] = mVerticesp[mCount - 1]; mColorsp[mCount] = mColorsp[mCount - 1]; mTexcoordsp[mCount] = mTexcoordsp[mCount - 1]; } mPrimitiveReset = false; } void LLRender::vertexBatchPreTransformed(LLVector4a* verts, S32 vert_count) { if (mCount + vert_count > 4094) { // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL; return; } if (mPrimitiveReset && mCount) { // Insert degenerate ++mCount; mVerticesp[mCount] = verts[0]; mColorsp[mCount] = mColorsp[mCount - 1]; mTexcoordsp[mCount] = mTexcoordsp[mCount - 1]; mVerticesp[mCount - 1] = mVerticesp[mCount - 2]; mColorsp[mCount - 1] = mColorsp[mCount - 2]; mTexcoordsp[mCount - 1] = mTexcoordsp[mCount - 2]; ++mCount; mColorsp[mCount] = mColorsp[mCount - 1]; mTexcoordsp[mCount] = mTexcoordsp[mCount - 1]; } 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]; mPrimitiveReset = false; } void LLRender::vertexBatchPreTransformed(LLVector4a* verts, LLVector2* uvs, S32 vert_count) { if (mCount + vert_count > 4094) { // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL; return; } if (mPrimitiveReset && mCount) { // Insert degenerate ++mCount; mVerticesp[mCount] = verts[0]; mColorsp[mCount] = mColorsp[mCount - 1]; mTexcoordsp[mCount] = uvs[0]; mVerticesp[mCount - 1] = mVerticesp[mCount - 2]; mColorsp[mCount - 1] = mColorsp[mCount - 2]; mTexcoordsp[mCount - 1] = mTexcoordsp[mCount - 2]; ++mCount; mColorsp[mCount] = mColorsp[mCount - 1]; mTexcoordsp[mCount] = mTexcoordsp[mCount - 1]; } 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]; mPrimitiveReset = false; } void LLRender::vertexBatchPreTransformed(LLVector4a* verts, LLVector2* uvs, LLColor4U* colors, S32 vert_count) { if (mCount + vert_count > 4094) { // LL_WARNS() << "GL immediate mode overflow. Some geometry not drawn." << LL_ENDL; return; } if (mPrimitiveReset && mCount) { // Insert degenerate ++mCount; mVerticesp[mCount] = verts[0]; mColorsp[mCount] = colors[mCount - 1]; mTexcoordsp[mCount] = uvs[0]; mVerticesp[mCount - 1] = mVerticesp[mCount - 2]; mColorsp[mCount - 1] = mColorsp[mCount - 2]; mTexcoordsp[mCount - 1] = mTexcoordsp[mCount - 2]; ++mCount; mColorsp[mCount] = mColorsp[mCount - 1]; mTexcoordsp[mCount] = mTexcoordsp[mCount - 1]; } // Singu Note: Batch copies instead of iterating. mVerticesp.copyArray(mCount, verts, vert_count); mTexcoordsp.copyArray(mCount, uvs, vert_count); mColorsp.copyArray(mCount, colors, vert_count); mCount += vert_count; mVerticesp[mCount] = mVerticesp[mCount-1]; mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; mColorsp[mCount] = mColorsp[mCount-1]; mPrimitiveReset = false; } 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) { if (!LLGLSLShader::sCurBoundShaderPtr || LLGLSLShader::sCurBoundShaderPtr->mAttributeMask & LLVertexBuffer::MAP_COLOR) { mColorsp[mCount] = LLColor4U(r,g,b,a); } else { //not using shaders or shader reads color from a uniform diffuseColor4ub(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::diffuseColor3f(F32 r, F32 g, F32 b) { LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; llassert(!LLGLSLShader::sNoFixedFunction || shader != NULL); if (shader) { shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r,g,b,1.f); } else if (r != mNewContext.color.mV[0] || g != mNewContext.color.mV[1] || b != mNewContext.color.mV[2] || mNewContext.color.mV[3] != 1.f || mDirty) { flush(); mNewContext.color.set(r, g, b, 1.f); } } void LLRender::diffuseColor3fv(const F32* c) { LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; llassert(!LLGLSLShader::sNoFixedFunction || shader != NULL); if (shader) { shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, c[0], c[1], c[2], 1.f); } else if (c[0] != mNewContext.color.mV[0] || c[1] != mNewContext.color.mV[1] || c[2] != mNewContext.color.mV[2] || mNewContext.color.mV[3] != 1.f || mDirty) { flush(); mNewContext.color.set(c[0], c[1], c[2], 1.f); } } void LLRender::diffuseColor4f(F32 r, F32 g, F32 b, F32 a) { LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; llassert(!LLGLSLShader::sNoFixedFunction || shader != NULL); if (shader) { shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r,g,b,a); } else if (r != mNewContext.color.mV[0] || g != mNewContext.color.mV[1] || b != mNewContext.color.mV[2] || a != mNewContext.color.mV[3] || mDirty) { flush(); mNewContext.color = { r, g, b, a }; } } void LLRender::diffuseColor4fv(const F32* c) { LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; llassert(!LLGLSLShader::sNoFixedFunction || shader != NULL); if (shader) { shader->uniform4fv(LLShaderMgr::DIFFUSE_COLOR, 1, c); } else if (c[0] != mNewContext.color.mV[0] || c[1] != mNewContext.color.mV[1] || c[2] != mNewContext.color.mV[2] || c[3] != mNewContext.color.mV[3] || mDirty) { flush(); mNewContext.color.set(c); } } void LLRender::diffuseColor4ubv(const U8* c) { LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; llassert(!LLGLSLShader::sNoFixedFunction || shader != NULL); if (shader) { shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, c[0]/255.f, c[1]/255.f, c[2]/255.f, c[3]/255.f); } else if (c[0] / 255.f != mNewContext.color.mV[0] || c[1] / 255.f != mNewContext.color.mV[1] || c[2] / 255.f != mNewContext.color.mV[2] || c[3] / 255.f != mNewContext.color.mV[3] || mDirty) { flush(); mNewContext.color.mV[0] = c[0] / 255.f; mNewContext.color.mV[1] = c[1] / 255.f; mNewContext.color.mV[2] = c[2] / 255.f; mNewContext.color.mV[3] = c[3] / 255.f; } } void LLRender::diffuseColor4ub(U8 r, U8 g, U8 b, U8 a) { LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; llassert(!LLGLSLShader::sNoFixedFunction || shader != NULL); if (shader) { shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r/255.f, g/255.f, b/255.f, a/255.f); } else if (r / 255.f != mNewContext.color.mV[0] || g / 255.f != mNewContext.color.mV[1] || b / 255.f != mNewContext.color.mV[2] || a / 255.f != mNewContext.color.mV[3] || mDirty) { flush(); mNewContext.color.mV[0] = r / 255.f; mNewContext.color.mV[1] = g / 255.f; mNewContext.color.mV[2] = b / 255.f; mNewContext.color.mV[3] = a / 255.f; } } void LLRender::debugTexUnits(void) { LL_INFOS("TextureUnit") << "Active TexUnit: " << getCurrentTexUnitIndex() << LL_ENDL; std::string active_enabled = "false"; for (U32 i = 0; i < mTexUnits.size(); i++) { if (getTexUnit(i)->mCurrTexType != LLTexUnit::TT_NONE) { if (i == getCurrentTexUnitIndex()) 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_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; }