// Ryzom - MMORPG Framework // Copyright (C) 2010-2020 Winch Gate Property Limited // // This source file has been modified by the following contributors: // Copyright (C) 2013-2014 Laszlo KIS-ADAM (dfighter) // Copyright (C) 2020 Jan BOON (Kaetemi) // // 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 "nel/gui/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 "nel/misc/base64.h" #include "nel/misc/md5.h" using namespace NLMISC; using namespace std; using namespace NL3D; #ifdef DEBUG_NEW #define new DEBUG_NEW #endif 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::TFontsList CViewRenderer::fonts; CViewRenderer::CViewRenderer() { nlassert( CViewRenderer::driver != NULL ); nlassert( CViewRenderer::textcontext != NULL ); nlassert( CViewRenderer::hwCursors != NULL ); setup(); } CViewRenderer::~CViewRenderer() { for(uint i=0;igetWindowSize (w, h); // not minimized? change coords if(w!=0 && h!=0) { _IsMinimized= false; if (w != _ScreenW || h != _ScreenH) { _ScreenW = w; _ScreenH = h; updateInterfaceScale(); } } else { // Keep old coordinates (suppose resolution won't change, even if typically false wen we swithch from outgame to ingame) _IsMinimized= true; } } void CViewRenderer::updateInterfaceScale() { if(_ScreenW>0) _OneOverScreenW = 1.0f / (float)_ScreenW; else _OneOverScreenW = 1000; if(_ScreenH>0) _OneOverScreenH = 1.0f / (float)_ScreenH; else _OneOverScreenH = 1000; _InterfaceScale = _InterfaceUserScale; if (_InterfaceBaseW > 0 && _InterfaceBaseH > 0) { float wRatio = (float)_ScreenW / _InterfaceBaseW; float rRatio = (float)_ScreenH / _InterfaceBaseH; _InterfaceScale *= std::min(wRatio, rRatio); } if (_InterfaceScale != 1.0f) { _OneOverScreenW *= _InterfaceScale; _OneOverScreenH *= _InterfaceScale; _EffectiveScreenW = sint(_ScreenW / _InterfaceScale); _EffectiveScreenH = sint(_ScreenH / _InterfaceScale); } else { _EffectiveScreenW = _ScreenW; _EffectiveScreenH = _ScreenH; } } /* * getScreenSize : get the screen window size */ void CViewRenderer::getScreenSize (uint32 &w, uint32 &h) { w = _EffectiveScreenW; h = _EffectiveScreenH; } /* * get OOW / OOH */ void CViewRenderer::getScreenOOSize (float &oow, float &ooh) { oow= _OneOverScreenW; ooh= _OneOverScreenH; } void CViewRenderer::setInterfaceScale(float scale, sint32 width/*=0*/, sint32 height/*=0*/) { // prevent #div/0 if (sint(scale*100) > 0) _InterfaceUserScale = scale; else _InterfaceUserScale = 1.0f; _InterfaceBaseW = width; _InterfaceBaseH = height; updateInterfaceScale(); } void CViewRenderer::setup() { _ClipX = _ClipY = 0; _ClipW = 800; _ClipH = 600; _ScreenW = 800; _ScreenH = 600; _InterfaceScale = 1.0f; _InterfaceUserScale = 1.0f; _InterfaceBaseW = 0; _InterfaceBaseH = 0; _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); } 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(ite->Texture); if (tf) { driver->deleteTextureFile (tf); } ite++; } TFontsList::iterator iteFonts = fonts.begin(); while (iteFonts != fonts.end()) { driver->deleteTextContext(iteFonts->second); ++iteFonts; } _GlobalTextures.clear(); _SImages.clear(); _SImageIterators.clear(); _TextureMap.clear(); _IndexesToTextureIds.clear(); fonts.clear(); } NL3D::UDriver* CViewRenderer::getDriver(){ return driver; } // *************************************************************************** NL3D::UTextContext* CViewRenderer::getTextContext(const std::string &name) { if (!name.empty() && fonts.count(name) > 0) return fonts[name]; return textcontext; } // *************************************************************************** bool CViewRenderer::registerFont(const std::string &name, const std::string &font) { nlassert(driver != NULL); // free existing font if (fonts.count(name) > 0) driver->deleteTextContext(fonts[name]); std::string fontFile = NLMISC::startsWith(font, "ui") ? font : CPath::lookup(font, false); if (fontFile.empty()) { nlwarning("Font file '%s' not found", font.c_str()); return false; } NL3D::UTextContext *context; context = driver->createTextContext(fontFile); if (context == NULL) { nlwarning("Cannot create a TextContext with font '%s'.", font.c_str()); return false; } context->setKeep800x600Ratio(false); fonts[name] = context; return true; } 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); // to avoid a division by zero crash later if (txw < 0 || txh < 0) return; 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, float x, float y, float width, float 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; getTextureSizeFromId(nTxId, txw, txh); 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 */ 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.empty() ) return false; CIFile ifTmp; if (ifTmp.open(filename)) CBitmap::loadSize (ifTmp, gt.Width, gt.Height); // extract textures scale from filename // texture_interface_v3_2x.tga / texture_interface_v3_4x.tga if (textureFileName.find("_2x.") != std::string::npos) gt.Scale = 2.f; else if (textureFileName.find("_4x.") != std::string::npos) gt.Scale = 4.f; 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.empty() ) return false; if (!iFile.open(filename)) return false; _GlobalTextures.push_back (gt); driver->setCursorScale( CViewRenderer::hwCursorScale ); char bufTmp[256], tgaName[256]; tgaName[0] = 0; 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); // FIXME: Return value ignored, tgaName may be uninitialized SImage image; image.UVMin.U = uvMinU; image.UVMin.V = uvMinV; image.UVMax.U = uvMaxU; image.UVMax.V = uvMaxV; sTGAname = toLowerAscii(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); // TODO: scaled cursors not supported if (gt.Scale > 1.f) { curs.resample((sint)(curs.getWidth() / gt.Scale), (sint)(curs.getHeight() / gt.Scale)); } 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 = toLowerAscii(sGlobalTextureName); TGlobalTextureList::iterator ite = _GlobalTextures.begin(); while (ite != _GlobalTextures.end()) { if (toLowerAscii(ite->Name) == 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; if (startsWith(sGlobalTextureName, "data:image/")) return createTextureFromDataURL(sGlobalTextureName, uploadDXTC, bReleasable); // Look if already existing string sLwrGTName = toLowerAscii(sGlobalTextureName); TGlobalTextureList::iterator ite = _GlobalTextures.begin(); while (ite != _GlobalTextures.end()) { std::string sText = toLowerAscii(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.empty() ) 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; } sint32 CViewRenderer::createTextureFromDataURL(const std::string &data, bool uploadDXTC, bool bReleasable) { if (!startsWith(data, "data:image/")) return -1; size_t pos = data.find(";base64,"); if (pos == std::string::npos) { nlwarning("Failed to parse dataURL (not base64?) '%s'", data.c_str()); return -1; } std::string md5hash = getMD5((uint8 *)data.c_str(), (uint32)data.size()).toString(); TGlobalTextureList::iterator ite = _GlobalTextures.begin(); while (ite != _GlobalTextures.end()) { if (md5hash == ite->Name) break; ite++; } // If global texture not exists create it if (ite == _GlobalTextures.end()) { std::string decoded = base64::decode(data.substr(pos + 8)); if (decoded.empty()) { nlwarning("base64 decode failed '%s'", data.substr(pos + 8).c_str()); return -1; } // CMemStream buf; if (buf.isReading()) buf.invert(); buf.serialBuffer((uint8 *)(decoded.data()), decoded.size()); buf.invert(); CBitmap btm; btm.load(buf); SGlobalTexture gtTmp; gtTmp.FromGlobaleTexture = false; gtTmp.Width = gtTmp.DefaultWidth = btm.getWidth();; gtTmp.Height = gtTmp.DefaultHeight = btm.getHeight(); if (gtTmp.Width == 0 || gtTmp.Height == 0) { nlwarning("Failed to load the texture '%s', please check image format", data.c_str()); return -1; } UTextureMem *texture = driver->createTextureMem(btm.getWidth(), btm.getHeight(), CBitmap::RGBA); if (!texture) { nlwarning("Failed to create mem texture (%d,%d)", btm.getWidth(), btm.getHeight()); return -1; } memcpy(texture->getPointer(), btm.getPixels().getPtr(), btm.getSize() * 4); gtTmp.Texture = texture; gtTmp.Name = md5hash; gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff); gtTmp.Texture->setReleasable(bReleasable); if(uploadDXTC) gtTmp.Texture->setUploadFormat(UTexture::DXTC5); _GlobalTextures.push_back(gtTmp); ite = _GlobalTextures.end(); ite--; } // Add a texture with reference to the i th global texture SImage iTmp; // Set default parameters iTmp.Name = data; iTmp.GlobalTexturePtr = &(*ite); iTmp.UVMin = CUV(0.f , 0.f); iTmp.UVMax = CUV(1.f , 1.f); sint32 TextID = addSImage(iTmp); 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 = NLMISC::toLowerAscii(name); TGlobalTextureList::iterator ite = _GlobalTextures.begin(); while (ite != _GlobalTextures.end()) { std::string sText = NLMISC::toLowerAscii(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); } else { UTextureMem *tf = dynamic_cast(iteGT->Texture); if (tf) driver->deleteTextureMem(tf); } _GlobalTextures.erase (iteGT); return; } } // Global texture has not been found nlstop; } } } bool CViewRenderer::getTexture( NLMISC::CBitmap &bm, const std::string &name ) { TTextureMap::const_iterator itr = _TextureMap.find( name ); if( itr == _TextureMap.end() ) return false; sint32 id = itr->second; SImage *si = getSImage( id ); NLMISC::CBitmap *src = si->GlobalTexturePtr->Texture->generateDatas(); if( src->getPixelFormat() != NLMISC::CBitmap::RGBA ) return false; uint x0 = (uint)( si->UVMin.U * si->GlobalTexturePtr->Width ); uint y0 = (uint)( si->UVMin.V * si->GlobalTexturePtr->Height ); uint x1 = (uint)( si->UVMax.U * si->GlobalTexturePtr->Width ); uint y1 = (uint)( si->UVMax.V * si->GlobalTexturePtr->Height ); if( x1 == x0 ) return false; if( y1 == y0 ) return false; bm.resize( x1 - x0, y1 - y0 ); bm.blit( *src, x0, y0, ( x1 - x0 ), ( y1 - y0 ), 0, 0 ); return true; } void CViewRenderer::getTextureNames( std::vector< std::string > &textures ) { TTextureMap::const_iterator itr = _TextureMap.begin(); while( itr != _TextureMap.end() ) { textures.push_back( itr->first ); ++itr; } } /* * getTextureIdFromName */ sint32 CViewRenderer::getTextureIdFromName (const string &sName) const { if(sName.empty()) return -1; // convert to lowCase string nameLwr = toLowerAscii(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 / rImage.GlobalTexturePtr->Scale)+0.5f); height = (sint32)(((rImage.UVMax.V - rImage.UVMin.V)*rImage.GlobalTexturePtr->Height / rImage.GlobalTexturePtr->Scale)+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; // get (possibly) scaled width/height sint32 width, height; getTextureSizeFromId(id, width, height); if (width == 0 || height == 0) return CRGBA(255,255,255); 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(_Bilinear || (_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(_Bilinear || (_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 () { 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::clear() { if (_TextureId >= 0) { CViewRenderer::getInstance()->deleteTexture(_TextureId); _TextureId = -1; } } // *************************************************************************** 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) { xmin = xmin * _OneOverScreenW; ymin = ymin * _OneOverScreenH; xmax = xmax * _OneOverScreenW; ymax = ymax * _OneOverScreenH; if (_InterfaceScale != 1.0f && _InterfaceScale != 2.0f) { // align to screen pixel x *= _OneOverScreenW * _ScreenW; y *= _OneOverScreenH * _ScreenH; x = floorf(x) * 1.f / (float) _ScreenW; y = floorf(y) * 1.f / (float) _ScreenH; } else { x = floorf(x) * _OneOverScreenW; y = floorf(y) * _OneOverScreenH; } 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; } } }