// Ryzom - MMORPG Framework // 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 . #include "stdpch.h" #include "view_renderer.h" #include "nel/misc/path.h" #include "nel/misc/file.h" #include "nel/misc/uv.h" #include "nel/misc/hierarchical_timer.h" #include "interface_manager.h" #include "../client_cfg.h" using namespace NLMISC; using namespace std; using namespace NL3D; CViewRenderer::CViewRenderer( NL3D::UDriver *driver, NL3D::UTextContext *textcontext ) { setup(); this->driver = driver; this->textcontext = textcontext; } CViewRenderer::~CViewRenderer() { for(uint i=0;igetWindowSize (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;icreateMaterial(); setRenderStates(); // Init all renderBuffer for(uint i=0;icreateRenderBuffer(); } } 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); } /* * reset: reset the whole view renderer */ void CViewRenderer::reset() { TGlobalTextureList::iterator ite = _GlobalTextures.begin(); while (ite != _GlobalTextures.end()) { UTextureFile *tf = dynamic_cast(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) { this->textcontext = textcontext; } // *************************************************************************** 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 > = *(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 && layerIdLayers[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 && layerIdLayers[layerId]; std::vector *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 &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 && layerIdLayers[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 */ void 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; 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; if (!iFile.open(filename)) return; _GlobalTextures.push_back (gt); driver->setCursorScale(ClientCfg.HardwareCursorScale); 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 = tgaName; sTGAname = toLower(sTGAname); 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 (ClientCfg.HardwareCursors.count(image.Name)) { 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(); } 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(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=0) && (cLayers[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 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::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 >, sint layerId, const NLMISC::CQuadColorUV &qcoluv, uint rot) { layerId+= VR_BIAS_LAYER; nlassert(layerId>=0 && layerId=0 && layerIddrawWiredQuad(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 () { CInterfaceManager *pIM = CInterfaceManager::getInstance(); CViewRenderer &rVR = pIM->getViewRenderer(); if (_TextureId>=0) rVR.deleteTexture(_TextureId); _TextureId = -1; } // *************************************************************************** bool CViewRenderer::CTextureId::setTexture (const char *textureName, sint32 offsetX, sint32 offsetY, sint32 width, sint32 height, bool uploadDXTC, bool bReleasable) { CInterfaceManager *pIM = CInterfaceManager::getInstance(); CViewRenderer &rVR = pIM->getViewRenderer(); 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 { CInterfaceManager *pIM = CInterfaceManager::getInstance(); CViewRenderer &rVR = pIM->getViewRenderer(); 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; } }