khanat-opennel-code/code/nel/src/gui/view_renderer.cpp

1887 lines
55 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 "nel/misc/path.h"
#include "nel/misc/file.h"
#include "nel/misc/uv.h"
#include "nel/misc/hierarchical_timer.h"
#include "nel/gui/view_renderer.h"
using namespace NLMISC;
using namespace std;
using namespace NL3D;
namespace NLGUI
{
CViewRenderer* CViewRenderer::instance = NULL;
NL3D::UDriver* CViewRenderer::driver = NULL;
NL3D::UTextContext* CViewRenderer::textcontext = NULL;
std::set< std::string >* CViewRenderer::hwCursors = NULL;
float CViewRenderer::hwCursorScale = 1.0f;
CViewRenderer::CViewRenderer()
{
nlassert( CViewRenderer::driver != NULL );
nlassert( CViewRenderer::textcontext != NULL );
nlassert( CViewRenderer::hwCursors != NULL );
setup();
}
CViewRenderer::~CViewRenderer()
{
for(uint i=0;i<VR_NUM_LAYER;i++)
{
delete _StringRBLayers[i];
_StringRBLayers[i]= NULL;
_EmptyLayer[i]= true;
}
}
CViewRenderer* CViewRenderer::getInstance()
{
if( instance == NULL )
instance = new CViewRenderer;
return instance;
}
/*
* setClipWindow : set the current clipping window
* (this window do not inherit properties from parent or whatever)
*/
void CViewRenderer::setClipWindow (sint32 x, sint32 y, sint32 w, sint32 h)
{
_ClipX = x;
_ClipY = y;
_ClipW = w;
_ClipH = h;
_XMin = (float)_ClipX * _OneOverScreenW;
_XMax = (float)(_ClipX+_ClipW) * _OneOverScreenW;
_YMin = (float)_ClipY * _OneOverScreenH;
_YMax = (float)(_ClipY+_ClipH) * _OneOverScreenH;
}
/*
* checkNewScreenSize
*/
void CViewRenderer::checkNewScreenSize ()
{
if (!driver) return;
uint32 w, h;
driver->getWindowSize (w, h);
// not minimized? change coords
if(w!=0 && h!=0)
{
_IsMinimized= false;
_ScreenW = w;
_ScreenH = h;
if(_ScreenW>0)
_OneOverScreenW = 1.0f / (float)_ScreenW;
else
_OneOverScreenW = 1000;
if(_ScreenH>0)
_OneOverScreenH = 1.0f / (float)_ScreenH;
else
_OneOverScreenH = 1000;
}
else
{
// Keep old coordinates (suppose resolution won't change, even if typically false wen we swithch from outgame to ingame)
_IsMinimized= true;
}
}
/*
* getScreenSize : get the screen window size
*/
void CViewRenderer::getScreenSize (uint32 &w, uint32 &h)
{
w = _ScreenW;
h = _ScreenH;
}
/*
* get OOW / OOH
*/
void CViewRenderer::getScreenOOSize (float &oow, float &ooh)
{
oow= _OneOverScreenW;
ooh= _OneOverScreenH;
}
void CViewRenderer::setup()
{
_ClipX = _ClipY = 0;
_ClipW = 800;
_ClipH = 600;
_ScreenW = 800;
_ScreenH = 600;
_OneOverScreenW= 1.0f / (float)_ScreenW;
_OneOverScreenH= 1.0f / (float)_ScreenH;
_IsMinimized= false;
_WFigurTexture= 0;
_HFigurTexture= 0;
_WFigurSeparatorTexture = 0;
_FigurSeparatorTextureId = -1;
_FigurBlankId = -1;
_BlankId = -1;
_WorldSpaceTransformation = true;
_CurrentZ = 10;
for(uint i=0;i<VR_NUM_LAYER;i++)
{
_StringRBLayers[i]= NULL;
_EmptyLayer[i]= true;
}
_BlankGlobalTexture = NULL;
}
/*
* init: init material and string buffer
*/
void CViewRenderer::init()
{
if (!driver) return;
_Material = driver->createMaterial();
setRenderStates();
// Init all renderBuffer
for(uint i=0;i<VR_NUM_LAYER;i++)
{
_StringRBLayers[i]= textcontext->createRenderBuffer();
}
}
void CViewRenderer::setRenderStates()
{
_Material.setDoubleSided();
_Material.setZWrite(false);
_Material.setZFunc(UMaterial::always);
_Material.setBlend (true);
_Material.setBlendFunc (NL3D::UMaterial::srcalpha, NL3D::UMaterial::invsrcalpha);
_Material.setColor(CRGBA::White);
_Material.setTexture(0, NULL);
_Material.setTexture(1, NULL);
_Material.setTexture(2, NULL);
_Material.setTexture(3, NULL);
_Material.setZBias(0);
}
void CViewRenderer::release()
{
if( instance != NULL )
{
instance->reset();
delete instance;
instance = NULL;
}
}
/*
* reset: reset the whole view renderer
*/
void CViewRenderer::reset()
{
TGlobalTextureList::iterator ite = _GlobalTextures.begin();
while (ite != _GlobalTextures.end())
{
UTextureFile *tf = dynamic_cast<NL3D::UTextureFile *>(ite->Texture);
if (tf)
{
driver->deleteTextureFile (tf);
}
ite++;
}
_GlobalTextures.clear();
_SImages.clear();
_SImageIterators.clear();
_TextureMap.clear();
_IndexesToTextureIds.clear();
}
NL3D::UDriver* CViewRenderer::getDriver(){
return driver;
}
void CViewRenderer::setTextContext(NL3D::UTextContext *textcontext)
{
CViewRenderer::textcontext = textcontext;
}
void CViewRenderer::setDriver( NL3D::UDriver *driver )
{
CViewRenderer::driver = driver;
}
// ***************************************************************************
void CViewRenderer::SImage::setupQuadUV(bool flipv, uint8 rot, CQuadColorUV &dest)
{
nlassert(rot<=3);
// Rotation is CW and flip is along x axis
// Flip is vertical flip (this means we invert all y for a constant x)
// The transforms are done in this order : first apply the flip (or not) and then rotate
static const CUV UVTab[8][4] = {
{ CUV(0, 0), CUV(1, 0), CUV(1, 1), CUV(0, 1) }, // rot 0, no flip
{ CUV(1, 0), CUV(1, 1), CUV(0, 1), CUV(0, 0) }, // rot 1, no flip
{ CUV(1, 1), CUV(0, 1), CUV(0, 0), CUV(1, 0) }, // rot 2, no flip
{ CUV(0, 1), CUV(0, 0), CUV(1, 0), CUV(1, 1) }, // rot 3, no flip
{ CUV(1, 0), CUV(0, 0), CUV(0, 1), CUV(1, 1) }, // rot 0, flipped
{ CUV(0, 0), CUV(0, 1), CUV(1, 1), CUV(1, 0) }, // rot 1, flipped
{ CUV(0, 1), CUV(1, 1), CUV(1, 0), CUV(0, 0) }, // rot 2, flipped
{ CUV(1, 1), CUV(1, 0), CUV(0, 0), CUV(0, 1) } // rot 3, flipped
};
// Take care that the origin in the texture is top left so to get the texture in bottom-up
// we have to start at Max and go at Min. For left and right this is Min to Max.
float du = UVMax.U - UVMin.U;
float dv = UVMin.V - UVMax.V;
uint idx = flipv*4 + rot;
dest.Uv0 = CUV (UVMin.U + UVTab[idx][0].U * du, UVMax.V + UVTab[idx][0].V * dv);
dest.Uv1 = CUV (UVMin.U + UVTab[idx][1].U * du, UVMax.V + UVTab[idx][1].V * dv);
dest.Uv2 = CUV (UVMin.U + UVTab[idx][2].U * du, UVMax.V + UVTab[idx][2].V * dv);
dest.Uv3 = CUV (UVMin.U + UVTab[idx][3].U * du, UVMax.V + UVTab[idx][3].V * dv);
/* // TRAP : Unrolled Version (To be tested to know if it is faster than the previous one)
if (flipv)
{
switch (rot)
{
case 0:
qcoluv.Uv0.U = rI.UVMax.U; qcoluv.Uv0.V = rI.UVMax.V;
qcoluv.Uv1.U = rI.UVMin.U; qcoluv.Uv1.V = rI.UVMax.V;
qcoluv.Uv2.U = rI.UVMin.U; qcoluv.Uv2.V = rI.UVMin.V;
qcoluv.Uv3.U = rI.UVMax.U; qcoluv.Uv3.V = rI.UVMin.V;
break;
case 1:
qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMax.V;
qcoluv.Uv1.U = rI.UVMin.U; qcoluv.Uv1.V = rI.UVMin.V;
qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMin.V;
qcoluv.Uv3.U = rI.UVMax.U; qcoluv.Uv3.V = rI.UVMax.V;
break;
case 2:
qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMin.V;
qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMin.V;
qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMax.V;
qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMax.V;
break;
case 3:
qcoluv.Uv0.U = rI.UVMax.U; qcoluv.Uv0.V = rI.UVMin.V;
qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMax.V;
qcoluv.Uv2.U = rI.UVMin.U; qcoluv.Uv2.V = rI.UVMax.V;
qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMin.V;
break;
}
}
else
{
switch (rot)
{
case 0:
qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMax.V;
qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMax.V;
qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMin.V;
qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMin.V;
break;
case 1:
qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMin.V;
qcoluv.Uv1.U = rI.UVMin.U; qcoluv.Uv1.V = rI.UVMax.V;
qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMax.V;
qcoluv.Uv3.U = rI.UVMax.U; qcoluv.Uv3.V = rI.UVMin.V;
break;
case 2:
qcoluv.Uv0.U = rI.UVMax.U; qcoluv.Uv0.V = rI.UVMin.V;
qcoluv.Uv1.U = rI.UVMin.U; qcoluv.Uv1.V = rI.UVMin.V;
qcoluv.Uv2.U = rI.UVMin.U; qcoluv.Uv2.V = rI.UVMax.V;
qcoluv.Uv3.U = rI.UVMax.U; qcoluv.Uv3.V = rI.UVMax.V;
break;
case 3:
qcoluv.Uv0.U = rI.UVMax.U; qcoluv.Uv0.V = rI.UVMax.V;
qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMin.V;
qcoluv.Uv2.U = rI.UVMin.U; qcoluv.Uv2.V = rI.UVMin.V;
qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMax.V;
break;
}
}
*/
}
// ***************************************************************************
/*
* drawRotFlipBitmapTiled
*/
void CViewRenderer::drawRotFlipBitmapTiled (sint layerId, sint32 x, sint32 y, sint32 width, sint32 height, uint8 rot, bool flip,
sint32 nTxId, uint tileOrigin, const CRGBA &col)
{
static volatile bool draw = true;
if (!draw) return;
if (width <= 0 || height <= 0) return;
if (nTxId < 0) return;
// Is totally clipped ?
if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
(y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
return;
SImage &rI = *getSImage(nTxId);
sint32 txw, txh;
// start to draw at the reference corner
getTextureSizeFromId (nTxId, txw, txh);
if (rot > 3) rot = 3;
sint32 startX = x, startY = y;
sint32 stepX = txw, stepY = txh;
if (rot & 1)
{
std::swap(txw, txh);
}
// choose new start pos & uvs depending on the reference corner
// Along x axis
if (tileOrigin & 1) // right or left ?
{ // right
startX = x + width - txw;
stepX = -txw;
}
// Along y axis
if (tileOrigin & 2) // bottom or top ?
{ // top
startY = y + height - txh;
stepY = -txh;
}
// Fit screen coordinates
float fStartX = (float) startX * _OneOverScreenW;
float fStartY = (float) startY * _OneOverScreenH;
float fStepX = (float) stepX * _OneOverScreenW;
float fStepY = (float) stepY * _OneOverScreenH;
float fTxW = (float) txw * _OneOverScreenW;
float fTxH = (float) txh * _OneOverScreenH;
CQuadColorUV qcoluv;
qcoluv.Color0 = qcoluv.Color1 = qcoluv.Color2 = qcoluv.Color3 = col;
qcoluv.V0.z = qcoluv.V1.z = qcoluv.V2.z = qcoluv.V3.z = 0;
rI.setupQuadUV(flip,rot,qcoluv);
uint numTileX = (uint32)((width - 1) / txw);
uint numTileY = (uint32)((height- 1) / txh);
float currY = fStartY;
sint32 oldClipX = _ClipX;
sint32 oldClipY = _ClipY;
sint32 oldClipW = _ClipW;
sint32 oldClipH = _ClipH;
if (x < _ClipX) { width -= _ClipX - x; x = _ClipX; }
if (y < _ClipY) { height -= _ClipY - y; y = _ClipY; }
if ((x+width) > (_ClipX+_ClipW)) width -= (x+width) - (_ClipX+_ClipW);
if ((y+height) > (_ClipY+_ClipH)) height -= (y+height) - (_ClipY+_ClipH);
setClipWindow (x, y, width, height);
// draw result let the clipper clip the quads
for(uint py = 0; py <= numTileY; ++py)
{
float currX = fStartX;
for(uint px = 0; px <= numTileX; ++px)
{
/// There is room for speedup there
qcoluv.V0.x = currX;
qcoluv.V1.x = currX + fTxW;
qcoluv.V2.x = currX + fTxW;
qcoluv.V3.x = currX;
qcoluv.V0.y = currY;
qcoluv.V1.y = currY;
qcoluv.V2.y = currY + fTxH;
qcoluv.V3.y = currY + fTxH;
// Is NOT totally clipped ?
if ( !( (qcoluv.V0.x > _XMax) || (qcoluv.V2.x < _XMin) ||
(qcoluv.V0.y > _YMax) || (qcoluv.V2.y < _YMin) ) )
putQuadInLayer (*(rI.GlobalTexturePtr), layerId, qcoluv, rot);
currX += fStepX;
}
currY += fStepY;
}
setClipWindow (oldClipX, oldClipY, oldClipW, oldClipH);
}
/*
* drawBitmap
*/
void CViewRenderer::drawRotFlipBitmap (sint layerId, sint32 x, sint32 y, sint32 width, sint32 height,
uint8 rot, bool flipv, sint32 nTxId, const CRGBA &col)
{
if (width <= 0 || height <= 0) return;
if (nTxId < 0)
return;
float dstXmin, dstYmin, dstXmax, dstYmax;
// Is totally clipped ?
if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
(y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
return;
dstXmin = (float)(x) * _OneOverScreenW;
dstYmin = (float)(y) * _OneOverScreenH;
dstXmax = (float)(x + width) * _OneOverScreenW;
dstYmax = (float)(y + height) * _OneOverScreenH;
CQuadColorUV qcoluv;
qcoluv.V0.set (dstXmin, dstYmin, 0);
qcoluv.V1.set (dstXmax, dstYmin, 0);
qcoluv.V2.set (dstXmax, dstYmax, 0);
qcoluv.V3.set (dstXmin, dstYmax, 0);
qcoluv.Color0 = qcoluv.Color1 = qcoluv.Color2 = qcoluv.Color3 = col;
SImage &rI = *getSImage(nTxId);
// Avoid switch in common case
if (!flipv && !rot)
{
qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMax.V;
qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMax.V;
qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMin.V;
qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMin.V;
}
// else standard case
else
{
if (rot > 3)
rot = 3;
rI.setupQuadUV(flipv, rot, qcoluv);
}
static volatile bool doRot[4] = { true, true, true, true };
if (doRot[rot])
{
putQuadInLayer (*(rI.GlobalTexturePtr), layerId, qcoluv, rot);
}
}
/*
* draw11RotBitmap
* sTx must be lowered !!!
*/
void CViewRenderer::draw11RotFlipBitmap (sint layerId, sint32 x, sint32 y, uint8 rot, bool flipv, sint32 nTxId, const CRGBA &col)
{
if (nTxId < 0)
return;
sint32 txw, txh;
SImage &rImage = *getSImage(nTxId);
txw = (sint32)((rImage.UVMax.U - rImage.UVMin.U)*rImage.GlobalTexturePtr->Width+0.5f);
txh = (sint32)((rImage.UVMax.V - rImage.UVMin.V)*rImage.GlobalTexturePtr->Height+0.5f);
drawRotFlipBitmap (layerId, x, y, txw, txh, rot, flipv, nTxId, col);
}
inline void remapUV(CUV &dest, const CUV &src, const CUV &min, const CUV &max)
{
dest.set(src.U * (max.U - min.U) + min.U, src.V * (max.V - min.V) + min.V);
}
void CViewRenderer::drawQuad(sint layerId, const NLMISC::CQuadUV &quadUV, sint32 nTxId, NLMISC::CRGBA col /*=NLMISC::CRGBA(255,255,255,255)*/, bool additif, bool filtered)
{
nlassert(!(additif && !filtered)); // not implemented yet!
if (nTxId < 0)
return;
CQuadColorUV normedQuad;
//
normedQuad.V0.set(quadUV.V0.x * _OneOverScreenW, quadUV.V0.y * _OneOverScreenH, 0.f);
normedQuad.V1.set(quadUV.V1.x * _OneOverScreenW, quadUV.V1.y * _OneOverScreenH, 0.f);
normedQuad.V2.set(quadUV.V2.x * _OneOverScreenW, quadUV.V2.y * _OneOverScreenH, 0.f);
normedQuad.V3.set(quadUV.V3.x * _OneOverScreenW, quadUV.V3.y * _OneOverScreenH, 0.f);
//
float qXMin = minof(normedQuad.V0.x, normedQuad.V1.x, normedQuad.V2.x, normedQuad.V3.x);
if (qXMin > _XMax) return;
float qXMax = maxof(normedQuad.V0.x, normedQuad.V1.x, normedQuad.V2.x, normedQuad.V3.x);
if (qXMax < _XMin) return;
float qYMin = minof(normedQuad.V0.y, normedQuad.V1.y, normedQuad.V2.y, normedQuad.V3.y);
if (qYMin > _YMax) return;
float qYMax = maxof(normedQuad.V0.y, normedQuad.V1.y, normedQuad.V2.y, normedQuad.V3.y);
if (qYMax < _YMin) return;
//
SImage &rImage = *getSImage(nTxId);
SGlobalTexture &gt = *(rImage.GlobalTexturePtr);
CUV deltaUV(1.f / (float) gt.Width, 1.f / (float) gt.Height);
CUV cornerMin = rImage.UVMin + deltaUV;
CUV cornerMax = rImage.UVMax - deltaUV;
remapUV(normedQuad.Uv0, quadUV.Uv0, cornerMin, cornerMax);
remapUV(normedQuad.Uv1, quadUV.Uv1, cornerMin, cornerMax);
remapUV(normedQuad.Uv2, quadUV.Uv2, cornerMin, cornerMax);
remapUV(normedQuad.Uv3, quadUV.Uv3, cornerMin, cornerMax);
// test if clipping is required
if (qXMin >= _XMin && qYMin >= _YMin && qXMax <= _XMax && qYMax <= _YMax)
{
// not clipped, easy case
normedQuad.Color0 = normedQuad.Color1 = normedQuad.Color2 = normedQuad.Color3 = col;
if (_WorldSpaceTransformation)
{
worldSpaceTransformation (normedQuad);
}
layerId+= VR_BIAS_LAYER;
nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
CLayer &layer = rImage.GlobalTexturePtr->Layers[layerId];
if (!filtered)
{
if (layer.NbQuads == layer.Quads.size())
layer.Quads.push_back (normedQuad);
else
layer.Quads[layer.NbQuads] = normedQuad;
++ layer.NbQuads;
}
else if (additif) layer.FilteredAdditifQuads.push_back(normedQuad);
else layer.FilteredAlphaBlendedQuads.push_back(normedQuad);
_EmptyLayer[layerId]= false;
}
else
{
// Partially clipped (slowest case)
// Must do the clip manually
static const uint maxNumCorners = 8;
//
static CVector outPos0[maxNumCorners];
static CUV outUV0[maxNumCorners];
static CVector outPos1[maxNumCorners];
static CUV outUV1[maxNumCorners];
//
outUV0[0] = normedQuad.Uv0;
outUV0[1] = normedQuad.Uv1;
outUV0[2] = normedQuad.Uv2;
outUV0[3] = normedQuad.Uv3;
//
outPos0[0] = normedQuad.V0;
outPos0[1] = normedQuad.V1;
outPos0[2] = normedQuad.V2;
outPos0[3] = normedQuad.V3;
//
CVector *pPos0 = outPos0;
CVector *pPos1 = outPos1;
CUV *pUV0 = outUV0;
CUV *pUV1 = outUV1;
//
sint count = 4;
//
if (qXMin < _XMin)
{
// clip left
CPlane clipper(-1.f, 0.f, 0.f, _XMin);
count = clipper.clipPolygonBack(pPos0, pUV0, pPos1, pUV1, count);
std::swap(pPos0, pPos1);
std::swap(pUV0, pUV1);
}
if (qXMax > _XMax)
{
// clip right
CPlane clipper(1.f, 0.f, 0.f, -_XMax);
count = clipper.clipPolygonBack(pPos0, pUV0, pPos1, pUV1, count);
std::swap(pPos0, pPos1);
std::swap(pUV0, pUV1);
}
//
if (qYMin < _YMin)
{
// clip bottom
CPlane clipper(0.f, -1.f, 0.f, _YMin);
count = clipper.clipPolygonBack(pPos0, pUV0, pPos1, pUV1, count);
std::swap(pPos0, pPos1);
std::swap(pUV0, pUV1);
}
if (qYMax > _YMax)
{
// clip top
CPlane clipper(0.f, 1.f, 0.f, -_YMax);
count = clipper.clipPolygonBack(pPos0, pUV0, pPos1, pUV1, count);
std::swap(pPos0, pPos1);
std::swap(pUV0, pUV1);
}
nlassert(count <= (sint)maxNumCorners);
if (count >= 3)
{
count -= 2;
layerId+= VR_BIAS_LAYER;
nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
CLayer &layer = rImage.GlobalTexturePtr->Layers[layerId];
std::vector<NLMISC::CTriangleColorUV> *tris;
if (!filtered)
{
tris = &layer.Tris;
}
else
{
tris = additif ? &layer.FilteredAdditifTris : &layer.FilteredAlphaBlendedTris;
}
tris->resize(tris->size() + count);
CTriangleColorUV *lastTri = &tris->back() + 1;
CTriangleColorUV *currTri = lastTri - count;
const CVector *firstPos = pPos0++;
const CUV *firstUV = pUV0++;
do
{
currTri->V0 = *firstPos;
currTri->V1 = *pPos0;
currTri->V2 = *(pPos0 + 1);
currTri->Color0 = col;
currTri->Color1 = col;
currTri->Color2 = col;
currTri->Uv0 = *firstUV;
currTri->Uv1 = *pUV0;
currTri->Uv2 = *(pUV0 + 1);
pPos0 ++;
pUV0 ++;
++currTri;
}
while (currTri != lastTri);
_EmptyLayer[layerId]= false;
}
}
}
void CViewRenderer::drawUnclippedTriangles(sint layerId, const std::vector<NLMISC::CTriangle> &tris, NLMISC::CRGBA col)
{
if (tris.empty()) return;
if (!_BlankGlobalTexture) return;
// primary goal here is batching, so we prefer to draw the triangle with a blank texture rather than
// switching material and having to flush all primitives .
layerId+= VR_BIAS_LAYER;
nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
CLayer &layer = _BlankGlobalTexture->Layers[layerId];
uint startCount = (uint)layer.FilteredAlphaBlendedTris.size();
layer.FilteredAlphaBlendedTris.resize(startCount + tris.size());
const NLMISC::CTriangle *src =&tris[0];
const NLMISC::CTriangle *last = src + tris.size();
NLMISC::CTriangleColorUV *dest = &layer.FilteredAlphaBlendedTris[0] + startCount;
_EmptyLayer[layerId]= false;
do
{
dest->V0.set(src->V0.x * _OneOverScreenW, src->V0.y * _OneOverScreenH, 0.f);
dest->V1.set(src->V1.x * _OneOverScreenW, src->V1.y * _OneOverScreenH, 0.f);
dest->V2.set(src->V2.x * _OneOverScreenW, src->V2.y * _OneOverScreenH, 0.f);
static volatile bool testOpaque = false;
if (testOpaque)
{
dest->Color0 = CRGBA::White;
dest->Color1 = CRGBA::White;
dest->Color2 = CRGBA::White;
dest->Uv0.set(0.f, 0.f);
dest->Uv1.set(1.f, 0.f);
dest->Uv2.set(1.f, 1.f);
}
else
{
dest->Color0 = col;
dest->Color1 = col;
dest->Color2 = col;
dest->Uv0 = _BlankUV;
dest->Uv1 = _BlankUV;
dest->Uv2 = _BlankUV;
}
++ dest;
++ src;
}
while (src != last);
}
/*
* loadTextures
*/
bool CViewRenderer::loadTextures (const std::string &textureFileName, const std::string &uvFileName, bool uploadDXTC)
{
SGlobalTexture gt;
// Load texture file
string filename = CPath::lookup (textureFileName, false);
if (filename == "")
return false;
CIFile ifTmp;
if (ifTmp.open(filename))
CBitmap::loadSize (ifTmp, gt.Width, gt.Height);
gt.Texture = driver->createTextureFile (filename);
// Force to generate the texture now. This way we can extract the mouse bitmaps from it now without having to load it again.
// Its why we don't release it at the end, because it is likely to be uploaded soon)
CBitmap *texDatas = gt.Texture->generateDatas();
//
gt.Name = filename;
gt.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
if(uploadDXTC)
gt.Texture->setUploadFormat(UTexture::DXTC5);
// Load uv file
CIFile iFile;
filename = CPath::lookup (uvFileName, false);
if (filename == "")
return false;
if (!iFile.open(filename))
return false;
_GlobalTextures.push_back (gt);
driver->setCursorScale( CViewRenderer::hwCursorScale );
char bufTmp[256], tgaName[256];
string sTGAname;
float uvMinU, uvMinV, uvMaxU, uvMaxV;
while (!iFile.eof())
{
iFile.getline (bufTmp, 256);
sscanf (bufTmp, "%s %f %f %f %f", tgaName, &uvMinU, &uvMinV, &uvMaxU, &uvMaxV);
SImage image;
image.UVMin.U = uvMinU;
image.UVMin.V = uvMinV;
image.UVMax.U = uvMaxU;
image.UVMax.V = uvMaxV;
sTGAname = toLower(string(tgaName));
string::size_type stripPng = sTGAname.find(".png");
if (stripPng != string::npos)
{
sTGAname[stripPng + 1] = 't';
sTGAname[stripPng + 2] = 'g';
sTGAname[stripPng + 3] = 'a';
}
image.Name = sTGAname;
image.GlobalTexturePtr = &(_GlobalTextures.back());
if (getTextureIdFromName(sTGAname) != -1)
{
string tmp = string("duplicate texture name in ") + textureFileName + "(" + sTGAname + ")";
nlwarning(tmp.c_str());
}
else
{
sint32 textureId = addSImage(image);
//nlwarning("SIMAGE ADDED: id = %x, name = %s", textureId, image.Name.c_str());
// Insert in map.
_TextureMap.insert( make_pair(image.Name, textureId) );
}
// if this is a cursor texture, extract it now (supported for rgba only now, because of the blit)
if (texDatas && texDatas->getPixelFormat() == CBitmap::RGBA)
{
if( CViewRenderer::hwCursors->count( image.Name ) > 0 )
{
uint x0 = (uint) (image.UVMin.U * gt.Width);
uint y0 = (uint) (image.UVMin.V * gt.Height);
uint x1 = (uint) (image.UVMax.U * gt.Width);
uint y1 = (uint) (image.UVMax.V * gt.Height);
if (x1 != x0 && y1 != y0)
{
CBitmap curs;
curs.resize(x1 - x0, y1 - y0);
curs.blit(*texDatas, x0, y0, (x1 - x0), (y1 - y0), 0, 0);
driver->addCursor(image.Name, curs);
}
}
}
}
initIndexesToTextureIds ();
initSystemTextures();
initTypo();
return true;
}
void CViewRenderer::setExternalTexture(const std::string &sGlobalTextureName,
NL3D::UTexture *externalTexture,
uint32 externalTexWidth,
uint32 externalTexHeight,
uint32 defaultTexWidth,
uint32 defaultTexHeight
)
{
if (sGlobalTextureName.empty())
{
nlwarning("Can't create aglobal texture with an empty name");
return;
}
// Look if already existing
string sLwrGTName = strlwr(sGlobalTextureName);
TGlobalTextureList::iterator ite = _GlobalTextures.begin();
while (ite != _GlobalTextures.end())
{
std::string sText = strlwr(ite->Name);
if (sText == sLwrGTName)
break;
ite++;
}
if (ite == _GlobalTextures.end())
{
SGlobalTexture gtTmp;
gtTmp.FromGlobaleTexture = true;
gtTmp.Name = sLwrGTName;
_GlobalTextures.push_back(gtTmp);
ite = _GlobalTextures.end();
ite--;
}
ite->Width = externalTexWidth;
ite->Height = externalTexHeight;
ite->DefaultWidth = defaultTexWidth;
ite->DefaultHeight = defaultTexHeight;
ite->Texture = externalTexture;
}
/*
* createTexture
*/
sint32 CViewRenderer::createTexture (const std::string &sGlobalTextureName,
sint32 offsetX,
sint32 offsetY,
sint32 width,
sint32 height,
bool uploadDXTC,
bool bReleasable
)
{
if (sGlobalTextureName.empty()) return -1;
// Look if already existing
string sLwrGTName = strlwr(sGlobalTextureName);
TGlobalTextureList::iterator ite = _GlobalTextures.begin();
while (ite != _GlobalTextures.end())
{
std::string sText = strlwr(ite->Name);
if (sText == sLwrGTName)
break;
ite++;
}
// If global texture not exists create it
if (ite == _GlobalTextures.end())
{
SGlobalTexture gtTmp;
gtTmp.FromGlobaleTexture = false;
string filename = CPath::lookup (sLwrGTName, false);
if (filename == "") return -1;
CIFile ifTmp;
if (ifTmp.open(filename))
{
CBitmap::loadSize (ifTmp, gtTmp.Width, gtTmp.Height);
gtTmp.DefaultWidth = gtTmp.Width;
gtTmp.DefaultHeight = gtTmp.Height;
if (gtTmp.Width == 0 || gtTmp.Height == 0)
{
nlwarning("Failed to load the texture '%s', please check image format", filename.c_str());
}
}
gtTmp.Texture = driver->createTextureFile (sLwrGTName);
gtTmp.Name = sLwrGTName;
gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
if(uploadDXTC)
gtTmp.Texture->setUploadFormat(UTexture::DXTC5);
gtTmp.Texture->setReleasable(bReleasable);
_GlobalTextures.push_back(gtTmp);
ite = _GlobalTextures.end();
ite--;
}
// Add a texture with reference to the i th global texture
SImage iTmp;
// Set default parameters
if (width == -1)
width = ite->DefaultWidth;
if (height == -1)
height = ite->DefaultHeight;
iTmp.Name = sLwrGTName;
iTmp.GlobalTexturePtr = &(*ite);
iTmp.UVMin = CUV(((float)offsetX)/ite->Width , ((float)offsetY)/ite->Height);
iTmp.UVMax = CUV(((float)offsetX+width)/ite->Width , ((float)offsetY+height)/ite->Height);
sint32 TextID = addSImage(iTmp);
//nlwarning("SIMAGE ADDED: id = %d, name = %s", TextID, iTmp.Name.c_str());
// Insert / replace in map.
// TMP TMP FIX NICO
//_TextureMap.insert( make_pair(iTmp.Name, TextID) );
return TextID;
}
void CViewRenderer::updateTexturePos(const std::string &texturefileName, sint32 offsetX /*=0*/, sint32 offsetY /*=0*/, sint32 width /*=-1*/, sint32 height /*=-1*/)
{
sint32 id = getTextureIdFromName (texturefileName);
if (id == -1)
{
nlwarning("Unknwown texture %s, can't update pos", texturefileName.c_str());
return;
}
SImage *im = getSImage(id);
nlassert(im);
// Set default parameters
sint32 gw = im->GlobalTexturePtr->Width;
sint32 gh = im->GlobalTexturePtr->Height;
if (width == -1)
width = gw;
if (height == -1)
height = gh;
im->UVMin = CUV(((float)offsetX)/gw , ((float)offsetY)/gh);
im->UVMax = CUV(((float)offsetX+width)/gw, ((float)offsetY+height)/gh);
}
/*
* getGlobalTexture
*/
NL3D::UTexture *CViewRenderer::getGlobalTexture(const std::string &name)
{
string sLwrGTName = strlwr(name);
TGlobalTextureList::iterator ite = _GlobalTextures.begin();
while (ite != _GlobalTextures.end())
{
std::string sText = strlwr(ite->Name);
if (sText == sLwrGTName)
break;
ite++;
}
if (ite != _GlobalTextures.end())
{
return ite->Texture;
}
return NULL;
}
/*
* deleteTexture
*/
void CViewRenderer::deleteTexture (sint32 textureId)
{
// Checks
nlassert ((uint)textureId < _SImageIterators.size());
if (_SImageIterators[textureId] == _SImages.end())
{
nlwarning("Can't delete texture with name %s", getTextureNameFromId(textureId).c_str());
nlassert(0);
return;
}
// Backup global texture pointer
SGlobalTexture *gt = getSImage(textureId)->GlobalTexturePtr;
// Erase only texture from global texture
if (!(gt->FromGlobaleTexture))
{
//nlwarning("Removing texture with id %d", (int) textureId);
// Erase the SImage
//nlwarning("SIMAGE REMOVE : id = %x, name = %s", (int) textureId, getSImage(textureId)->Name.c_str());
removeSImage (textureId);
// Check if someone else use this global texture..
TSImageList::iterator ite = _SImages.begin();
while (ite != _SImages.end())
{
// Same global texture ?
if (ite->GlobalTexturePtr == gt)
break;
ite++;
}
// Global texture still used ?
if (ite == _SImages.end())
{
//nlwarning("REMOVE GLOBAL TEXTURE : id of simage = %x", (int) textureId);
// No, remove the global texture
for (TGlobalTextureList::iterator iteGT = _GlobalTextures.begin(); iteGT != _GlobalTextures.end(); iteGT++)
{
// This one ?
if (&(*iteGT) == gt)
{
// Remove this global texture
UTextureFile *tf = dynamic_cast<NL3D::UTextureFile *>(iteGT->Texture);
if (tf)
{
driver->deleteTextureFile (tf);
}
_GlobalTextures.erase (iteGT);
return;
}
}
// Global texture has not been found
nlstop;
}
}
}
/*
* getTextureIdFromName
*/
sint32 CViewRenderer::getTextureIdFromName (const string &sName) const
{
if(sName.empty())
return -1;
// convert to lowCase
string nameLwr = toLower(sName);
string::size_type stripPng = nameLwr.find(".png");
if (stripPng != string::npos)
{
nameLwr[stripPng + 1] = 't';
nameLwr[stripPng + 2] = 'g';
nameLwr[stripPng + 3] = 'a';
}
// Search in map
TTextureMap::const_iterator it= _TextureMap.find(nameLwr);
if( it==_TextureMap.end() )
return -1;
else
return it->second;
}
/*
* getTextureNameFromId
*/
std::string CViewRenderer::getTextureNameFromId (sint32 TxID)
{
if ((TxID < 0) || (TxID >= (sint32)_SImageIterators.size()))
return "";
SImage *img = getSImage(TxID);
return img->Name;
}
/*
* getTextureSizeFromName
*/
void CViewRenderer::getTextureSizeFromId (sint32 id, sint32 &width, sint32 &height)
{
if ((id < 0) || (id >= (sint32)_SImageIterators.size()))
{
width = height = 0;
}
else
{
SImage &rImage = *getSImage(id);
width = (sint32)((rImage.UVMax.U - rImage.UVMin.U)*rImage.GlobalTexturePtr->Width+0.5f);
height = (sint32)((rImage.UVMax.V - rImage.UVMin.V)*rImage.GlobalTexturePtr->Height+0.5f);
}
}
/*
* getTextureColor
*/
CRGBA CViewRenderer::getTextureColor(sint32 id, sint32 x, sint32 y)
{
if ((id < 0) || (id >= (sint32)_SImageIterators.size()))
{
return CRGBA(255,255,255);
}
SImage &rImage = *getSImage(id);
SGlobalTexture &rGT = *rImage.GlobalTexturePtr;
sint32 width, height;
width = (sint32)((rImage.UVMax.U - rImage.UVMin.U)*rGT.Width+0.5f);
height = (sint32)((rImage.UVMax.V - rImage.UVMin.V)*rGT.Height+0.5f);
float xRatio = ((float)x) / ((float)(width));
float yRatio = ((float)y) / ((float)(height));
UTexture *pTF = rGT.Texture;
sint32 xConv = (sint32)((rImage.UVMin.U + xRatio * (rImage.UVMax.U - rImage.UVMin.U))*rGT.Width+0.5f);
sint32 yConv = (rGT.Height-1)-(sint32)((rImage.UVMin.V + yRatio * (rImage.UVMax.V - rImage.UVMin.V))*rGT.Height+0.5f);
return pTF->getPixelColor(xConv, yConv);
}
// ***************************************************************************
sint32 CViewRenderer::getTypoTextureW(char c)
{
if ((c>=0) && (c<NumTypoChar))
return _TypoCharWs[(uint)c];
else
return 1;
}
// ***************************************************************************
sint32 CViewRenderer::getTypoTextureH(char /* c */)
{
return _TypoH;
}
// ***************************************************************************
sint32 CViewRenderer::getTypoTextureId(char c)
{
if ((c>=0) && (c<NumTypoChar))
return _TypoCharToTextureIds[(uint)c];
else
return -1;
}
/*
* flush
*/
void CViewRenderer::flush ()
{
H_AUTO ( RZ_Interface_ViewRenderer_flush )
// Run All layers.
for(uint layerId=0;layerId<VR_NUM_LAYER;layerId++)
{
if(_EmptyLayer[layerId])
continue;
// **** Run all Global Textures
TGlobalTextureList::iterator ite = _GlobalTextures.begin();
while (ite != _GlobalTextures.end())
{
// TMP TMP
// volatile SGlobalTexture *sg = &(*ite);
CLayer &layer= ite->Layers[layerId];
if(layer.NbQuads>0 || !layer.Tris.empty())
{
// setup the global texture to material
_Material.setTexture(0, ite->Texture);
// Special Case if _WorldSpaceTransformation and _WorldSpaceScale, enable bilinear
if(_WorldSpaceTransformation && _WorldSpaceScale)
ite->Texture->setFilterMode(UTexture::Linear, UTexture::LinearMipMapOff);
// draw quads and empty list
if (layer.NbQuads != 0)
{
driver->drawQuads (&(layer.Quads[0]), layer.NbQuads, _Material);
layer.NbQuads = 0;
}
if (!layer.Tris.empty())
{
driver->drawTriangles(layer.Tris, _Material);
layer.Tris.clear();
}
// Special Case if _WorldSpaceTransformation and _WorldSpaceScale, reset
if(_WorldSpaceTransformation && _WorldSpaceScale)
ite->Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
}
if (!layer.FilteredAlphaBlendedQuads.empty() ||
!layer.FilteredAlphaBlendedTris.empty() ||
!layer.FilteredAdditifQuads.empty() ||
!layer.FilteredAdditifTris.empty())
{
// setup the global texture to material
_Material.setTexture(0, ite->Texture);
// force filtering
ite->Texture->setFilterMode(UTexture::Linear, UTexture::LinearMipMapOff);
// alpha blended
if (!layer.FilteredAlphaBlendedQuads.empty())
{
driver->drawQuads (&(layer.FilteredAlphaBlendedQuads[0]), (uint32)layer.FilteredAlphaBlendedQuads.size(), _Material);
layer.FilteredAlphaBlendedQuads.clear();
}
if (!layer.FilteredAlphaBlendedTris.empty())
{
driver->drawTriangles(layer.FilteredAlphaBlendedTris, _Material);
layer.FilteredAlphaBlendedTris.clear();
}
// additif
if (!layer.FilteredAdditifQuads.empty() ||
!layer.FilteredAdditifTris.empty())
{
_Material.setBlendFunc (NL3D::UMaterial::one, NL3D::UMaterial::one);
if (!layer.FilteredAdditifQuads.empty())
{
driver->drawQuads (&(layer.FilteredAdditifQuads[0]), (uint32)layer.FilteredAdditifQuads.size(), _Material);
layer.FilteredAdditifQuads.clear();
}
if (!layer.FilteredAdditifTris.empty())
{
driver->drawTriangles(layer.FilteredAdditifTris, _Material);
layer.FilteredAdditifTris.clear();
}
// restore alpha blend
_Material.setBlendFunc (NL3D::UMaterial::srcalpha, NL3D::UMaterial::invsrcalpha);
}
ite->Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
}
ite++;
}
// **** Display Computed Strings of this layer
if (_WorldSpaceTransformation)
textcontext->flushRenderBufferUnProjected(_StringRBLayers[layerId], false);
else
textcontext->flushRenderBuffer(_StringRBLayers[layerId]);
// flushed
_EmptyLayer[layerId]= true;
}
}
/**
* init the map _IndexesToTextures
*/
void CViewRenderer::initIndexesToTextureIds()
{
char buf[20];
_IndexesToTextureIds.clear();
for (uint i = 0; i < 10; i++)
{
sprintf (buf, "numbers_%d.tga", i);
_IndexesToTextureIds.push_back (getTextureIdFromName(buf));
}
_FigurSeparatorTextureId = getTextureIdFromName("Numbers_sep.tga");
_FigurBlankId = getTextureIdFromName("numbers_blank.tga");
_BlankId = getTextureIdFromName("blank.tga");
SImage *blank = getSImage(_BlankId);
if (blank)
{
_BlankGlobalTexture = blank->GlobalTexturePtr;
_BlankUV = 0.5f * (blank->UVMin + blank->UVMax);
}
else
{
_BlankUV.set(0.f, 0.f);
}
// Init size
if(_IndexesToTextureIds[0]!=-1)
{
getTextureSizeFromId (_IndexesToTextureIds[0], _WFigurTexture, _HFigurTexture);
}
if (_FigurSeparatorTextureId != -1)
{
getTextureSizeFromId (_FigurSeparatorTextureId, _WFigurSeparatorTexture, _HFigurSeparatorTexture);
}
}
/**
*
*/
void CViewRenderer::initTypo()
{
_TypoH = 0;
// since filename dose not support special char (?,. ....), specify a map from char to string.
map<char, string> specialCharMap;
specialCharMap['?']= "question";
char buf[256];
// For all supported chars (if tga exist)
for (uint i = 0; i < NumTypoChar; i++)
{
// Get the token string for this char.
string token;
map<char, string>::iterator it= specialCharMap.find(i);
// General case
if(it==specialCharMap.end())
token= (char)i;
else
token= it->second;
// get the fileName
sprintf (buf, "typo_%s.tga", token.c_str());
sint32 id = getTextureIdFromName(buf);
if(id>=0)
{
_TypoCharToTextureIds[i]= id;
sint32 w,h;
getTextureSizeFromId (id, w, h);
_TypoCharWs[i]= w;
_TypoH = h;
}
else
{
_TypoCharToTextureIds[i]= -1;
// simulate a space.
_TypoCharWs[i]= 1;
}
}
}
/**
* needClipping
*/
bool CViewRenderer::needClipping (const CQuad &q)
{
if ((q.V0.x >= _XMin) && (q.V0.y >= _YMin) && (q.V2.x <= _XMax) && (q.V2.y <= _YMax))
return false;
else
return true;
}
/**
* clip
*/
void CViewRenderer::clip (CQuadColorUV &qout, const CQuadColorUV &qin, uint rot)
{
float ratio;
qout = qin;
if (rot & 1)
{
// must reverse U & V during clipping
if (qin.V0.x < _XMin)
{
ratio = ((float)(_XMin - qin.V0.x))/((float)(qin.V1.x - qin.V0.x));
qout.V3.x = qout.V0.x = _XMin;
qout.Uv0.V += ratio*(qin.Uv1.V-qin.Uv0.V);
qout.Uv3.V += ratio*(qin.Uv2.V-qin.Uv3.V);
}
if (qin.V0.y < _YMin)
{
ratio = ((float)(_YMin - qin.V0.y))/((float)(qin.V3.y - qin.V0.y));
qout.V1.y = qout.V0.y = _YMin;
qout.Uv0.U += ratio*(qin.Uv3.U-qin.Uv0.U);
qout.Uv1.U += ratio*(qin.Uv2.U-qin.Uv1.U);
}
if (qin.V2.x > _XMax)
{
ratio = ((float)(_XMax - qin.V2.x))/((float)(qin.V3.x - qin.V2.x));
qout.V2.x = qout.V1.x = _XMax;
qout.Uv2.V += ratio*(qin.Uv3.V-qin.Uv2.V);
qout.Uv1.V += ratio*(qin.Uv0.V-qin.Uv1.V);
}
if (qin.V2.y > _YMax)
{
ratio = ((float)(_YMax - qin.V2.y))/((float)(qin.V1.y - qin.V2.y));
qout.V2.y = qout.V3.y = _YMax;
qout.Uv2.U += ratio*(qin.Uv1.U-qin.Uv2.U);
qout.Uv3.U += ratio*(qin.Uv0.U-qin.Uv3.U);
}
}
else
{
if (qin.V0.x < _XMin)
{
ratio = ((float)(_XMin - qin.V0.x))/((float)(qin.V1.x - qin.V0.x));
qout.V3.x = qout.V0.x = _XMin;
qout.Uv0.U += ratio*(qin.Uv1.U-qin.Uv0.U);
qout.Uv3.U += ratio*(qin.Uv2.U-qin.Uv3.U);
}
if (qin.V0.y < _YMin)
{
ratio = ((float)(_YMin - qin.V0.y))/((float)(qin.V3.y - qin.V0.y));
qout.V1.y = qout.V0.y = _YMin;
qout.Uv0.V += ratio*(qin.Uv3.V-qin.Uv0.V);
qout.Uv1.V += ratio*(qin.Uv2.V-qin.Uv1.V);
}
if (qin.V2.x > _XMax)
{
ratio = ((float)(_XMax - qin.V2.x))/((float)(qin.V3.x - qin.V2.x));
qout.V2.x = qout.V1.x = _XMax;
qout.Uv2.U += ratio*(qin.Uv3.U-qin.Uv2.U);
qout.Uv1.U += ratio*(qin.Uv0.U-qin.Uv1.U);
}
if (qin.V2.y > _YMax)
{
ratio = ((float)(_YMax - qin.V2.y))/((float)(qin.V1.y - qin.V2.y));
qout.V2.y = qout.V3.y = _YMax;
qout.Uv2.V += ratio*(qin.Uv1.V-qin.Uv2.V);
qout.Uv3.V += ratio*(qin.Uv0.V-qin.Uv3.V);
}
}
}
/**
* clip with uv2
*/
void CViewRenderer::clip (CQuadColorUV2 &qout, const CQuadColorUV2 &qin)
{
float ratio;
qout = qin;
if (qin.V0.x < _XMin)
{
ratio = ((float)(_XMin - qin.V0.x))/((float)(qin.V1.x - qin.V0.x));
qout.V3.x = qout.V0.x = _XMin;
qout.Uv0.U += ratio*(qin.Uv1.U-qin.Uv0.U);
qout.Uv3.U += ratio*(qin.Uv2.U-qin.Uv3.U);
qout.Uv02.U += ratio*(qin.Uv12.U-qin.Uv02.U);
qout.Uv32.U += ratio*(qin.Uv22.U-qin.Uv32.U);
}
if (qin.V0.y < _YMin)
{
ratio = ((float)(_YMin - qin.V0.y))/((float)(qin.V3.y - qin.V0.y));
qout.V1.y = qout.V0.y = _YMin;
qout.Uv0.V += ratio*(qin.Uv3.V-qin.Uv0.V);
qout.Uv1.V += ratio*(qin.Uv2.V-qin.Uv1.V);
qout.Uv02.V += ratio*(qin.Uv32.V-qin.Uv02.V);
qout.Uv12.V += ratio*(qin.Uv22.V-qin.Uv12.V);
}
if (qin.V2.x > _XMax)
{
ratio = ((float)(_XMax - qin.V2.x))/((float)(qin.V3.x - qin.V2.x));
qout.V2.x = qout.V1.x = _XMax;
qout.Uv2.U += ratio*(qin.Uv3.U-qin.Uv2.U);
qout.Uv1.U += ratio*(qin.Uv0.U-qin.Uv1.U);
qout.Uv22.U += ratio*(qin.Uv32.U-qin.Uv22.U);
qout.Uv12.U += ratio*(qin.Uv02.U-qin.Uv12.U);
}
if (qin.V2.y > _YMax)
{
ratio = ((float)(_YMax - qin.V2.y))/((float)(qin.V1.y - qin.V2.y));
qout.V2.y = qout.V3.y = _YMax;
qout.Uv2.V += ratio*(qin.Uv1.V-qin.Uv2.V);
qout.Uv3.V += ratio*(qin.Uv0.V-qin.Uv3.V);
qout.Uv22.V += ratio*(qin.Uv12.V-qin.Uv22.V);
qout.Uv32.V += ratio*(qin.Uv02.V-qin.Uv32.V);
}
}
// ***************************************************************************
/**
* putQuadInLayer : put a quad in a specific layer of a specific texture
*/
void CViewRenderer::putQuadInLayer (SGlobalTexture &gt, sint layerId, const NLMISC::CQuadColorUV &qcoluv, uint rot)
{
layerId+= VR_BIAS_LAYER;
nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
CLayer &layer= gt.Layers[layerId];
// Clipping part
if (!needClipping(qcoluv))
{
// World space transformation
if (_WorldSpaceTransformation)
{
NLMISC::CQuadColorUV qcolor = qcoluv;
worldSpaceTransformation (qcolor);
// No need to clip the quad
if (layer.NbQuads == layer.Quads.size())
layer.Quads.push_back (qcolor);
else
layer.Quads[layer.NbQuads] = qcolor;
}
else
{
// No need to clip the quad
if (layer.NbQuads == layer.Quads.size())
layer.Quads.push_back (qcoluv);
else
layer.Quads[layer.NbQuads] = qcoluv;
}
++layer.NbQuads;
}
else
{
CQuadColorUV qclipped;
clip (qclipped, qcoluv, rot);
// World space transformation
if (_WorldSpaceTransformation)
worldSpaceTransformation (qclipped);
if (layer.NbQuads == layer.Quads.size())
layer.Quads.push_back (qclipped);
else
layer.Quads[layer.NbQuads] = qclipped;
++layer.NbQuads;
}
// layer filled
_EmptyLayer[layerId]= false;
}
// ***************************************************************************
void CViewRenderer::addSystemTexture(TSystemTexture e, const char *s)
{
_SystemTextures[e].Id= getTextureIdFromName(s);
if(_SystemTextures[e].Id!=-1)
{
getTextureSizeFromId(_SystemTextures[e].Id, _SystemTextures[e].W, _SystemTextures[e].H);
}
}
// ***************************************************************************
void CViewRenderer::initSystemTextures()
{
addSystemTexture(QuantityCrossTexture, "w_quantity.tga");
addSystemTexture(DefaultBrickTexture, "brick_default.tga");
addSystemTexture(DefaultItemTexture, "item_default.tga");
addSystemTexture(ItemPlanTexture, "item_plan_over.tga");
addSystemTexture(SkillTexture, "skill.tga");
addSystemTexture(ItemEnchantedTexture, "sapload.tga");
addSystemTexture(DragCopyTexture, "W_copy.tga");
addSystemTexture(ItemWornedTexture, "ico_task_failed.tga");
addSystemTexture(OutOfRangeTexture, "ico_out_of_range.tga");
addSystemTexture(RegenTexture, "regen.tga");
addSystemTexture(RegenBackTexture, "regen_back.tga");
addSystemTexture(GlowStarTexture, "glow_star_24.tga");
addSystemTexture(ItemLockedByOwnerTexture, "r2ed_toolbar_lock_small.tga");
}
// ***************************************************************************
URenderStringBuffer *CViewRenderer::getStringRenderBuffer(sint layerId)
{
layerId+= VR_BIAS_LAYER;
nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
return _StringRBLayers[layerId];
}
// ***************************************************************************
void CViewRenderer::drawWiredQuad(sint32 x, sint32 y, sint32 width, sint32 height, NLMISC::CRGBA col /*=NLMISC::CRGBA::White*/)
{
driver->drawWiredQuad(x * _OneOverScreenW, y * _OneOverScreenH, (x + width) * _OneOverScreenW, (y + height) * _OneOverScreenH, col);
}
// ***************************************************************************
void CViewRenderer::drawFilledQuad(sint32 x, sint32 y, sint32 width, sint32 height, NLMISC::CRGBA col /*=NLMISC::CRGBA::White*/)
{
driver->drawQuad(x * _OneOverScreenW, y * _OneOverScreenH, (x + width) * _OneOverScreenW, (y + height) * _OneOverScreenH, col);
}
// ***************************************************************************
void CViewRenderer::drawCustom (sint32 x, sint32 y, sint32 width, sint32 height, CRGBA col, UMaterial Mat)
{
float dstXmin, dstYmin, dstXmax, dstYmax;
// Is totally clipped ?
if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
(y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
return;
flush();
// Initialize quad
dstXmin = (float)(x) * _OneOverScreenW;
dstYmin = (float)(y) * _OneOverScreenH;
dstXmax = (float)(x + width) * _OneOverScreenW;
dstYmax = (float)(y + height) * _OneOverScreenH;
CQuadColorUV2 qcoluv2;
qcoluv2.V0.set (dstXmin, dstYmin, 0);
qcoluv2.V1.set (dstXmax, dstYmin, 0);
qcoluv2.V2.set (dstXmax, dstYmax, 0);
qcoluv2.V3.set (dstXmin, dstYmax, 0);
qcoluv2.Color0 = qcoluv2.Color1 = qcoluv2.Color2 = qcoluv2.Color3 = col;
qcoluv2.Uv0.U = 0; qcoluv2.Uv0.V = 1;
qcoluv2.Uv1.U = 1; qcoluv2.Uv1.V = 1;
qcoluv2.Uv2.U = 1; qcoluv2.Uv2.V = 0;
qcoluv2.Uv3.U = 0; qcoluv2.Uv3.V = 0;
qcoluv2.Uv02.U = 0; qcoluv2.Uv02.V = 1;
qcoluv2.Uv12.U = 1; qcoluv2.Uv12.V = 1;
qcoluv2.Uv22.U = 1; qcoluv2.Uv22.V = 0;
qcoluv2.Uv32.U = 0; qcoluv2.Uv32.V = 0;
// Clipping part
CQuadColorUV2 qcoluv2_clipped;
if (!needClipping(qcoluv2))
{
// No need to clip the quad
qcoluv2_clipped = qcoluv2;
}
else
{
clip (qcoluv2_clipped, qcoluv2);
}
// World space transformation
if (_WorldSpaceTransformation)
worldSpaceTransformation (qcoluv2_clipped);
// Draw clipped quad
driver->drawQuads (&qcoluv2_clipped, 1, Mat);
}
// ***************************************************************************
void CViewRenderer::drawCustom(sint32 x, sint32 y, sint32 width, sint32 height, const NLMISC::CUV &uv0Min, const NLMISC::CUV &uv0Max, NLMISC::CRGBA col, NL3D::UMaterial Mat)
{
float dstXmin, dstYmin, dstXmax, dstYmax;
// Is totally clipped ?
if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
(y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
return;
flush();
// Initialize quad
dstXmin = (float)(x) * _OneOverScreenW;
dstYmin = (float)(y) * _OneOverScreenH;
dstXmax = (float)(x + width) * _OneOverScreenW;
dstYmax = (float)(y + height) * _OneOverScreenH;
CQuadColorUV qcoluv;
qcoluv.V0.set (dstXmin, dstYmin, 0);
qcoluv.V1.set (dstXmax, dstYmin, 0);
qcoluv.V2.set (dstXmax, dstYmax, 0);
qcoluv.V3.set (dstXmin, dstYmax, 0);
qcoluv.Color0 = qcoluv.Color1 = qcoluv.Color2 = qcoluv.Color3 = col;
qcoluv.Uv0.U = uv0Min.U; qcoluv.Uv0.V = uv0Max.V;
qcoluv.Uv1.U = uv0Max.U; qcoluv.Uv1.V = uv0Max.V;
qcoluv.Uv2.U = uv0Max.U; qcoluv.Uv2.V = uv0Min.V;
qcoluv.Uv3.U = uv0Min.U; qcoluv.Uv3.V = uv0Min.V;
// Clipping part
CQuadColorUV qcoluv_clipped;
if (!needClipping(qcoluv))
{
// No need to clip the quad
qcoluv_clipped = qcoluv;
}
else
{
clip (qcoluv_clipped, qcoluv, 0);
}
// Draw clipped quad
driver->drawQuads (&qcoluv_clipped, 1, Mat);
}
// ***************************************************************************
void CViewRenderer::drawCustom (sint32 x, sint32 y, sint32 width, sint32 height,
const CUV &uv0Min, const CUV &uv0Max, const CUV &uv1Min, const CUV &uv1Max,
NLMISC::CRGBA col, NL3D::UMaterial Mat)
{
float dstXmin, dstYmin, dstXmax, dstYmax;
// Is totally clipped ?
if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
(y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
return;
flush();
// Initialize quad
dstXmin = (float)(x) * _OneOverScreenW;
dstYmin = (float)(y) * _OneOverScreenH;
dstXmax = (float)(x + width) * _OneOverScreenW;
dstYmax = (float)(y + height) * _OneOverScreenH;
CQuadColorUV2 qcoluv2;
qcoluv2.V0.set (dstXmin, dstYmin, 0);
qcoluv2.V1.set (dstXmax, dstYmin, 0);
qcoluv2.V2.set (dstXmax, dstYmax, 0);
qcoluv2.V3.set (dstXmin, dstYmax, 0);
qcoluv2.Color0 = qcoluv2.Color1 = qcoluv2.Color2 = qcoluv2.Color3 = col;
qcoluv2.Uv0.U = uv0Min.U; qcoluv2.Uv0.V = uv0Max.V;
qcoluv2.Uv1.U = uv0Max.U; qcoluv2.Uv1.V = uv0Max.V;
qcoluv2.Uv2.U = uv0Max.U; qcoluv2.Uv2.V = uv0Min.V;
qcoluv2.Uv3.U = uv0Min.U; qcoluv2.Uv3.V = uv0Min.V;
qcoluv2.Uv02.U = uv1Min.U; qcoluv2.Uv02.V = uv1Max.V;
qcoluv2.Uv12.U = uv1Max.U; qcoluv2.Uv12.V = uv1Max.V;
qcoluv2.Uv22.U = uv1Max.U; qcoluv2.Uv22.V = uv1Min.V;
qcoluv2.Uv32.U = uv1Min.U; qcoluv2.Uv32.V = uv1Min.V;
// Clipping part
CQuadColorUV2 qcoluv2_clipped;
if (!needClipping(qcoluv2))
{
// No need to clip the quad
qcoluv2_clipped = qcoluv2;
}
else
{
clip (qcoluv2_clipped, qcoluv2);
}
// World space transformation
if (_WorldSpaceTransformation)
worldSpaceTransformation (qcoluv2_clipped);
// Draw clipped quad
driver->drawQuads (&qcoluv2_clipped, 1, Mat);
}
// ***************************************************************************
CViewRenderer::CTextureId::~CTextureId ()
{
if (_TextureId>=0)
CViewRenderer::getInstance()->deleteTexture(_TextureId);
_TextureId = -1;
}
// ***************************************************************************
bool CViewRenderer::CTextureId::setTexture (const char *textureName, sint32 offsetX, sint32 offsetY, sint32 width, sint32 height,
bool uploadDXTC, bool bReleasable)
{
CViewRenderer &rVR = *CViewRenderer::getInstance();
if (_TextureId>=0)
rVR.deleteTexture(_TextureId);
_TextureId = rVR.getTextureIdFromName(textureName);
if (_TextureId<0)
_TextureId = rVR.createTexture (textureName, offsetX, offsetY, width, height, uploadDXTC, bReleasable);
return _TextureId >= 0;
}
// ***************************************************************************
void CViewRenderer::CTextureId::serial(NLMISC::IStream &f)
{
std::string texName;
if (f.isReading())
{
f.serial(texName);
setTexture(texName.c_str());
}
else
{
CViewRenderer &rVR = *CViewRenderer::getInstance();
texName = rVR.getTextureNameFromId(_TextureId);
f.serial(texName);
}
}
// ***************************************************************************
void CViewRenderer::worldSpaceTransformation (NLMISC::CQuadColorUV &qcoluv)
{
// set the world Z
qcoluv.V0.z = _CurrentZ;
qcoluv.V1.z = _CurrentZ;
qcoluv.V2.z = _CurrentZ;
qcoluv.V3.z = _CurrentZ;
// for scaled interface, apply the scale matrix
qcoluv.V0= _WorldSpaceMatrix * qcoluv.V0;
qcoluv.V1= _WorldSpaceMatrix * qcoluv.V1;
qcoluv.V2= _WorldSpaceMatrix * qcoluv.V2;
qcoluv.V3= _WorldSpaceMatrix * qcoluv.V3;
// unproject
qcoluv.V0 = _CameraFrustum.unProjectZ(qcoluv.V0);
qcoluv.V1 = _CameraFrustum.unProjectZ(qcoluv.V1);
qcoluv.V2 = _CameraFrustum.unProjectZ(qcoluv.V2);
qcoluv.V3 = _CameraFrustum.unProjectZ(qcoluv.V3);
}
// ***************************************************************************
void CViewRenderer::setWorldSpaceFrustum (const NL3D::CFrustum &cameraFrustum)
{
_CameraFrustum = cameraFrustum;
}
// ***************************************************************************
void CViewRenderer::activateWorldSpaceMatrix (bool activate)
{
_WorldSpaceTransformation = activate;
if (!_Material.empty())
_Material.setZFunc(activate?UMaterial::lessequal:UMaterial::always);
}
// ***************************************************************************
void CViewRenderer::drawText (sint layerId, float x, float y, uint wordIndex, float xmin, float ymin, float xmax, float ymax, UTextContext &textContext)
{
if (_WorldSpaceTransformation)
{
textContext.printClipAtUnProjected(*getStringRenderBuffer(layerId), _CameraFrustum, _WorldSpaceMatrix, x, y, _CurrentZ, wordIndex, xmin, ymin, xmax, ymax);
}
else
{
textContext.printClipAt(*getStringRenderBuffer(layerId), x, y, wordIndex, xmin, ymin, xmax, ymax);
}
// layer is no more empty
_EmptyLayer[layerId + VR_BIAS_LAYER]= false;
}
// ***************************************************************************
void CViewRenderer::setInterfaceDepth (const NLMISC::CVector &projCenter, float scale)
{
_CurrentZ = projCenter.z;
// no scale? => identity matrix (faster)
if(scale==1)
{
_WorldSpaceMatrix.identity();
_WorldSpaceScale= false;
}
else
{
_WorldSpaceMatrix.identity();
// must be in 0..1 coordinate here...
CVector pos= projCenter;
pos.x*= _OneOverScreenW;
pos.y*= _OneOverScreenH;
// set a pivoted scale matrix
_WorldSpaceMatrix.translate(pos);
_WorldSpaceMatrix.scale(scale);
_WorldSpaceMatrix.translate(-pos);
_WorldSpaceScale= true;
}
}
}