You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ryzom-core/code/nel/src/3d/texture_far.cpp

2112 lines
52 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "std3d.h"
#include "nel/3d/texture_far.h"
#include "nel/3d/tile_far_bank.h"
#include "nel/3d/patch.h"
#include "nel/3d/tile_color.h"
#include "nel/3d/zone.h"
#include "nel/3d/landscape.h"
#include "nel/misc/system_info.h"
using namespace NLMISC;
using namespace NL3D;
using namespace std;
namespace NL3D {
CRGBA CTextureFar::_LightmapExpanded[NL_NUM_PIXELS_ON_FAR_TILE_EDGE*NL_MAX_TILES_BY_PATCH_EDGE*NL_NUM_PIXELS_ON_FAR_TILE_EDGE*NL_MAX_TILES_BY_PATCH_EDGE];
uint8 CTextureFar::_LumelExpanded[(NL_MAX_TILES_BY_PATCH_EDGE*NL_LUMEL_BY_TILE+1)*(NL_MAX_TILES_BY_PATCH_EDGE*NL_LUMEL_BY_TILE+1)];
CRGBA CTextureFar::_TileTLIColors[(NL_MAX_TILES_BY_PATCH_EDGE+1)*(NL_MAX_TILES_BY_PATCH_EDGE+1)];
// ***************************************************************************
CTextureFar::CTextureFar()
{
/* NB: define Values work only if NL_MAX_TILES_BY_PATCH_EDGE is 16.
Else must change NL_MAX_FAR_EDGE and NL_NUM_RECTANGLE_RATIO
*/
nlctassert(NL_MAX_TILES_BY_PATCH_EDGE==16);
// This texture is releasable. It doesn't stays in standard memory after been uploaded into video memory.
setReleasable (true);
// Init upload format 16 bits
setUploadFormat(RGB565);
// Set filter mode. No mipmap!
setFilterMode (Linear, LinearMipMapOff);
// Wrap
setWrapS (Clamp);
setWrapT (Clamp);
// init update Lighting
_ULPrec= this;
_ULNext= this;
// Start With All Patch of Max Far (64x64) Frees!
uint freeListId= getFreeListId(NL_MAX_FAR_PATCH_EDGE, NL_MAX_FAR_PATCH_EDGE);
for(uint i=0;i<NL_NUM_FAR_BIGGEST_PATCH_PER_EDGE;i++)
{
for(uint j=0;j<NL_NUM_FAR_BIGGEST_PATCH_PER_EDGE;j++)
{
CVector2s pos;
pos.x= i*NL_MAX_FAR_PATCH_EDGE;
pos.y= j*NL_MAX_FAR_PATCH_EDGE;
// add this place to the free list.
_FreeSpaces[freeListId].push_back(pos);
}
}
// reset
_ItULPatch= _PatchToPosMap.end();
}
// ***************************************************************************
CTextureFar::~CTextureFar()
{
// verify the textureFar is correctly unlinked from any ciruclar list.
nlassert(_ULPrec==this && _ULNext==this);
}
// ***************************************************************************
void CTextureFar::linkBeforeUL(CTextureFar *textNext)
{
nlassert(textNext);
// first, unlink others from me. NB: works even if _ULPrec==_ULNext==this.
_ULNext->_ULPrec= _ULPrec;
_ULPrec->_ULNext= _ULNext;
// link to igNext.
_ULNext= textNext;
_ULPrec= textNext->_ULPrec;
// link others to me.
_ULNext->_ULPrec= this;
_ULPrec->_ULNext= this;
}
// ***************************************************************************
void CTextureFar::unlinkUL()
{
// first, unlink others from me. NB: works even if _ULPrec==_ULNext==this.
_ULNext->_ULPrec= _ULPrec;
_ULPrec->_ULNext= _ULNext;
// reset
_ULPrec= this;
_ULNext= this;
}
// ***************************************************************************
uint CTextureFar::getFreeListId(uint width, uint height)
{
nlassert(width>=height);
nlassert(isPowerOf2(width));
nlassert(isPowerOf2(height));
nlassert(width<=NL_MAX_FAR_PATCH_EDGE);
// compute the level index
uint sizeIndex= getPowerOf2(NL_MAX_FAR_PATCH_EDGE / width);
nlassert(sizeIndex < NL_NUM_FAR_PATCH_EDGE_LEVEL);
// Compute the aspect ratio index.
uint aspectRatioIndex= getPowerOf2(width/height);
nlassert(aspectRatioIndex < NL_NUM_FAR_RECTANGLE_RATIO );
return sizeIndex*NL_NUM_FAR_RECTANGLE_RATIO + aspectRatioIndex;
}
// ***************************************************************************
bool CTextureFar::getUpperSize(uint &width, uint &height)
{
nlassert(width>=height);
nlassert(isPowerOf2(width));
nlassert(isPowerOf2(height));
// if height is smaller than widht, then reduce the ratio
if(width>height)
{
height*= 2;
return true;
}
else
{
// else raise up to the next square level, if possible
if(width<NL_MAX_FAR_PATCH_EDGE)
{
width*= 2;
height*= 2;
return true;
}
else
return false;
}
}
// ***************************************************************************
sint CTextureFar::tryAllocatePatch (CPatch *pPatch, uint farIndex)
{
// get the size of the subtexture to allocate
uint width=(pPatch->getOrderS ()*NL_NUM_PIXELS_ON_FAR_TILE_EDGE)>>(farIndex-1);
uint height=(pPatch->getOrderT ()*NL_NUM_PIXELS_ON_FAR_TILE_EDGE)>>(farIndex-1);
// make width the biggest
if(width<height)
std::swap(width, height);
// get where to find a subtexture
uint freeListId= getFreeListId(width, height);
// if some place, ok!
if(!_FreeSpaces[freeListId].empty())
return 0;
else
{
// try to get the next size
while( getUpperSize(width, height) )
{
freeListId= getFreeListId(width, height);
// if some subtexture free
if(!_FreeSpaces[freeListId].empty())
{
// Ok! return the size of this texture we must split
return width*height;
}
}
// fail => no more space => -1
return -1;
}
}
// ***************************************************************************
void CTextureFar::recursSplitNext(uint wson, uint hson)
{
// get the upper subTexture
uint wup= wson, hup= hson;
nlverify( getUpperSize(wup, hup) );
// get the list id.
uint fatherListId= getFreeListId(wup, hup);
// if must split bigger patch...
if(_FreeSpaces[fatherListId].empty())
{
// recurs, try to get a bigger subtexture and split it.
recursSplitNext(wup, hup);
}
// OK, now we should have a free entry.
nlassert( !_FreeSpaces[fatherListId].empty() );
// remove from free list, because it is split now!
CVector2s fatherPos= _FreeSpaces[fatherListId].front();
_FreeSpaces[fatherListId].pop_front();
// Create New free rectangles for sons
uint sonListId= getFreeListId(wson, hson);
CVector2s sonPos;
// if my son is a rectangle son
if(wson>hson)
{
// Then Add 2 free Spaces!
sonPos.x= fatherPos.x;
// 1st.
sonPos.y= fatherPos.y;
_FreeSpaces[sonListId].push_back(sonPos);
// 2nd.
sonPos.y= fatherPos.y+hson;
_FreeSpaces[sonListId].push_back(sonPos);
}
else
{
// Then Add 4 free Spaces!
// 1st.
sonPos.x= fatherPos.x;
sonPos.y= fatherPos.y;
_FreeSpaces[sonListId].push_back(sonPos);
// 2nd.
sonPos.x= fatherPos.x+wson;
sonPos.y= fatherPos.y;
_FreeSpaces[sonListId].push_back(sonPos);
// 3rd.
sonPos.x= fatherPos.x;
sonPos.y= fatherPos.y+hson;
_FreeSpaces[sonListId].push_back(sonPos);
// 4th.
sonPos.x= fatherPos.x+wson;
sonPos.y= fatherPos.y+hson;
_FreeSpaces[sonListId].push_back(sonPos);
}
}
// ***************************************************************************
void CTextureFar::allocatePatch (CPatch *pPatch, uint farIndex, float& farUScale, float& farVScale, float& farUBias, float& farVBias, bool& bRot)
{
// get the size of the subtexture to allocate
uint width=(pPatch->getOrderS ()*NL_NUM_PIXELS_ON_FAR_TILE_EDGE)>>(farIndex-1);
uint height=(pPatch->getOrderT ()*NL_NUM_PIXELS_ON_FAR_TILE_EDGE)>>(farIndex-1);
// make width the biggest
if(width<height)
std::swap(width, height);
// get where to find a subtexture
uint freeListId= getFreeListId(width, height);
// if free list is empty, must split bigger patch...
if(_FreeSpaces[freeListId].empty())
{
// try to get a bigger subtexture and split it.
recursSplitNext(width, height);
}
// now the list should have som free space.
nlassert( !_FreeSpaces[freeListId].empty() );
CVector2s pos= _FreeSpaces[freeListId].front();
// Allocate. Add this entry to the maps
CPatchIdent pid;
pid.Patch= pPatch;
pid.FarIndex= farIndex;
// verify not already here.
nlassert( _PatchToPosMap.find(pid) == _PatchToPosMap.end() );
_PatchToPosMap[pid]= pos;
_PosToPatchMap[pos]= pid;
// remove from free list.
_FreeSpaces[freeListId].pop_front();
// Invalidate the rectangle
CRect rect (pos.x, pos.y, width, height);
ITexture::touchRect (rect);
// ** Return some values
// Rotation flag
bRot = ( pPatch->getOrderS() < pPatch->getOrderT() );
// Scale is the same for all
farUScale=(float)(width-1)/(float)NL_FAR_TEXTURE_EDGE_SIZE;
farVScale=(float)(height-1)/(float)NL_FAR_TEXTURE_EDGE_SIZE;
// UBias is the same for all
farUBias=((float)pos.x+0.5f)/(float)NL_FAR_TEXTURE_EDGE_SIZE;
// UBias is the same for all
farVBias=((float)pos.y+0.5f)/(float)NL_FAR_TEXTURE_EDGE_SIZE;
}
// ***************************************************************************
// Remove a patch in the CTexture Patch
void CTextureFar::removePatch (CPatch *pPatch, uint farIndex)
{
// must be found
CPatchIdent pid;
pid.Patch= pPatch;
pid.FarIndex= farIndex;
TPatchToPosMap::iterator it= _PatchToPosMap.find(pid);
nlassert( it != _PatchToPosMap.end() );
// get the pos where this patch texture is stored
CVector2s pos= it->second;
// If I erase the patch wihch must next UL, then update UL
if( it == _ItULPatch )
_ItULPatch++;
// erase from the 1st map
_PatchToPosMap.erase(it);
// erase from the second map
_PosToPatchMap.erase(pos);
// Append to the free list.
uint width=(pPatch->getOrderS ()*NL_NUM_PIXELS_ON_FAR_TILE_EDGE)>>(farIndex-1);
uint height=(pPatch->getOrderT ()*NL_NUM_PIXELS_ON_FAR_TILE_EDGE)>>(farIndex-1);
if(width<height)
std::swap(width, height);
uint freeListId= getFreeListId(width, height);
_FreeSpaces[freeListId].push_back(pos);
}
// ***************************************************************************
uint CTextureFar::touchPatchULAndNext()
{
// if there is still a patch here
if( _ItULPatch!=_PatchToPosMap.end() )
{
// Position of the invalide rectangle
int x = _ItULPatch->second.x;
int y = _ItULPatch->second.y;
uint farIndex= _ItULPatch->first.FarIndex;
CPatch *pPatch= _ItULPatch->first.Patch;
// recompute the correct size.
uint width=(pPatch->getOrderS ()*NL_NUM_PIXELS_ON_FAR_TILE_EDGE)>>(farIndex-1);
uint height=(pPatch->getOrderT ()*NL_NUM_PIXELS_ON_FAR_TILE_EDGE)>>(farIndex-1);
if(width<height)
std::swap(width, height);
// Invalidate the associated rectangle
CRect rect (x, y, width, height);
ITexture::touchRect (rect);
// Go next.
_ItULPatch++;
// return number of pixels touched
return width * height;
}
else
{
// no touch
return 0;
}
}
// ***************************************************************************
void CTextureFar::startPatchULTouch()
{
_ItULPatch= _PatchToPosMap.begin();
}
// ***************************************************************************
bool CTextureFar::endPatchULTouch() const
{
return _ItULPatch == _PatchToPosMap.end();
}
// ***************************************************************************
// Generate the texture. See ITexture::doGenerate().
void CTextureFar::doGenerate (bool async)
{
// Resize. But don't need to fill with 0!!
CBitmap::resize (NL_FAR_TEXTURE_EDGE_SIZE, NL_FAR_TEXTURE_EDGE_SIZE, RGBA, false);
// Rectangle invalidate ?
if (_ListInvalidRect.begin()!=_ListInvalidRect.end())
{
// Yes, rebuild only those rectangles.
// For each rectangle to compute
std::list<NLMISC::CRect>::iterator ite=_ListInvalidRect.begin();
while (ite!=_ListInvalidRect.end())
{
// Get the PatchIdent.
CVector2s pos((uint16)ite->left(), (uint16)ite->top());
TPosToPatchMap::iterator itPosToPid= _PosToPatchMap.find( pos );
// If the patch is still here...
if( itPosToPid!=_PosToPatchMap.end() )
{
// ReBuild the rectangle.
rebuildPatch (pos, itPosToPid->second);
}
// Next rectangle
ite++;
}
}
else
{
// Parse all existing Patchs.
TPosToPatchMap::iterator itPosToPid= _PosToPatchMap.begin();
while( itPosToPid!= _PosToPatchMap.end() )
{
// ReBuild the rectangle.
rebuildPatch (itPosToPid->first, itPosToPid->second);
itPosToPid++;
}
}
}
// ***************************************************************************
// Rebuild the rectangle passed with coordinate passed in parameter
void CTextureFar::rebuildPatch (const CVector2s texturePos, const CPatchIdent &pid)
{
uint x= texturePos.x;
uint y= texturePos.y;
// Patch pointer
CPatch* patch= pid.Patch;
// Check it exists
nlassert (patch);
// get the order
uint nS=patch->getOrderS();
uint nT=patch->getOrderT();
// get the size of the texture to compute
uint subTextWidth=(nS*NL_NUM_PIXELS_ON_FAR_TILE_EDGE)>>(pid.FarIndex-1);
// Check it is a 16 bits texture
nlassert (getPixelFormat()==RGBA);
// Check pixels exist
nlassert (getPixels().size()!=0);
// Base offset of the first pixel of the patch's texture
uint nBaseOffset;
// Delta to add to the destination offset when walk for a pixel to the right in the source tile
sint dstDeltaX;
// Delta to add to the destination offset when walk for a pixel to the bottom in the source tile
sint dstDeltaY;
// larger than higher (regular)
if (nS>=nT)
{
// Regular offset, top left
nBaseOffset= x + y*_Width;
// Regular deltaX, to the right
dstDeltaX=1;
// Regular deltaY, to the bottom
dstDeltaY=_Width;
}
// higher than larger (goofy), the patch is stored with a rotation of 1 (to the left of course)
else
{
// Goofy offset, bottom left
nBaseOffset= x + y*_Width;
nBaseOffset+=(subTextWidth-1)*_Width;
// Goofy deltaX, to the top
dstDeltaX=-(sint)_Width;
// Goofy deltaY, to the right
dstDeltaY=1;
}
// Compute the order of the patch
CTileFarBank::TFarOrder orderX=CTileFarBank::order0;
uint tileSize=0;
switch (pid.FarIndex)
{
case 3:
// Ratio 1:4
orderX=CTileFarBank::order2;
tileSize=NL_NUM_PIXELS_ON_FAR_TILE_EDGE>>2;
break;
case 2:
// Ratio 1:2
orderX=CTileFarBank::order1;
tileSize=NL_NUM_PIXELS_ON_FAR_TILE_EDGE>>1;
break;
case 1:
// Ratio 1:1
orderX=CTileFarBank::order0;
tileSize=NL_NUM_PIXELS_ON_FAR_TILE_EDGE;
break;
default:
// no!: must be one of the previous values
nlassert (0);
}
// Must have a far tile bank pointer set in the CFarTexture
nlassert (_Bank);
// For all the tiles in the textures
sint nTileInPatch=0;
// ** Fill the struct for the tile fill method for each layers
NL3D_CComputeTileFar TileFar;
TileFar.SrcDiffusePixels = NULL;
TileFar.SrcAdditivePixels = NULL;
TileFar.SrcDeltaX = 0;
TileFar.SrcDeltaY = 0;
TileFar.AsmMMX= false;
#if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
TileFar.AsmMMX= NLMISC::CSystemInfo::hasMMX();
#endif
// Destination pointer
// Destination delta
TileFar.DstDeltaX=dstDeltaX;
TileFar.DstDeltaY=dstDeltaY;
// ** Build expand lightmap..
NL3D_CExpandLightmap lightMap;
// Fill the structure
lightMap.MulFactor=tileSize;
lightMap.ColorTile=&patch->TileColors[0];
lightMap.Width=nS+1;
lightMap.Height=nT+1;
lightMap.StaticLightColor=patch->getZone()->getLandscape()->getStaticLight();
lightMap.DstPixels=_LightmapExpanded;
// Compute current TLI colors.
patch->computeCurrentTLILightmapDiv2(_TileTLIColors);
lightMap.TLIColor= _TileTLIColors;
// Expand the shadowmap
patch->unpackShadowMap (_LumelExpanded);
lightMap.LumelTile=_LumelExpanded;
// Expand the patch lightmap now
NL3D_expandLightmap (&lightMap);
// DeltaY for lightmap
TileFar.SrcLightingDeltaY=nS*tileSize;
// Base Dst pointer on the tile line
uint nBaseDstTileLine=nBaseOffset;
for (uint t=0; t<nT; t++)
{
// Base Dst pointer on the tile
uint nBaseDstTilePixels=nBaseDstTileLine;
// For each tile of the line
for (uint s=0; s<nS; s++)
{
// Base pointer of the destination texture
TileFar.DstPixels=(CRGBA*)&(getPixels()[0])+nBaseDstTilePixels;
// Lightmap pointer
TileFar.SrcLightingPixels=_LightmapExpanded+(s*tileSize)+(t*nS*tileSize*tileSize);
// For each layer of the tile
for (sint l=0; l<3; l++)
{
// Use of additive in this layer ?
bool bAdditive=false;
// Size of the edge far tile
TileFar.Size=tileSize;
// Get a tile element reference for this tile.
const CTileElement &tileElm=patch->Tiles[nTileInPatch];
// Check for 256 tiles...
bool is256x256;
uint8 uvOff;
tileElm.getTile256Info(is256x256, uvOff);
// Get the tile number
sint tile=tileElm.Tile[l];
// Is the last layer ?
bool lastLayer = ( (l == 2) || (tileElm.Tile[l+1] == NL_TILE_ELM_LAYER_EMPTY) );
// Is an non-empty layer ?
if (tile!=NL_TILE_ELM_LAYER_EMPTY)
{
// Get the read only pointer on the far tile
const CTileFarBank::CTileFar* pTile=_Bank->getTile (tile);
// This pointer must not be null, else the farBank is not valid!
if (pTile==NULL)
nlwarning ("FarBank is not valid!");
// If the tile exist
if (pTile)
{
// Tile exist ?
if (pTile->isFill (CTileFarBank::diffuse))
{
// Get rotation of the tile in this layer
sint nRot=tileElm.getTileOrient(l);
// Source pointer
const CRGBA* pSrcDiffusePixels=pTile->getPixels (CTileFarBank::diffuse, orderX);
const CRGBA* pSrcAdditivePixels=NULL;
// Additive ?
if (pTile->isFill (CTileFarBank::additive))
{
// Use it
bAdditive=true;
// Get additive pointer
pSrcAdditivePixels=pTile->getPixels (CTileFarBank::additive, orderX);
}
// Source size
sint sourceSize;
// Source offset (for 256)
uint sourceOffset=0;
// 256 ?
if (is256x256)
{
// On the left ?
if (uvOff&0x02)
sourceOffset+=tileSize;
// On the bottom ?
if ((uvOff==1)||(uvOff==2))
sourceOffset+=2*tileSize*tileSize;
// Yes, 256
sourceSize=tileSize<<1;
}
else
{
// No, 128
sourceSize=tileSize;
}
// Compute offset and deltas
switch (nRot)
{
case 0:
// Source pointers
TileFar.SrcDiffusePixels=pSrcDiffusePixels+sourceOffset;
TileFar.SrcAdditivePixels=pSrcAdditivePixels+sourceOffset;
// Source delta
TileFar.SrcDeltaX=1;
TileFar.SrcDeltaY=sourceSize;
break;
case 1:
{
// Source pointers
uint newOffset=sourceOffset+(tileSize-1);
TileFar.SrcDiffusePixels=pSrcDiffusePixels+newOffset;
TileFar.SrcAdditivePixels=pSrcAdditivePixels+newOffset;
// Source delta
TileFar.SrcDeltaX=sourceSize;
TileFar.SrcDeltaY=-1;
}
break;
case 2:
{
// Destination pointer
uint newOffset=sourceOffset+(tileSize-1)*sourceSize+tileSize-1;
TileFar.SrcDiffusePixels=pSrcDiffusePixels+newOffset;
TileFar.SrcAdditivePixels=pSrcAdditivePixels+newOffset;
// Source delta
TileFar.SrcDeltaX=-1;
TileFar.SrcDeltaY=-sourceSize;
}
break;
case 3:
{
// Destination pointer
uint newOffset=sourceOffset+(tileSize-1)*sourceSize;
TileFar.SrcDiffusePixels=pSrcDiffusePixels+newOffset;
TileFar.SrcAdditivePixels=pSrcAdditivePixels+newOffset;
// Source delta
TileFar.SrcDeltaX=-sourceSize;
TileFar.SrcDeltaY=1;
}
break;
}
// *** Draw the layer
// Alpha layer ?
if (l>0)
{
// Additive layer ?
if (bAdditive && lastLayer)
NL3D_drawFarTileInFarTextureAdditiveAlpha (&TileFar);
else // No additive layer
NL3D_drawFarTileInFarTextureAlpha (&TileFar);
}
else // no alpha
{
// Additive layer ?
if (bAdditive && lastLayer)
NL3D_drawFarTileInFarTextureAdditive (&TileFar);
else // No additive layer
NL3D_drawFarTileInFarTexture (&TileFar);
}
}
}
}
else
// Stop, no more layer
break;
}
// Next tile
nTileInPatch++;
// Next tile on the line
nBaseDstTilePixels+=dstDeltaX*tileSize;
}
// Next line of tiles
nBaseDstTileLine+=dstDeltaY*tileSize;
}
}
} // NL3D
// ***************************************************************************
// ***************************************************************************
// NL3D_ExpandLightmap. C and Asm Part
// ***************************************************************************
// ***************************************************************************
#if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
// EMMS called not in __asm block.
# pragma warning (disable : 4799)
// ***************************************************************************
inline void NL3D_asmEndMMX()
{
__asm
{
// close MMX computation
emms
}
}
// ***************************************************************************
/** Expand a line of color with MMX.
* NB: start to write at pixel 1.
*/
inline void NL3D_asmExpandLineColor565(const uint16 *src, CRGBA *dst, uint du, uint len)
{
static uint64 blank = 0;
static uint64 cF800 = INT64_CONSTANT (0x0000F8000000F800);
static uint64 cE000 = INT64_CONSTANT (0x0000E0000000E000);
static uint64 c07E0 = INT64_CONSTANT (0x000007E0000007E0);
static uint64 c0600 = INT64_CONSTANT (0x0000060000000600);
static uint64 c001F = INT64_CONSTANT (0x0000001F0000001F);
static uint64 c001C = INT64_CONSTANT (0x0000001C0000001C);
if(len==0)
return;
// Loop for pix.
__asm
{
movq mm7, blank
// start at pixel 1 => increment dst, and start u= du
mov esi, src
mov edi, dst
add edi, 4
mov ecx, len
mov edx, du
// Loop
myLoop:
// Read 565 colors
//----------
// index u.
mov ebx, edx
shr ebx, 8
// pack the 2 colors in eax: // Hedx= color0, Ledx= color1
xor eax, eax // avoid partial stall.
mov ax, [esi + ebx*2]
shl eax, 16
mov ax, [esi + ebx*2 +2]
// store and unpack in mm2: Hmm2= color0, Lmm2= color1
movd mm2, eax
punpcklwd mm2, mm7
// reset accumulator mm3 to black
movq mm3, mm7
// Expand 565 to 888: color0 and color1 in parrallel
// R
movq mm0, mm2
movq mm1, mm2
pand mm0, cF800
pand mm1, cE000
psrld mm0, 8
psrld mm1, 13
por mm3, mm0
por mm3, mm1
// G
movq mm0, mm2
movq mm1, mm2
pand mm0, c07E0
pand mm1, c0600
pslld mm0, 5
psrld mm1, 1
por mm3, mm0
por mm3, mm1
// B
movq mm0, mm2
movq mm1, mm2
pand mm0, c001F
pand mm1, c001C
pslld mm0, 19
pslld mm1, 14
por mm3, mm0
por mm3, mm1
// unpack mm3 quad to mm0=color0 and mm1=color1.
movq mm0, mm3
movq mm1, mm3
psrlq mm0, 32
// Blend.
//----------
// blend factors
mov ebx, edx
mov eax, 256
and ebx, 0xFF
sub eax, ebx
movd mm2, ebx // mm2= factor
movd mm3, eax // mm3= 1-factor
// replicate to the 4 words.
punpckldq mm2, mm2 // mm2= 0000 00AA 0000 00AA
punpckldq mm3, mm3 // mm3= 0000 00AA 0000 00AA
packssdw mm2, mm2 // mm2= 00AA 00AA 00AA 00AA
packssdw mm3, mm3 // mm3= 00AA 00AA 00AA 00AA
// mul
punpcklbw mm0, mm7
punpcklbw mm1, mm7
pmullw mm0, mm3 // color0*(1-factor)
pmullw mm1, mm2 // color1*factor
// add, and unpack
paddusw mm0, mm1
psrlw mm0, 8
packuswb mm0, mm0
// store
movd [edi], mm0
// next pix
add edx, du
add edi, 4
dec ecx
jnz myLoop
}
}
// ***************************************************************************
/** Expand a line of color with MMX.
* NB: start to write at pixel 1.
*/
inline void NL3D_asmExpandLineColor8888(const CRGBA *src, CRGBA *dst, uint du, uint len)
{
static uint64 blank = 0;
if(len==0)
return;
// Loop for pix.
__asm
{
movq mm7, blank
// start at pixel 1 => increment dst, and start u= du
mov esi, src
mov edi, dst
add edi, 4
mov ecx, len
mov edx, du
// Loop
myLoop:
// Read 8888 colors
//----------
// index u.
mov ebx, edx
shr ebx, 8
// read the 2 colors: mm0= color0, mm1= color1
movd mm0 , [esi + ebx*4]
movd mm1 , [esi + ebx*4 + 4]
// Blend.
//----------
// blend factors
mov ebx, edx
mov eax, 256
and ebx, 0xFF
sub eax, ebx
movd mm2, ebx // mm2= factor
movd mm3, eax // mm3= 1-factor
// replicate to the 4 words.
punpckldq mm2, mm2 // mm2= 0000 00AA 0000 00AA
punpckldq mm3, mm3 // mm3= 0000 00AA 0000 00AA
packssdw mm2, mm2 // mm2= 00AA 00AA 00AA 00AA
packssdw mm3, mm3 // mm3= 00AA 00AA 00AA 00AA
// mul
punpcklbw mm0, mm7
punpcklbw mm1, mm7
pmullw mm0, mm3 // color0*(1-factor)
pmullw mm1, mm2 // color1*factor
// add, and unpack
paddusw mm0, mm1
psrlw mm0, 8
packuswb mm0, mm0
// store
movd [edi], mm0
// next pix
add edx, du
add edi, 4
dec ecx
jnz myLoop
}
}
// ***************************************************************************
/** Blend 2 lines of color into one line.
* NB: start at pix 0 here
*/
inline void NL3D_asmBlendLines(CRGBA *dst, const CRGBA *src0, const CRGBA *src1, uint index, uint len)
{
static uint64 blank = 0;
if(len==0)
return;
// Loop for pix.
__asm
{
movq mm7, blank
// read the factor and expand it to 4 words.
mov ebx, index
mov eax, 256
and ebx, 0xFF
sub eax, ebx
movd mm2, ebx // mm2= factor
movd mm3, eax // mm3= 1-factor
punpckldq mm2, mm2 // mm2= 0000 00AA 0000 00AA
punpckldq mm3, mm3 // mm3= 0000 00AA 0000 00AA
packssdw mm2, mm2 // mm2= 00AA 00AA 00AA 00AA
packssdw mm3, mm3 // mm3= 00AA 00AA 00AA 00AA
// setup ptrs
mov esi, src0
mov edx, src1
sub edx, esi // difference between 2 src
mov edi, dst
mov ecx, len
// Loop
myLoop:
// Read
movd mm0, [esi]
movd mm1, [esi+edx]
// mul
punpcklbw mm0, mm7
punpcklbw mm1, mm7
pmullw mm0, mm3 // color0*(1-factor)
pmullw mm1, mm2 // color1*factor
// add, and unpack
paddusw mm0, mm1
psrlw mm0, 8
packuswb mm0, mm0
// store
movd [edi], mm0
// next pix
add esi, 4
add edi, 4
dec ecx
jnz myLoop
}
}
// ***************************************************************************
/** Lightmap Combining for Far level 2 (farthest)
* Average 16 lumels, and deals with UserColor and TLI
*/
static void NL3D_asmAssembleShading1x1(const uint8 *lumels, const CRGBA *colorMap,
const CRGBA *srcTLIs, const CRGBA *srcUSCs, CRGBA *dst, uint lineWidth, uint nbTexel)
{
static uint64 blank = 0;
if(nbTexel==0)
return;
// local var
uint offsetTLIs= ((uint)srcTLIs-(uint)dst);
uint offsetUSCs= ((uint)srcUSCs-(uint)dst);
// Loop for pix.
__asm
{
movq mm7, blank
// setup ptrs
mov esi, lumels
mov edi, dst
mov ecx, nbTexel
// Loop
myLoop:
// Average shade part
//------------
mov ebx, colorMap
mov edx, lineWidth
// read and accumulate shade
xor eax,eax // avoid partial stall
// add with line 0
mov al, [esi + 0]
add al, [esi + 1]
adc ah, 0
add al, [esi + 2]
adc ah, 0
add al, [esi + 3]
adc ah, 0
// add with line 1
add al, [esi + edx + 0]
adc ah, 0
add al, [esi + edx + 1]
adc ah, 0
add al, [esi + edx + 2]
adc ah, 0
add al, [esi + edx + 3]
adc ah, 0
// add with line 2
add al, [esi + edx*2 + 0]
adc ah, 0
add al, [esi + edx*2 + 1]
adc ah, 0
add al, [esi + edx*2 + 2]
adc ah, 0
add al, [esi + edx*2 + 3]
adc ah, 0
// add with line 3
lea edx, [edx + edx*2]
add al, [esi + edx + 0]
adc ah, 0
add al, [esi + edx + 1]
adc ah, 0
add al, [esi + edx + 2]
adc ah, 0
add al, [esi + edx + 3]
adc ah, 0
// average
shr eax, 4
// convert to RGBA from the color Map
movd mm0, [ebx + eax*4]
// Assemble part
//------------
mov edx, offsetTLIs
mov ebx, offsetUSCs
// Add with TLI, and clamp.
paddusb mm0, [edi + edx]
// mul with USC
movd mm1, [edi + ebx]
punpcklbw mm0, mm7
punpcklbw mm1, mm7
pmullw mm0, mm1
// unpack
psrlw mm0, 7
packuswb mm0, mm0
// store
movd [edi], mm0
// next pix
add esi, 4 // skip 4 lumels
add edi, 4 // next texel
dec ecx
jnz myLoop
}
}
// ***************************************************************************
/** Lightmap Combining for Far level 1 (middle)
* Average 4 lumels, and deals with UserColor and TLI
*/
static void NL3D_asmAssembleShading2x2(const uint8 *lumels, const CRGBA *colorMap,
const CRGBA *srcTLIs, const CRGBA *srcUSCs, CRGBA *dst, uint lineWidth, uint nbTexel)
{
static uint64 blank = 0;
if(nbTexel==0)
return;
// local var
uint offsetTLIs= ((uint)srcTLIs-(uint)dst);
uint offsetUSCs= ((uint)srcUSCs-(uint)dst);
// Loop for pix.
__asm
{
movq mm7, blank
// setup ptrs
mov esi, lumels
mov edi, dst
mov ecx, nbTexel
// Loop
myLoop:
// Average shade part
//------------
mov ebx, colorMap
mov edx, lineWidth
// read and accumulate shade
xor eax,eax // avoid partial stall
mov al, [esi] // read lumel
// add with nbors
add al, [esi + 1]
adc ah, 0
add al, [esi + edx]
adc ah, 0
add al, [esi + edx + 1]
adc ah, 0
// average
shr eax, 2
// convert to RGBA from the color Map
movd mm0, [ebx + eax*4]
// Assemble part
//------------
mov edx, offsetTLIs
mov ebx, offsetUSCs
// Add with TLI, and clamp.
paddusb mm0, [edi + edx]
// mul with USC
movd mm1, [edi + ebx]
punpcklbw mm0, mm7
punpcklbw mm1, mm7
pmullw mm0, mm1
// unpack
psrlw mm0, 7
packuswb mm0, mm0
// store
movd [edi], mm0
// next pix
add esi, 2 // skip 2 lumels
add edi, 4 // next texel
dec ecx
jnz myLoop
}
}
// ***************************************************************************
# pragma warning (disable : 4731) // frame pointer register 'ebp' modified by inline assembly code
/** Lightmap Combining for Far level 0 (nearest)
* read 1 lumel, and deals with UserColor and TLI
*/
static void NL3D_asmAssembleShading4x4(const uint8 *lumels, const CRGBA *colorMap,
const CRGBA *srcTLIs, const CRGBA *srcUSCs, CRGBA *dst, uint nbTexel)
{
static uint64 blank = 0;
if(nbTexel==0)
return;
// Loop for pix.
__asm
{
// Use ebp as a register for faster access...
push ebp
movq mm7, blank
// setup ptrs
mov esi, lumels
mov edi, dst
mov edx, srcTLIs
sub edx, edi // difference src and dest
mov ebx, srcUSCs
sub ebx, edi // difference src and dest
mov ecx, nbTexel
// set ebp after reading locals...
mov ebp, colorMap
// Loop
myLoop:
// read shade RGBA into the color Map
xor eax,eax // avoid partial stall
mov al,[esi] // read lumel
movd mm0, [ebp + eax*4]
// Add with TLI, and clamp.
paddusb mm0, [edi + edx]
// mul with USC
movd mm1, [edi + ebx]
punpcklbw mm0, mm7
punpcklbw mm1, mm7
pmullw mm0, mm1
// unpack
psrlw mm0, 7
packuswb mm0, mm0
// store
movd [edi], mm0
// next pix
add esi, 1 // next lumel
add edi, 4 // next texel
dec ecx
jnz myLoop
// restore
pop ebp
}
}
# pragma warning (default : 4731) // frame pointer register 'ebp' modified by inline assembly code
#else // NL_OS_WINDOWS
// Dummy for non-windows platforms
inline void NL3D_asmEndMMX() {}
inline void NL3D_asmExpandLineColor565(const uint16 *src, CRGBA *dst, uint du, uint len) {}
inline void NL3D_asmExpandLineColor8888(const CRGBA *src, CRGBA *dst, uint du, uint len) {}
inline void NL3D_asmBlendLines(CRGBA *dst, const CRGBA *src0, const CRGBA *src1, uint index, uint len) {}
static void NL3D_asmAssembleShading1x1(const uint8 *lumels, const CRGBA *colorMap,
const CRGBA *srcTLIs, const CRGBA *srcUSCs, CRGBA *dst, uint lineWidth, uint nbTexel)
{
}
static void NL3D_asmAssembleShading2x2(const uint8 *lumels, const CRGBA *colorMap,
const CRGBA *srcTLIs, const CRGBA *srcUSCs, CRGBA *dst, uint lineWidth, uint nbTexel)
{
}
static void NL3D_asmAssembleShading4x4(const uint8 *lumels, const CRGBA *colorMap,
const CRGBA *srcTLIs, const CRGBA *srcUSCs, CRGBA *dst, uint nbTexel)
{
}
#endif // NL_OS_WINDOWS
// ***************************************************************************
extern "C" void NL3D_expandLightmap (const NL3D_CExpandLightmap* pLightmap)
{
bool asmMMX= false;
#if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
asmMMX= CSystemInfo::hasMMX();
// A CTileColor must be a 565 only.
nlassert(sizeof(CTileColor)==2);
#endif
// Expanded width
uint dstWidth=(pLightmap->Width-1)*pLightmap->MulFactor;
uint dstHeight=(pLightmap->Height-1)*pLightmap->MulFactor;
// *** First expand user color and TLI colors
// First pass, expand on U
static CRGBA expandedUserColorLine[ (NL_MAX_TILES_BY_PATCH_EDGE+1)*
(NL_MAX_TILES_BY_PATCH_EDGE+1)*NL_LUMEL_BY_TILE ];
static CRGBA expandedTLIColorLine[ (NL_MAX_TILES_BY_PATCH_EDGE+1)*
(NL_MAX_TILES_BY_PATCH_EDGE+1)*NL_LUMEL_BY_TILE ];
// Second pass, expand on V.
static CRGBA expandedUserColor[ (NL_MAX_TILES_BY_PATCH_EDGE+1)*NL_LUMEL_BY_TILE *
(NL_MAX_TILES_BY_PATCH_EDGE+1)*NL_LUMEL_BY_TILE ];
static CRGBA expandedTLIColor[ (NL_MAX_TILES_BY_PATCH_EDGE+1)*NL_LUMEL_BY_TILE *
(NL_MAX_TILES_BY_PATCH_EDGE+1)*NL_LUMEL_BY_TILE ];
// ** Expand on U
//=========
uint u, v;
// Expansion factor
uint expandFactor=((pLightmap->Width-1)<<8)/(dstWidth-1);
// Destination pointer
CRGBA *expandedUserColorLinePtr= expandedUserColorLine;
CRGBA *expandedTLIColorLinePtr= expandedTLIColorLine;
// Source pointer
const NL3D::CTileColor *colorTilePtr=pLightmap->ColorTile;
const NLMISC::CRGBA *colorTLIPtr= pLightmap->TLIColor;
// Go for U
for (v=0; v<pLightmap->Height; v++)
{
// First pixel
expandedUserColorLinePtr[0].set565 (colorTilePtr[0].Color565);
expandedTLIColorLinePtr[0]= colorTLIPtr[0];
// MMX implementation.
//-------------
if(asmMMX)
{
NL3D_asmExpandLineColor565(&colorTilePtr->Color565, expandedUserColorLinePtr, expandFactor, dstWidth-2);
NL3D_asmExpandLineColor8888(colorTLIPtr, expandedTLIColorLinePtr, expandFactor, dstWidth-2);
}
// C implementation
//-------------
else
{
// Index next pixel
uint srcIndexPixel=expandFactor;
for (u=1; u<dstWidth-1; u++)
{
// Check
nlassert ( (u+v*dstWidth) < (sizeof(expandedUserColorLine)/sizeof(CRGBA)) );
// Color index
uint srcIndex=srcIndexPixel>>8;
//nlassert (srcIndex>=0); // uint => always >= 0
nlassert (srcIndex<pLightmap->Width-1);
// Compute current color
CRGBA color0;
CRGBA color1;
color0.A = 255;
color0.set565 (colorTilePtr[srcIndex].Color565);
color1.A = 255;
color1.set565 (colorTilePtr[srcIndex+1].Color565);
expandedUserColorLinePtr[u].blendFromui (color0, color1, srcIndexPixel&0xff);
// Compute current TLI color
color0= colorTLIPtr[srcIndex];
color1= colorTLIPtr[srcIndex+1];
expandedTLIColorLinePtr[u].blendFromui (color0, color1, srcIndexPixel&0xff);
// Next index
srcIndexPixel+=expandFactor;
}
}
// Last pixel
expandedUserColorLinePtr[dstWidth-1].set565 (colorTilePtr[pLightmap->Width-1].Color565);
expandedTLIColorLinePtr[dstWidth-1]= colorTLIPtr[pLightmap->Width-1];
// Next line
expandedUserColorLinePtr+= dstWidth;
expandedTLIColorLinePtr+= dstWidth;
colorTilePtr+=pLightmap->Width;
colorTLIPtr+=pLightmap->Width;
}
// stop MMX if used
if(asmMMX)
NL3D_asmEndMMX();
// ** Expand on V
//=========
// Expansion factor
expandFactor=((pLightmap->Height-1)<<8)/(dstHeight-1);
// Destination pointer
CRGBA *expandedUserColorPtr= expandedUserColor;
CRGBA *expandedTLIColorPtr= expandedTLIColor;
// Src pointer
expandedUserColorLinePtr= expandedUserColorLine;
expandedTLIColorLinePtr= expandedTLIColorLine;
// Copy first row
memcpy(expandedUserColorPtr, expandedUserColorLinePtr, dstWidth*sizeof(CRGBA));
memcpy(expandedTLIColorPtr, expandedTLIColorLinePtr, dstWidth*sizeof(CRGBA));
// Next line
expandedUserColorPtr+=dstWidth;
expandedTLIColorPtr+=dstWidth;
// Index next pixel
uint indexPixel=expandFactor;
// Go for V
for (v=1; v<dstHeight-1; v++)
{
// Color index
uint index=indexPixel>>8;
// Source pointer
CRGBA *colorTilePtr0= expandedUserColorLine + index*dstWidth;
CRGBA *colorTilePtr1= expandedUserColorLine + (index+1)*dstWidth;
CRGBA *colorTLIPtr0= expandedTLIColorLine + index*dstWidth;
CRGBA *colorTLIPtr1= expandedTLIColorLine + (index+1)*dstWidth;
// MMX implementation.
//-------------
if(asmMMX)
{
NL3D_asmBlendLines(expandedUserColorPtr, colorTilePtr0, colorTilePtr1, indexPixel, dstWidth);
NL3D_asmBlendLines(expandedTLIColorPtr, colorTLIPtr0, colorTLIPtr1, indexPixel, dstWidth);
}
// C implementation
//-------------
else
{
// Copy the row
for (u=0; u<dstWidth; u++)
{
expandedUserColorPtr[u].blendFromui (colorTilePtr0[u], colorTilePtr1[u], indexPixel&0xff);
expandedTLIColorPtr[u].blendFromui (colorTLIPtr0[u], colorTLIPtr1[u], indexPixel&0xff);
}
}
// Next index
indexPixel+=expandFactor;
// Next line
expandedUserColorPtr+=dstWidth;
expandedTLIColorPtr+=dstWidth;
}
// stop MMX if used
if(asmMMX)
NL3D_asmEndMMX();
// Last row
// Destination pointer
expandedUserColorPtr= expandedUserColor + dstWidth*(dstHeight-1);
expandedTLIColorPtr= expandedTLIColor + dstWidth*(dstHeight-1);
// Src pointer
expandedUserColorLinePtr= expandedUserColorLine + dstWidth*(pLightmap->Height-1);
expandedTLIColorLinePtr= expandedTLIColorLine + dstWidth*(pLightmap->Height-1);
// Copy last row
memcpy(expandedUserColorPtr, expandedUserColorLinePtr, dstWidth*sizeof(CRGBA));
memcpy(expandedTLIColorPtr, expandedTLIColorLinePtr, dstWidth*sizeof(CRGBA));
// *** Now combine with shading
//=========
// Switch to the optimal method for each expansion value
switch (pLightmap->MulFactor)
{
case 1:
{
// Make 4x4 -> 1x1 blend
CRGBA *lineUSCPtr= expandedUserColor;
CRGBA *lineTLIPtr= expandedTLIColor;
CRGBA *lineDestPtr=pLightmap->DstPixels;
const uint8 *lineLumelPtr=pLightmap->LumelTile;
uint lineWidth=dstWidth<<2;
uint lineWidthx2=lineWidth<<1;
uint lineWidthx3=lineWidthx2+lineWidth;
uint lineWidthx4=lineWidth<<2;
// For each line
for (v=0; v<dstHeight; v++)
{
// MMX implementation.
//-------------
if(asmMMX)
{
NL3D_asmAssembleShading1x1(lineLumelPtr, pLightmap->StaticLightColor, lineTLIPtr, lineUSCPtr, lineDestPtr,
lineWidth, dstWidth);
}
// C implementation
//-------------
else
{
// For each lumel block
for (u=0; u<dstWidth; u++)
{
// index
uint lumelIndex=u<<2;
// Shading is filtred
uint shading=
((uint)lineLumelPtr[lumelIndex]+(uint)lineLumelPtr[lumelIndex+1]+(uint)lineLumelPtr[lumelIndex+2]+(uint)lineLumelPtr[lumelIndex+3]
+(uint)lineLumelPtr[lumelIndex+lineWidth]+(uint)lineLumelPtr[lumelIndex+1+lineWidth]+(uint)lineLumelPtr[lumelIndex+2+lineWidth]+(uint)lineLumelPtr[lumelIndex+3+lineWidth]
+(uint)lineLumelPtr[lumelIndex+lineWidthx2]+(uint)lineLumelPtr[lumelIndex+1+lineWidthx2]+(uint)lineLumelPtr[lumelIndex+2+lineWidthx2]+(uint)lineLumelPtr[lumelIndex+3+lineWidthx2]
+(uint)lineLumelPtr[lumelIndex+lineWidthx3]+(uint)lineLumelPtr[lumelIndex+1+lineWidthx3]+(uint)lineLumelPtr[lumelIndex+2+lineWidthx3]+(uint)lineLumelPtr[lumelIndex+3+lineWidthx3]
)>>4;
// Add shading with TLI color.
CRGBA col;
col.addRGBOnly(pLightmap->StaticLightColor[shading], lineTLIPtr[u]);
// Mul by the userColor
lineDestPtr[u].modulateFromColorRGBOnly(col, lineUSCPtr[u]);
lineDestPtr[u].R = min(((uint)lineDestPtr[u].R)*2, 255U);
lineDestPtr[u].G = min(((uint)lineDestPtr[u].G)*2, 255U);
lineDestPtr[u].B = min(((uint)lineDestPtr[u].B)*2, 255U);
}
}
// Next line
lineUSCPtr+=dstWidth;
lineTLIPtr+=dstWidth;
lineDestPtr+=dstWidth;
lineLumelPtr+=lineWidthx4;
}
break;
}
case 2:
{
// Make 2x2 -> 1x1 blend
CRGBA *lineUSCPtr= expandedUserColor;
CRGBA *lineTLIPtr= expandedTLIColor;
CRGBA *lineDestPtr=pLightmap->DstPixels;
const uint8 *lineLumelPtr=pLightmap->LumelTile;
uint lineWidth=dstWidth*2;
uint lineWidthx2=lineWidth<<1;
// For each line
for (v=0; v<dstHeight; v++)
{
// MMX implementation.
//-------------
if(asmMMX)
{
NL3D_asmAssembleShading2x2(lineLumelPtr, pLightmap->StaticLightColor, lineTLIPtr, lineUSCPtr, lineDestPtr,
lineWidth, dstWidth);
}
// C implementation
//-------------
else
{
// For each lumel block
for (u=0; u<dstWidth; u++)
{
// index
uint lumelIndex=u<<1;
// Shading is filtred
uint shading=
((uint)lineLumelPtr[lumelIndex]+(uint)lineLumelPtr[lumelIndex+1]+(uint)lineLumelPtr[lumelIndex+lineWidth]+(uint)lineLumelPtr[lumelIndex+1+lineWidth])>>2;
// Add shading with TLI color.
CRGBA col;
col.addRGBOnly(pLightmap->StaticLightColor[shading], lineTLIPtr[u]);
// Mul by the userColor
lineDestPtr[u].modulateFromColorRGBOnly(col, lineUSCPtr[u]);
lineDestPtr[u].R = min(((uint)lineDestPtr[u].R)*2, 255U);
lineDestPtr[u].G = min(((uint)lineDestPtr[u].G)*2, 255U);
lineDestPtr[u].B = min(((uint)lineDestPtr[u].B)*2, 255U);
}
}
// Next line
lineUSCPtr+=dstWidth;
lineTLIPtr+=dstWidth;
lineDestPtr+=dstWidth;
lineLumelPtr+=lineWidthx2;
}
break;
}
case 4:
// Make copy
CRGBA *lineUSCPtr= expandedUserColor;
CRGBA *lineTLIPtr= expandedTLIColor;
CRGBA *lineDestPtr=pLightmap->DstPixels;
const uint8 *lineLumelPtr=pLightmap->LumelTile;
uint nbTexel=dstWidth*dstHeight;
// MMX implementation.
//-------------
if(asmMMX)
{
NL3D_asmAssembleShading4x4(lineLumelPtr, pLightmap->StaticLightColor, lineTLIPtr, lineUSCPtr, lineDestPtr,
nbTexel);
}
// C implementation
//-------------
else
{
// For each pixel
for (u=0; u<nbTexel; u++)
{
// Shading is filtred
uint shading=lineLumelPtr[u];
// Add shading with TLI color.
CRGBA col;
col.addRGBOnly(pLightmap->StaticLightColor[shading], lineTLIPtr[u]);
// Mul by the userColor
lineDestPtr[u].modulateFromColorRGBOnly(col, lineUSCPtr[u]);
lineDestPtr[u].R = min(((uint)lineDestPtr[u].R)*2, 255U);
lineDestPtr[u].G = min(((uint)lineDestPtr[u].G)*2, 255U);
lineDestPtr[u].B = min(((uint)lineDestPtr[u].B)*2, 255U);
}
}
break;
}
// stop MMX if used
if(asmMMX)
NL3D_asmEndMMX();
}
// ***************************************************************************
// ***************************************************************************
// NL3D_drawFarTileInFar*. C and Asm Part
// ***************************************************************************
// ***************************************************************************
#if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
// ***************************************************************************
inline void NL3D_asmModulateLineColors(CRGBA *dst, const CRGBA *src0, const CRGBA *src1,
uint len, uint src0DeltaX, uint dstDeltaX)
{
static uint64 blank= 0;
if(len==0)
return;
__asm
{
movq mm7, blank
mov esi, src0 // esi point to src Pixels
mov edx, src1 // edx point to src lighting pixels
mov edi, dst
mov ecx, len
// compute increments for esi and edi
mov eax, src0DeltaX
mov ebx, dstDeltaX
sal eax, 2
sal ebx, 2
myLoop:
// read colors
movd mm0, [esi]
movd mm1, [edx]
// mul mm0 and mm1
punpcklbw mm0, mm7
punpcklbw mm1, mm7
pmullw mm0, mm1
psrlw mm0, 8
// pack
packuswb mm0, mm0
// out
movd [edi], mm0
// increment
add esi, eax
add edi, ebx
add edx, 4
dec ecx
jnz myLoop
}
}
// ***************************************************************************
inline void NL3D_asmModulateAndBlendLineColors(CRGBA *dst, const CRGBA *src0, const CRGBA *src1,
uint len, uint src0DeltaX, uint dstDeltaX)
{
static uint64 blank= 0;
static uint64 one= INT64_CONSTANT (0x0100010001000100);
if(len==0)
return;
__asm
{
movq mm7, blank
movq mm6, one
mov esi, src0 // esi point to src Pixels
mov edx, src1 // edx point to src lighting pixels
mov edi, dst
mov ecx, len
// compute increments for esi and edi
mov eax, src0DeltaX
mov ebx, dstDeltaX
sal eax, 2
sal ebx, 2
myLoop:
// read colors
movd mm0, [esi]
movd mm1, [edx]
// save and unpack Alpha. NB: ABGR
movq mm2, mm0
psrld mm2, 24 // mm2= 0000 0000 0000 00AA
punpckldq mm2, mm2 // mm2= 0000 00AA 0000 00AA
packssdw mm2, mm2 // mm2= 00AA 00AA 00AA 00AA
// negate with 256.
movq mm3, mm6
psubusw mm3, mm2
// mul mm0 and mm1
punpcklbw mm0, mm7
punpcklbw mm1, mm7
pmullw mm0, mm1
psrlw mm0, 8
// Alpha Blend with mm3 and mm2
movd mm1, [edi] // read dest
punpcklbw mm1, mm7
pmullw mm0, mm2 // mm0= srcColor*A
pmullw mm1, mm3 // mm1= dstColor*(1-A)
// add and pack
paddusw mm0, mm1
psrlw mm0, 8
packuswb mm0, mm0
// out
movd [edi], mm0
// increment
add esi, eax
add edi, ebx
add edx, 4
dec ecx
jnz myLoop
}
}
#else // NL_OS_WINDOWS
// Dummy for non-windows platforms
inline void NL3D_asmModulateLineColors(CRGBA *dst, const CRGBA *src0, const CRGBA *src1,
uint len, uint src0DeltaX, uint dstDeltaX)
{
}
inline void NL3D_asmModulateAndBlendLineColors(CRGBA *dst, const CRGBA *src0, const CRGBA *src1,
uint len, uint src0DeltaX, uint dstDeltaX)
{
}
#endif
// ***************************************************************************
void NL3D_drawFarTileInFarTexture (const NL3D_CComputeTileFar* pTileFar)
{
// Pointer of the Src diffuse pixels
const CRGBA* pSrcPixels=pTileFar->SrcDiffusePixels;
// Pointer of the Dst pixels
const CRGBA* pSrcLightPixels=pTileFar->SrcLightingPixels;
// Pointer of the Dst pixels
CRGBA* pDstPixels=pTileFar->DstPixels;
// For each pixels
int x, y;
for (y=0; y<pTileFar->Size; y++)
{
// MMX implementation
//---------
if(pTileFar->AsmMMX)
{
NL3D_asmModulateLineColors(pDstPixels, pSrcPixels, pSrcLightPixels,
pTileFar->Size, pTileFar->SrcDeltaX, pTileFar->DstDeltaX);
}
// C Implementation.
//---------
else
{
// Pointer of the source line
const CRGBA* pSrcLine=pSrcPixels;
// Pointer of the source lighting line
const CRGBA* pSrcLightingLine=pSrcLightPixels;
// Pointer of the destination line
CRGBA* pDstLine=pDstPixels;
// For each pixels on the line
for (x=0; x<pTileFar->Size; x++)
{
// Read and write a pixel
pDstLine->R=(uint8)(((uint)pSrcLine->R*(uint)pSrcLightingLine->R)>>8);
pDstLine->G=(uint8)(((uint)pSrcLine->G*(uint)pSrcLightingLine->G)>>8);
pDstLine->B=(uint8)(((uint)pSrcLine->B*(uint)pSrcLightingLine->B)>>8);
// Next pixel
pSrcLine+=pTileFar->SrcDeltaX;
pSrcLightingLine++;
pDstLine+=pTileFar->DstDeltaX;
}
}
// Next line
pSrcPixels+=pTileFar->SrcDeltaY;
pSrcLightPixels+=pTileFar->SrcLightingDeltaY;
pDstPixels+=pTileFar->DstDeltaY;
}
// stop MMX if used
if(pTileFar->AsmMMX)
NL3D_asmEndMMX();
}
// ***************************************************************************
void NL3D_drawFarTileInFarTextureAlpha (const NL3D_CComputeTileFar* pTileFar)
{
// Pointer of the Src pixels
const CRGBA* pSrcPixels=pTileFar->SrcDiffusePixels;
// Pointer of the Dst pixels
const CRGBA* pSrcLightPixels=pTileFar->SrcLightingPixels;
// Pointer of the Dst pixels
CRGBA* pDstPixels=pTileFar->DstPixels;
// Fill the buffer with layer 0
int x, y;
for (y=0; y<pTileFar->Size; y++)
{
// MMX implementation
//---------
if(pTileFar->AsmMMX)
{
NL3D_asmModulateAndBlendLineColors(pDstPixels, pSrcPixels, pSrcLightPixels,
pTileFar->Size, pTileFar->SrcDeltaX, pTileFar->DstDeltaX);
}
// C Implementation.
//---------
else
{
// Pointer of the source line
const CRGBA* pSrcLine=pSrcPixels;
// Pointer of the source lighting line
const CRGBA* pSrcLightingLine=pSrcLightPixels;
// Pointer of the Dst pixels
CRGBA* pDstLine=pDstPixels;
// For each pixels on the line
for (x=0; x<pTileFar->Size; x++)
{
// Read and write a pixel
register uint alpha=pSrcLine->A;
register uint oneLessAlpha=255-pSrcLine->A;
pDstLine->R=(uint8)(((((uint)pSrcLine->R*(uint)pSrcLightingLine->R)>>8)*alpha+(uint)pDstLine->R*oneLessAlpha)>>8);
pDstLine->G=(uint8)(((((uint)pSrcLine->G*(uint)pSrcLightingLine->G)>>8)*alpha+(uint)pDstLine->G*oneLessAlpha)>>8);
pDstLine->B=(uint8)(((((uint)pSrcLine->B*(uint)pSrcLightingLine->B)>>8)*alpha+(uint)pDstLine->B*oneLessAlpha)>>8);
// Next pixel
pSrcLine+=pTileFar->SrcDeltaX;
pSrcLightingLine++;
pDstLine+=pTileFar->DstDeltaX;
}
}
// Next line
pSrcPixels+=pTileFar->SrcDeltaY;
pSrcLightPixels+=pTileFar->SrcLightingDeltaY;
pDstPixels+=pTileFar->DstDeltaY;
}
// stop MMX if used
if(pTileFar->AsmMMX)
NL3D_asmEndMMX();
}
// ***************************************************************************
// TODO: asm implementation of this function \\//
//#ifdef NL_NO_ASM
void NL3D_drawFarTileInFarTextureAdditive (const NL3D_CComputeTileFar* pTileFar)
{
// Pointer of the Src diffuse pixels
const CRGBA* pSrcPixels=pTileFar->SrcDiffusePixels;
// Pointer of the Src additive pixels
const CRGBA* pSrcAddPixels=pTileFar->SrcAdditivePixels;
// Pointer of the Dst pixels
const CRGBA* pSrcLightPixels=pTileFar->SrcLightingPixels;
// Pointer of the Dst pixels
CRGBA* pDstPixels=pTileFar->DstPixels;
// For each pixels
int x, y;
for (y=0; y<pTileFar->Size; y++)
{
// Pointer of the source line
const CRGBA* pSrcLine=pSrcPixels;
// Pointer of the source line
const CRGBA* pSrcAddLine=pSrcAddPixels;
// Pointer of the source lighting line
const CRGBA* pSrcLightingLine=pSrcLightPixels;
// Pointer of the destination line
CRGBA* pDstLine=pDstPixels;
// For each pixels on the line
for (x=0; x<pTileFar->Size; x++)
{
// Read and write a pixel
uint nTmp=(((uint)pSrcLine->R*(uint)pSrcLightingLine->R)>>8)+(uint)pSrcAddLine->R;
if (nTmp>255)
nTmp=255;
pDstLine->R=(uint8)nTmp;
nTmp=(((uint)pSrcLine->G*(uint)pSrcLightingLine->G)>>8)+(uint)pSrcAddLine->G;
if (nTmp>255)
nTmp=255;
pDstLine->G=(uint8)nTmp;
nTmp=(((uint)pSrcLine->B*(uint)pSrcLightingLine->B)>>8)+(uint)pSrcAddLine->B;
if (nTmp>255)
nTmp=255;
pDstLine->B=(uint8)nTmp;
// Next pixel
pSrcLine+=pTileFar->SrcDeltaX;
pSrcAddLine+=pTileFar->SrcDeltaX;
pSrcLightingLine++;
pDstLine+=pTileFar->DstDeltaX;
}
// Next line
pSrcPixels+=pTileFar->SrcDeltaY;
pSrcAddPixels+=pTileFar->SrcDeltaY;
pSrcLightPixels+=pTileFar->SrcLightingDeltaY;
pDstPixels+=pTileFar->DstDeltaY;
}
}
//#endif // NL_NO_ASM
// ***************************************************************************
// TODO: asm implementation of this function \\//
//#ifdef NL_NO_ASM
void NL3D_drawFarTileInFarTextureAdditiveAlpha (const NL3D_CComputeTileFar* pTileFar)
{
// Pointer of the Src pixels
const CRGBA* pSrcPixels=pTileFar->SrcDiffusePixels;
// Pointer of the Src pixels
const CRGBA* pSrcAddPixels=pTileFar->SrcAdditivePixels;
// Pointer of the Src pixels
const CRGBA* pSrcLightPixels=pTileFar->SrcLightingPixels;
// Pointer of the Dst pixels
CRGBA* pDstPixels=pTileFar->DstPixels;
// Fill the buffer with layer 0
int x, y;
for (y=0; y<pTileFar->Size; y++)
{
// Pointer of the source line
const CRGBA* pSrcLine=pSrcPixels;
// Pointer of the source line
const CRGBA* pSrcAddLine=pSrcAddPixels;
// Pointer of the source lighting line
const CRGBA* pSrcLightingLine=pSrcLightPixels;
// Pointer of the Dst pixels
CRGBA* pDstLine=pDstPixels;
// For each pixels on the line
for (x=0; x<pTileFar->Size; x++)
{
// Read and write a pixel
register uint alpha=pSrcLine->A;
register uint oneLessAlpha=255-pSrcLine->A;
// Read and write a pixel
uint nTmp=(((uint)pSrcLine->R*(uint)pSrcLightingLine->R)>>8)+(uint)pSrcAddLine->R;
if (nTmp>255)
nTmp=255;
pDstLine->R=(uint8)((nTmp*alpha+pDstLine->R*oneLessAlpha)>>8);
nTmp=(((uint)pSrcLine->G*(uint)pSrcLightingLine->G)>>8)+(uint)pSrcAddLine->G;
if (nTmp>255)
nTmp=255;
pDstLine->G=(uint8)((nTmp*alpha+pDstLine->G*oneLessAlpha)>>8);
nTmp=(((uint)pSrcLine->B*(uint)pSrcLightingLine->B)>>8)+(uint)pSrcAddLine->B;
if (nTmp>255)
nTmp=255;
pDstLine->B=(uint8)((nTmp*alpha+pDstLine->B*oneLessAlpha)>>8);
// Next pixel
pSrcLine+=pTileFar->SrcDeltaX;
pSrcAddLine+=pTileFar->SrcDeltaX;
pSrcLightingLine++;
pDstLine+=pTileFar->DstDeltaX;
}
// Next line
pSrcPixels+=pTileFar->SrcDeltaY;
pSrcAddPixels+=pTileFar->SrcDeltaY;
pSrcLightPixels+=pTileFar->SrcLightingDeltaY;
pDstPixels+=pTileFar->DstDeltaY;
}
}
//#endif // NL_NO_ASM