summaryrefslogtreecommitdiff
path: root/indra/newview/lltextureatlas.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/lltextureatlas.cpp')
-rw-r--r--indra/newview/lltextureatlas.cpp422
1 files changed, 422 insertions, 0 deletions
diff --git a/indra/newview/lltextureatlas.cpp b/indra/newview/lltextureatlas.cpp
new file mode 100644
index 0000000000..79b3686386
--- /dev/null
+++ b/indra/newview/lltextureatlas.cpp
@@ -0,0 +1,422 @@
+/**
+ * @file lltextureatlas.cpp
+ * @brief LLTextureAtlas class implementation.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlife.com/developers/opensource/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlife.com/developers/opensource/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#include "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+#include "llerror.h"
+#include "llimage.h"
+#include "llmath.h"
+#include "llgl.h"
+#include "llrender.h"
+#include "lltextureatlas.h"
+
+//-------------------
+S16 LLTextureAtlas::sMaxSubTextureSize = 64 ;
+S16 LLTextureAtlas::sSlotSize = 32 ;
+
+#ifndef DEBUG_ATLAS
+#define DEBUG_ATLAS 0
+#endif
+
+#ifndef DEBUG_USAGE_BITS
+#define DEBUG_USAGE_BITS 0
+#endif
+//**************************************************************************************************************
+LLTextureAtlas::LLTextureAtlas(U8 ncomponents, S16 atlas_dim) :
+ LLViewerTexture(atlas_dim * sSlotSize, atlas_dim * sSlotSize, ncomponents, TRUE),
+ mAtlasDim(atlas_dim),
+ mNumSlotsReserved(0),
+ mMaxSlotsInAtlas(atlas_dim * atlas_dim)
+{
+ generateEmptyUsageBits() ;
+
+ //generate an empty texture
+ generateGLTexture() ;
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw(mFullWidth, mFullHeight, mComponents);
+ createGLTexture(0, image_raw, 0);
+ image_raw = NULL;
+}
+
+LLTextureAtlas::~LLTextureAtlas()
+{
+ if(mSpatialGroupList.size() > 0)
+ {
+ llerrs << "Not clean up the spatial groups!" << llendl ;
+ }
+ releaseUsageBits() ;
+}
+
+//virtual
+S8 LLTextureAtlas::getType() const
+{
+ return LLViewerTexture::ATLAS_TEXTURE ;
+}
+
+void LLTextureAtlas::getTexCoordOffset(S16 col, S16 row, F32& xoffset, F32& yoffset)
+{
+ xoffset = (F32)col / mAtlasDim ;
+ yoffset = (F32)row / mAtlasDim ;
+}
+
+void LLTextureAtlas::getTexCoordScale(S32 w, S32 h, F32& xscale, F32& yscale)
+{
+ xscale = (F32)w / (mAtlasDim * sSlotSize) ;
+ yscale = (F32)h / (mAtlasDim * sSlotSize) ;
+}
+
+//insert a texture piece into the atlas
+LLGLuint LLTextureAtlas::insertSubTexture(LLImageGL* source_gl_tex, S32 discard_level, const LLImageRaw* raw_image, S16 slot_col, S16 slot_row)
+{
+ if(!getTexName())
+ {
+ return 0 ;
+ }
+
+ S32 w = raw_image->getWidth() ;
+ S32 h = raw_image->getHeight() ;
+ if(w < 8 || w > sMaxSubTextureSize || h < 8 || h > sMaxSubTextureSize)
+ {
+ //size overflow
+ return 0 ;
+ }
+
+ BOOL res = gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, getTexName());
+ if (!res)
+ {
+ llerrs << "bindTexture failed" << llendl;
+ }
+
+ GLint xoffset = sSlotSize * slot_col ;
+ GLint yoffset = sSlotSize * slot_row ;
+
+ if(!source_gl_tex->preAddToAtlas(discard_level, raw_image))
+ {
+ return 0 ;
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, TRUE);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, w, h,
+ mGLTexturep->getPrimaryFormat(), mGLTexturep->getFormatType(), raw_image->getData());
+
+ source_gl_tex->postAddToAtlas() ;
+ return getTexName();
+}
+
+//release a sub-texture slot from the atlas
+void LLTextureAtlas::releaseSlot(S16 slot_col, S16 slot_row, S8 slot_width)
+{
+ unmarkUsageBits(slot_width, slot_col, slot_row) ;
+ mNumSlotsReserved -= slot_width * slot_width ;
+}
+
+BOOL LLTextureAtlas::isEmpty() const
+{
+ return !mNumSlotsReserved ;
+}
+
+BOOL LLTextureAtlas::isFull(S8 to_be_reserved) const
+{
+ return mNumSlotsReserved + to_be_reserved > mMaxSlotsInAtlas ;
+}
+F32 LLTextureAtlas::getFullness() const
+{
+ return (F32)mNumSlotsReserved / mMaxSlotsInAtlas ;
+}
+
+void LLTextureAtlas::addSpatialGroup(LLSpatialGroup* groupp)
+{
+ if(groupp && !hasSpatialGroup(groupp))
+ {
+ mSpatialGroupList.push_back(groupp);
+ }
+}
+
+void LLTextureAtlas::removeSpatialGroup(LLSpatialGroup* groupp)
+{
+ if(groupp)
+ {
+ mSpatialGroupList.remove(groupp);
+ }
+}
+
+void LLTextureAtlas::clearSpatialGroup()
+{
+ mSpatialGroupList.clear();
+}
+void LLTextureAtlas::removeLastSpatialGroup()
+{
+ mSpatialGroupList.pop_back() ;
+}
+
+LLSpatialGroup* LLTextureAtlas::getLastSpatialGroup()
+{
+ if(mSpatialGroupList.size() > 0)
+ {
+ return mSpatialGroupList.back() ;
+ }
+ return NULL ;
+}
+
+BOOL LLTextureAtlas::hasSpatialGroup(LLSpatialGroup* groupp)
+{
+ for(std::list<LLSpatialGroup*>::iterator iter = mSpatialGroupList.begin(); iter != mSpatialGroupList.end() ; ++iter)
+ {
+ if(*iter == groupp)
+ {
+ return TRUE ;
+ }
+ }
+ return FALSE ;
+}
+
+//--------------------------------------------------------------------------------------
+//private
+void LLTextureAtlas::generateEmptyUsageBits()
+{
+ S32 col_len = (mAtlasDim + 7) >> 3 ;
+ mUsageBits = new U8*[mAtlasDim] ;
+ *mUsageBits = new U8[mAtlasDim * col_len] ;
+
+ mUsageBits[0] = *mUsageBits ;
+ for(S32 i = 1 ; i < mAtlasDim ; i++)
+ {
+ mUsageBits[i] = mUsageBits[i-1] + col_len ;
+
+ for(S32 j = 0 ; j < col_len ; j++)
+ {
+ //init by 0 for all bits.
+ mUsageBits[i][j] = 0 ;
+ }
+ }
+
+ //do not forget mUsageBits[0]!
+ for(S32 j = 0 ; j < col_len ; j++)
+ {
+ //init by 0 for all bits.
+ mUsageBits[0][j] = 0 ;
+ }
+
+ mTestBits = NULL ;
+#if DEBUG_USAGE_BITS
+ //------------
+ //test
+ mTestBits = new U8*[mAtlasDim] ;
+ *mTestBits = new U8[mAtlasDim * mAtlasDim] ;
+ mTestBits[0] = *mTestBits ;
+ for(S32 i = 1 ; i < mAtlasDim ; i++)
+ {
+ mTestBits[i] = mTestBits[i-1] + mAtlasDim ;
+
+ for(S32 j = 0 ; j < mAtlasDim ; j++)
+ {
+ //init by 0 for all bits.
+ mTestBits[i][j] = 0 ;
+ }
+ }
+
+ for(S32 j = 0 ; j < mAtlasDim ; j++)
+ {
+ //init by 0 for all bits.
+ mTestBits[0][j] = 0 ;
+ }
+#endif
+}
+
+void LLTextureAtlas::releaseUsageBits()
+{
+ if(mUsageBits)
+ {
+ delete[] *mUsageBits ;
+ delete[] mUsageBits ;
+ }
+ mUsageBits = NULL ;
+
+ //test
+ if( mTestBits)
+ {
+ delete[] *mTestBits;
+ delete[] mTestBits;
+ }
+ mTestBits = NULL ;
+}
+
+void LLTextureAtlas::markUsageBits(S8 bits_len, U8 mask, S16 col, S16 row)
+{
+ S16 x = col >> 3 ;
+
+ for(S8 i = 0 ; i < bits_len ; i++)
+ {
+ mUsageBits[row + i][x] |= mask ;
+ }
+
+#if DEBUG_USAGE_BITS
+ //test
+ for(S8 i = row ; i < row + bits_len ; i++)
+ {
+ for(S8 j = col ; j < col + bits_len ; j++)
+ {
+ mTestBits[i][j] = 1 ;
+ }
+ }
+#endif
+}
+
+void LLTextureAtlas::unmarkUsageBits(S8 bits_len, S16 col, S16 row)
+{
+ S16 x = col >> 3 ;
+ U8 mask = 1 ;
+ for(S8 i = 1 ; i < bits_len ; i++)
+ {
+ mask |= (1 << i) ;
+ }
+ mask <<= (col & 7) ;
+ mask = ~mask ;
+
+ for(S8 i = 0 ; i < bits_len ; i++)
+ {
+ mUsageBits[row + i][x] &= mask ;
+ }
+
+#if DEBUG_USAGE_BITS
+ //test
+ for(S8 i = row ; i < row + bits_len ; i++)
+ {
+ for(S8 j = col ; j < col + bits_len ; j++)
+ {
+ mTestBits[i][j] = 0 ;
+ }
+ }
+#endif
+}
+
+//return true if any of bits in the range marked.
+BOOL LLTextureAtlas::areUsageBitsMarked(S8 bits_len, U8 mask, S16 col, S16 row)
+{
+ BOOL ret = FALSE ;
+ S16 x = col >> 3 ;
+
+ for(S8 i = 0 ; i < bits_len ; i++)
+ {
+ if(mUsageBits[row + i][x] & mask)
+ {
+ ret = TRUE ;
+ break ;
+ //return TRUE ;
+ }
+ }
+
+#if DEBUG_USAGE_BITS
+ //test
+ BOOL ret2 = FALSE ;
+ for(S8 i = row ; i < row + bits_len ; i++)
+ {
+ for(S8 j = col ; j < col + bits_len ; j++)
+ {
+ if(mTestBits[i][j])
+ {
+ ret2 = TRUE ;
+ }
+ }
+ }
+
+ if(ret != ret2)
+ {
+ llerrs << "bits map corrupted." << llendl ;
+ }
+#endif
+ return ret ;//FALSE ;
+}
+
+//----------------------------------------------------------------------
+//
+//index order: Z order, i.e.:
+// |-----|-----|-----|-----|
+// | 10 | 11 | 14 | 15 |
+// |-----|-----|-----|-----|
+// | 8 | 9 | 12 | 13 |
+// |-----|-----|-----|-----|
+// | 2 | 3 | 6 | 7 |
+// |-----|-----|-----|-----|
+// | 0 | 1 | 4 | 5 |
+// |-----|-----|-----|-----|
+void LLTextureAtlas::getPositionFromIndex(S16 index, S16& col, S16& row)
+{
+ col = 0 ;
+ row = 0 ;
+
+ S16 index_copy = index ;
+ for(S16 i = 0 ; index_copy && i < 16 ; i += 2)
+ {
+ col |= ((index & (1 << i)) >> i) << (i >> 1) ;
+ row |= ((index & (1 << (i + 1))) >> (i + 1)) << (i >> 1) ;
+ index_copy >>= 2 ;
+ }
+}
+void LLTextureAtlas::getIndexFromPosition(S16 col, S16 row, S16& index)
+{
+ index = 0 ;
+ S16 col_copy = col ;
+ S16 row_copy = row ;
+ for(S16 i = 0 ; (col_copy || row_copy) && i < 16 ; i++)
+ {
+ index |= ((col & 1 << i) << i) | ((row & 1 << i) << ( i + 1)) ;
+ col_copy >>= 1 ;
+ row_copy >>= 1 ;
+ }
+}
+//----------------------------------------------------------------------
+//return TRUE if succeeds.
+BOOL LLTextureAtlas::getNextAvailableSlot(S8 bits_len, S16& col, S16& row)
+{
+ S16 index_step = bits_len * bits_len ;
+
+ U8 mask = 1 ;
+ for(S8 i = 1 ; i < bits_len ; i++)
+ {
+ mask |= (1 << i) ;
+ }
+
+ U8 cur_mask ;
+ for(S16 index = 0 ; index < mMaxSlotsInAtlas ; index += index_step)
+ {
+ getPositionFromIndex(index, col, row) ;
+
+ cur_mask = mask << (col & 7) ;
+ if(!areUsageBitsMarked(bits_len, cur_mask, col, row))
+ {
+ markUsageBits(bits_len, cur_mask, col, row) ;
+ mNumSlotsReserved += bits_len * bits_len ;
+
+ return TRUE ;
+ }
+ }
+
+ return FALSE ;
+}