phoenix-firestorm/indra/newview/lltextureatlas.cpp

423 lines
9.5 KiB
C++

/**
* @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 ;
}