Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / gui / view_renderer.cpp
blob38c4a9bde75324d93d6fbe95452e94e9799b253d
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013-2014 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "stdpch.h"
22 #include "nel/gui/view_renderer.h"
24 #include "nel/misc/path.h"
25 #include "nel/misc/file.h"
26 #include "nel/misc/uv.h"
27 #include "nel/misc/hierarchical_timer.h"
28 #include "nel/misc/base64.h"
29 #include "nel/misc/md5.h"
31 using namespace NLMISC;
32 using namespace std;
33 using namespace NL3D;
35 #ifdef DEBUG_NEW
36 #define new DEBUG_NEW
37 #endif
39 namespace NLGUI
42 CViewRenderer* CViewRenderer::instance = NULL;
43 NL3D::UDriver* CViewRenderer::driver = NULL;
44 NL3D::UTextContext* CViewRenderer::textcontext = NULL;
45 std::set< std::string >* CViewRenderer::hwCursors = NULL;
46 float CViewRenderer::hwCursorScale = 1.0f;
47 CViewRenderer::TFontsList CViewRenderer::fonts;
49 CViewRenderer::CViewRenderer()
51 nlassert( CViewRenderer::driver != NULL );
52 nlassert( CViewRenderer::textcontext != NULL );
53 nlassert( CViewRenderer::hwCursors != NULL );
54 setup();
57 CViewRenderer::~CViewRenderer()
59 for(uint i=0;i<VR_NUM_LAYER;i++)
61 delete _StringRBLayers[i];
62 _StringRBLayers[i]= NULL;
63 _EmptyLayer[i]= true;
68 CViewRenderer* CViewRenderer::getInstance()
70 if( instance == NULL )
71 instance = new CViewRenderer;
72 return instance;
76 * setClipWindow : set the current clipping window
77 * (this window do not inherit properties from parent or whatever)
79 void CViewRenderer::setClipWindow (sint32 x, sint32 y, sint32 w, sint32 h)
81 _ClipX = x;
82 _ClipY = y;
83 _ClipW = w;
84 _ClipH = h;
86 _XMin = (float)_ClipX * _OneOverScreenW;
87 _XMax = (float)(_ClipX+_ClipW) * _OneOverScreenW;
88 _YMin = (float)_ClipY * _OneOverScreenH;
89 _YMax = (float)(_ClipY+_ClipH) * _OneOverScreenH;
94 * checkNewScreenSize
96 void CViewRenderer::checkNewScreenSize ()
98 if (!driver) return;
99 uint32 w, h;
100 driver->getWindowSize (w, h);
101 // not minimized? change coords
102 if(w!=0 && h!=0)
104 _IsMinimized= false;
105 if (w != _ScreenW || h != _ScreenH)
107 _ScreenW = w;
108 _ScreenH = h;
110 updateInterfaceScale();
113 else
115 // Keep old coordinates (suppose resolution won't change, even if typically false wen we swithch from outgame to ingame)
116 _IsMinimized= true;
120 void CViewRenderer::updateInterfaceScale()
122 if(_ScreenW>0)
123 _OneOverScreenW = 1.0f / (float)_ScreenW;
124 else
125 _OneOverScreenW = 1000;
126 if(_ScreenH>0)
127 _OneOverScreenH = 1.0f / (float)_ScreenH;
128 else
129 _OneOverScreenH = 1000;
131 _InterfaceScale = _InterfaceUserScale;
132 if (_InterfaceBaseW > 0 && _InterfaceBaseH > 0)
134 float wRatio = (float)_ScreenW / _InterfaceBaseW;
135 float rRatio = (float)_ScreenH / _InterfaceBaseH;
136 _InterfaceScale *= std::min(wRatio, rRatio);
139 if (_InterfaceScale != 1.0f)
141 _OneOverScreenW *= _InterfaceScale;
142 _OneOverScreenH *= _InterfaceScale;
144 _EffectiveScreenW = sint(_ScreenW / _InterfaceScale);
145 _EffectiveScreenH = sint(_ScreenH / _InterfaceScale);
147 else
149 _EffectiveScreenW = _ScreenW;
150 _EffectiveScreenH = _ScreenH;
156 * getScreenSize : get the screen window size
158 void CViewRenderer::getScreenSize (uint32 &w, uint32 &h)
160 w = _EffectiveScreenW;
161 h = _EffectiveScreenH;
165 * get OOW / OOH
167 void CViewRenderer::getScreenOOSize (float &oow, float &ooh)
169 oow= _OneOverScreenW;
170 ooh= _OneOverScreenH;
173 void CViewRenderer::setInterfaceScale(float scale, sint32 width/*=0*/, sint32 height/*=0*/)
175 // prevent #div/0
176 if (sint(scale*100) > 0)
177 _InterfaceUserScale = scale;
178 else
179 _InterfaceUserScale = 1.0f;
181 _InterfaceBaseW = width;
182 _InterfaceBaseH = height;
184 updateInterfaceScale();
187 void CViewRenderer::setup()
189 _ClipX = _ClipY = 0;
190 _ClipW = 800;
191 _ClipH = 600;
192 _ScreenW = 800;
193 _ScreenH = 600;
194 _InterfaceScale = 1.0f;
195 _InterfaceUserScale = 1.0f;
196 _InterfaceBaseW = 0;
197 _InterfaceBaseH = 0;
198 _IsMinimized= false;
199 _WFigurTexture= 0;
200 _HFigurTexture= 0;
201 _WFigurSeparatorTexture = 0;
202 _FigurSeparatorTextureId = -1;
203 _FigurBlankId = -1;
204 _BlankId = -1;
205 _WorldSpaceTransformation = true;
206 _CurrentZ = 10;
207 for(uint i=0;i<VR_NUM_LAYER;i++)
209 _StringRBLayers[i]= NULL;
210 _EmptyLayer[i]= true;
212 _BlankGlobalTexture = NULL;
213 _Bilinear = false;
215 updateInterfaceScale();
220 * init: init material and string buffer
222 void CViewRenderer::init()
224 if (!driver) return;
225 _Material = driver->createMaterial();
227 setRenderStates();
229 // Init all renderBuffer
230 for(uint i=0;i<VR_NUM_LAYER;i++)
232 _StringRBLayers[i]= textcontext->createRenderBuffer();
236 void CViewRenderer::setRenderStates()
238 _Material.setDoubleSided();
239 _Material.setZWrite(false);
240 _Material.setZFunc(UMaterial::always);
241 _Material.setBlend (true);
242 _Material.setBlendFunc (NL3D::UMaterial::srcalpha, NL3D::UMaterial::invsrcalpha);
243 _Material.setColor(CRGBA::White);
244 _Material.setTexture(0, NULL);
245 _Material.setTexture(1, NULL);
246 _Material.setTexture(2, NULL);
247 _Material.setTexture(3, NULL);
248 _Material.setZBias(0);
251 void CViewRenderer::release()
253 if( instance != NULL )
255 instance->reset();
256 delete instance;
257 instance = NULL;
263 * reset: reset the whole view renderer
265 void CViewRenderer::reset()
267 TGlobalTextureList::iterator ite = _GlobalTextures.begin();
268 while (ite != _GlobalTextures.end())
270 if (ite->Texture)
272 UTextureFile *tf = dynamic_cast<NL3D::UTextureFile *>(ite->Texture);
273 if (tf)
275 driver->deleteTextureFile (tf);
277 else
279 UTextureMem *tf = dynamic_cast<NL3D::UTextureMem *>(ite->Texture);
280 if (tf) driver->deleteTextureMem(tf);
283 ite++;
286 TFontsList::iterator iteFonts = fonts.begin();
287 while (iteFonts != fonts.end())
289 driver->deleteTextContext(iteFonts->second);
290 ++iteFonts;
293 _GlobalTextures.clear();
294 _SImages.clear();
295 _SImageIterators.clear();
296 _TextureMap.clear();
297 _IndexesToTextureIds.clear();
298 fonts.clear();
301 NL3D::UDriver* CViewRenderer::getDriver(){
302 return driver;
305 // ***************************************************************************
306 NL3D::UTextContext* CViewRenderer::getTextContext(const std::string &name)
308 if (!name.empty() && fonts.count(name) > 0)
309 return fonts[name];
311 return textcontext;
314 // ***************************************************************************
315 bool CViewRenderer::registerFont(const std::string &name, const std::string &font)
317 nlassert(driver != NULL);
319 // free existing font
320 if (fonts.count(name) > 0)
321 driver->deleteTextContext(fonts[name]);
323 std::string fontFile = NLMISC::startsWith(font, "ui") ? font : CPath::lookup(font, false);
324 if (fontFile.empty())
326 nlwarning("Font file '%s' not found", font.c_str());
327 return false;
330 NL3D::UTextContext *context;
331 context = driver->createTextContext(fontFile);
332 if (context == NULL)
334 nlwarning("Cannot create a TextContext with font '%s'.", font.c_str());
335 return false;
338 context->setKeep800x600Ratio(false);
340 fonts[name] = context;
342 return true;
345 void CViewRenderer::setTextContext(NL3D::UTextContext *textcontext)
347 CViewRenderer::textcontext = textcontext;
350 void CViewRenderer::setDriver( NL3D::UDriver *driver )
352 CViewRenderer::driver = driver;
355 // ***************************************************************************
356 void CViewRenderer::SImage::setupQuadUV(bool flipv, uint8 rot, CQuadColorUV &dest)
358 nlassert(rot<=3);
359 // Rotation is CW and flip is along x axis
360 // Flip is vertical flip (this means we invert all y for a constant x)
361 // The transforms are done in this order : first apply the flip (or not) and then rotate
362 static const CUV UVTab[8][4] = {
363 { CUV(0, 0), CUV(1, 0), CUV(1, 1), CUV(0, 1) }, // rot 0, no flip
364 { CUV(1, 0), CUV(1, 1), CUV(0, 1), CUV(0, 0) }, // rot 1, no flip
365 { CUV(1, 1), CUV(0, 1), CUV(0, 0), CUV(1, 0) }, // rot 2, no flip
366 { CUV(0, 1), CUV(0, 0), CUV(1, 0), CUV(1, 1) }, // rot 3, no flip
367 { CUV(1, 0), CUV(0, 0), CUV(0, 1), CUV(1, 1) }, // rot 0, flipped
368 { CUV(0, 0), CUV(0, 1), CUV(1, 1), CUV(1, 0) }, // rot 1, flipped
369 { CUV(0, 1), CUV(1, 1), CUV(1, 0), CUV(0, 0) }, // rot 2, flipped
370 { CUV(1, 1), CUV(1, 0), CUV(0, 0), CUV(0, 1) } // rot 3, flipped
373 // Take care that the origin in the texture is top left so to get the texture in bottom-up
374 // we have to start at Max and go at Min. For left and right this is Min to Max.
376 float du = UVMax.U - UVMin.U;
377 float dv = UVMin.V - UVMax.V;
379 uint idx = flipv*4 + rot;
381 dest.Uv0 = CUV (UVMin.U + UVTab[idx][0].U * du, UVMax.V + UVTab[idx][0].V * dv);
382 dest.Uv1 = CUV (UVMin.U + UVTab[idx][1].U * du, UVMax.V + UVTab[idx][1].V * dv);
383 dest.Uv2 = CUV (UVMin.U + UVTab[idx][2].U * du, UVMax.V + UVTab[idx][2].V * dv);
384 dest.Uv3 = CUV (UVMin.U + UVTab[idx][3].U * du, UVMax.V + UVTab[idx][3].V * dv);
386 /* // TRAP : Unrolled Version (To be tested to know if it is faster than the previous one)
387 if (flipv)
389 switch (rot)
391 case 0:
392 qcoluv.Uv0.U = rI.UVMax.U; qcoluv.Uv0.V = rI.UVMax.V;
393 qcoluv.Uv1.U = rI.UVMin.U; qcoluv.Uv1.V = rI.UVMax.V;
394 qcoluv.Uv2.U = rI.UVMin.U; qcoluv.Uv2.V = rI.UVMin.V;
395 qcoluv.Uv3.U = rI.UVMax.U; qcoluv.Uv3.V = rI.UVMin.V;
396 break;
397 case 1:
398 qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMax.V;
399 qcoluv.Uv1.U = rI.UVMin.U; qcoluv.Uv1.V = rI.UVMin.V;
400 qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMin.V;
401 qcoluv.Uv3.U = rI.UVMax.U; qcoluv.Uv3.V = rI.UVMax.V;
402 break;
403 case 2:
404 qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMin.V;
405 qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMin.V;
406 qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMax.V;
407 qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMax.V;
408 break;
409 case 3:
410 qcoluv.Uv0.U = rI.UVMax.U; qcoluv.Uv0.V = rI.UVMin.V;
411 qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMax.V;
412 qcoluv.Uv2.U = rI.UVMin.U; qcoluv.Uv2.V = rI.UVMax.V;
413 qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMin.V;
414 break;
417 else
419 switch (rot)
421 case 0:
422 qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMax.V;
423 qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMax.V;
424 qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMin.V;
425 qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMin.V;
426 break;
427 case 1:
428 qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMin.V;
429 qcoluv.Uv1.U = rI.UVMin.U; qcoluv.Uv1.V = rI.UVMax.V;
430 qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMax.V;
431 qcoluv.Uv3.U = rI.UVMax.U; qcoluv.Uv3.V = rI.UVMin.V;
432 break;
433 case 2:
434 qcoluv.Uv0.U = rI.UVMax.U; qcoluv.Uv0.V = rI.UVMin.V;
435 qcoluv.Uv1.U = rI.UVMin.U; qcoluv.Uv1.V = rI.UVMin.V;
436 qcoluv.Uv2.U = rI.UVMin.U; qcoluv.Uv2.V = rI.UVMax.V;
437 qcoluv.Uv3.U = rI.UVMax.U; qcoluv.Uv3.V = rI.UVMax.V;
438 break;
439 case 3:
440 qcoluv.Uv0.U = rI.UVMax.U; qcoluv.Uv0.V = rI.UVMax.V;
441 qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMin.V;
442 qcoluv.Uv2.U = rI.UVMin.U; qcoluv.Uv2.V = rI.UVMin.V;
443 qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMax.V;
444 break;
449 // ***************************************************************************
452 * drawRotFlipBitmapTiled
454 void CViewRenderer::drawRotFlipBitmapTiled (sint layerId, sint32 x, sint32 y, sint32 width, sint32 height, uint8 rot, bool flip,
455 sint32 nTxId, uint tileOrigin, const CRGBA &col)
457 static volatile bool draw = true;
458 if (!draw) return;
459 if (width <= 0 || height <= 0) return;
461 if (nTxId < 0) return;
463 // Is totally clipped ?
464 if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
465 (y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
466 return;
468 SImage &rI = *getSImage(nTxId);
470 sint32 txw, txh;
471 // start to draw at the reference corner
472 getTextureSizeFromId (nTxId, txw, txh);
474 // to avoid a division by zero crash later
475 if (txw < 0 || txh < 0) return;
477 if (rot > 3) rot = 3;
479 sint32 startX = x, startY = y;
480 sint32 stepX = txw, stepY = txh;
482 if (rot & 1)
484 std::swap(txw, txh);
487 // choose new start pos & uvs depending on the reference corner
488 // Along x axis
489 if (tileOrigin & 1) // right or left ?
490 { // right
491 startX = x + width - txw;
492 stepX = -txw;
495 // Along y axis
496 if (tileOrigin & 2) // bottom or top ?
497 { // top
498 startY = y + height - txh;
499 stepY = -txh;
502 // Fit screen coordinates
504 float fStartX = (float) startX * _OneOverScreenW;
505 float fStartY = (float) startY * _OneOverScreenH;
506 float fStepX = (float) stepX * _OneOverScreenW;
507 float fStepY = (float) stepY * _OneOverScreenH;
508 float fTxW = (float) txw * _OneOverScreenW;
509 float fTxH = (float) txh * _OneOverScreenH;
511 CQuadColorUV qcoluv;
513 qcoluv.Color0 = qcoluv.Color1 = qcoluv.Color2 = qcoluv.Color3 = col;
514 qcoluv.V0.z = qcoluv.V1.z = qcoluv.V2.z = qcoluv.V3.z = 0;
515 rI.setupQuadUV(flip,rot,qcoluv);
517 uint numTileX = (uint32)((width - 1) / txw);
518 uint numTileY = (uint32)((height- 1) / txh);
520 float currY = fStartY;
522 sint32 oldClipX = _ClipX;
523 sint32 oldClipY = _ClipY;
524 sint32 oldClipW = _ClipW;
525 sint32 oldClipH = _ClipH;
526 if (x < _ClipX) { width -= _ClipX - x; x = _ClipX; }
527 if (y < _ClipY) { height -= _ClipY - y; y = _ClipY; }
528 if ((x+width) > (_ClipX+_ClipW)) width -= (x+width) - (_ClipX+_ClipW);
529 if ((y+height) > (_ClipY+_ClipH)) height -= (y+height) - (_ClipY+_ClipH);
530 setClipWindow (x, y, width, height);
532 // draw result let the clipper clip the quads
533 for(uint py = 0; py <= numTileY; ++py)
535 float currX = fStartX;
536 for(uint px = 0; px <= numTileX; ++px)
538 /// There is room for speedup there
539 qcoluv.V0.x = currX;
540 qcoluv.V1.x = currX + fTxW;
541 qcoluv.V2.x = currX + fTxW;
542 qcoluv.V3.x = currX;
544 qcoluv.V0.y = currY;
545 qcoluv.V1.y = currY;
546 qcoluv.V2.y = currY + fTxH;
547 qcoluv.V3.y = currY + fTxH;
549 // Is NOT totally clipped ?
550 if ( !( (qcoluv.V0.x > _XMax) || (qcoluv.V2.x < _XMin) ||
551 (qcoluv.V0.y > _YMax) || (qcoluv.V2.y < _YMin) ) )
552 putQuadInLayer (*(rI.GlobalTexturePtr), layerId, qcoluv, rot);
554 currX += fStepX;
556 currY += fStepY;
559 setClipWindow (oldClipX, oldClipY, oldClipW, oldClipH);
564 * drawBitmap
566 void CViewRenderer::drawRotFlipBitmap (sint layerId, float x, float y, float width, float height,
567 uint8 rot, bool flipv, sint32 nTxId, const CRGBA &col)
569 if (width <= 0 || height <= 0) return;
571 if (nTxId < 0)
572 return;
576 float dstXmin, dstYmin, dstXmax, dstYmax;
578 // Is totally clipped ?
579 if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
580 (y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
581 return;
583 dstXmin = (float)(x) * _OneOverScreenW;
584 dstYmin = (float)(y) * _OneOverScreenH;
585 dstXmax = (float)(x + width) * _OneOverScreenW;
586 dstYmax = (float)(y + height) * _OneOverScreenH;
588 CQuadColorUV qcoluv;
589 qcoluv.V0.set (dstXmin, dstYmin, 0);
590 qcoluv.V1.set (dstXmax, dstYmin, 0);
591 qcoluv.V2.set (dstXmax, dstYmax, 0);
592 qcoluv.V3.set (dstXmin, dstYmax, 0);
594 qcoluv.Color0 = qcoluv.Color1 = qcoluv.Color2 = qcoluv.Color3 = col;
596 SImage &rI = *getSImage(nTxId);
598 // Avoid switch in common case
599 if (!flipv && !rot)
601 qcoluv.Uv0.U = rI.UVMin.U; qcoluv.Uv0.V = rI.UVMax.V;
602 qcoluv.Uv1.U = rI.UVMax.U; qcoluv.Uv1.V = rI.UVMax.V;
603 qcoluv.Uv2.U = rI.UVMax.U; qcoluv.Uv2.V = rI.UVMin.V;
604 qcoluv.Uv3.U = rI.UVMin.U; qcoluv.Uv3.V = rI.UVMin.V;
606 // else standard case
607 else
609 if (rot > 3)
610 rot = 3;
612 rI.setupQuadUV(flipv, rot, qcoluv);
615 static volatile bool doRot[4] = { true, true, true, true };
616 if (doRot[rot])
618 putQuadInLayer (*(rI.GlobalTexturePtr), layerId, qcoluv, rot);
624 * draw11RotBitmap
625 * sTx must be lowered !!!
627 void CViewRenderer::draw11RotFlipBitmap (sint layerId, sint32 x, sint32 y, uint8 rot, bool flipv, sint32 nTxId, const CRGBA &col)
629 if (nTxId < 0)
630 return;
632 sint32 txw, txh;
633 getTextureSizeFromId(nTxId, txw, txh);
635 drawRotFlipBitmap (layerId, x, y, txw, txh, rot, flipv, nTxId, col);
639 inline void remapUV(CUV &dest, const CUV &src, const CUV &min, const CUV &max)
641 dest.set(src.U * (max.U - min.U) + min.U, src.V * (max.V - min.V) + min.V);
644 void CViewRenderer::drawQuad(sint layerId, const NLMISC::CQuadUV &quadUV, sint32 nTxId, NLMISC::CRGBA col /*=NLMISC::CRGBA(255,255,255,255)*/, bool additif, bool filtered)
646 nlassert(!(additif && !filtered)); // not implemented yet!
647 if (nTxId < 0)
648 return;
649 CQuadColorUV normedQuad;
651 normedQuad.V0.set(quadUV.V0.x * _OneOverScreenW, quadUV.V0.y * _OneOverScreenH, 0.f);
652 normedQuad.V1.set(quadUV.V1.x * _OneOverScreenW, quadUV.V1.y * _OneOverScreenH, 0.f);
653 normedQuad.V2.set(quadUV.V2.x * _OneOverScreenW, quadUV.V2.y * _OneOverScreenH, 0.f);
654 normedQuad.V3.set(quadUV.V3.x * _OneOverScreenW, quadUV.V3.y * _OneOverScreenH, 0.f);
656 float qXMin = minof(normedQuad.V0.x, normedQuad.V1.x, normedQuad.V2.x, normedQuad.V3.x);
657 if (qXMin > _XMax) return;
658 float qXMax = maxof(normedQuad.V0.x, normedQuad.V1.x, normedQuad.V2.x, normedQuad.V3.x);
659 if (qXMax < _XMin) return;
660 float qYMin = minof(normedQuad.V0.y, normedQuad.V1.y, normedQuad.V2.y, normedQuad.V3.y);
661 if (qYMin > _YMax) return;
662 float qYMax = maxof(normedQuad.V0.y, normedQuad.V1.y, normedQuad.V2.y, normedQuad.V3.y);
663 if (qYMax < _YMin) return;
668 SImage &rImage = *getSImage(nTxId);
669 SGlobalTexture &gt = *(rImage.GlobalTexturePtr);
670 CUV deltaUV(1.f / (float) gt.Width, 1.f / (float) gt.Height);
671 CUV cornerMin = rImage.UVMin + deltaUV;
672 CUV cornerMax = rImage.UVMax - deltaUV;
673 remapUV(normedQuad.Uv0, quadUV.Uv0, cornerMin, cornerMax);
674 remapUV(normedQuad.Uv1, quadUV.Uv1, cornerMin, cornerMax);
675 remapUV(normedQuad.Uv2, quadUV.Uv2, cornerMin, cornerMax);
676 remapUV(normedQuad.Uv3, quadUV.Uv3, cornerMin, cornerMax);
678 // test if clipping is required
679 if (qXMin >= _XMin && qYMin >= _YMin && qXMax <= _XMax && qYMax <= _YMax)
681 // not clipped, easy case
682 normedQuad.Color0 = normedQuad.Color1 = normedQuad.Color2 = normedQuad.Color3 = col;
684 if (_WorldSpaceTransformation)
686 worldSpaceTransformation (normedQuad);
689 layerId+= VR_BIAS_LAYER;
690 nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
691 CLayer &layer = rImage.GlobalTexturePtr->Layers[layerId];
692 if (!filtered)
694 if (layer.NbQuads == layer.Quads.size())
695 layer.Quads.push_back (normedQuad);
696 else
697 layer.Quads[layer.NbQuads] = normedQuad;
698 ++ layer.NbQuads;
700 else if (additif) layer.FilteredAdditifQuads.push_back(normedQuad);
701 else layer.FilteredAlphaBlendedQuads.push_back(normedQuad);
702 _EmptyLayer[layerId]= false;
704 else
706 // Partially clipped (slowest case)
707 // Must do the clip manually
708 static const uint maxNumCorners = 8;
710 static CVector outPos0[maxNumCorners];
711 static CUV outUV0[maxNumCorners];
712 static CVector outPos1[maxNumCorners];
713 static CUV outUV1[maxNumCorners];
715 outUV0[0] = normedQuad.Uv0;
716 outUV0[1] = normedQuad.Uv1;
717 outUV0[2] = normedQuad.Uv2;
718 outUV0[3] = normedQuad.Uv3;
720 outPos0[0] = normedQuad.V0;
721 outPos0[1] = normedQuad.V1;
722 outPos0[2] = normedQuad.V2;
723 outPos0[3] = normedQuad.V3;
725 CVector *pPos0 = outPos0;
726 CVector *pPos1 = outPos1;
727 CUV *pUV0 = outUV0;
728 CUV *pUV1 = outUV1;
730 sint count = 4;
732 if (qXMin < _XMin)
734 // clip left
735 CPlane clipper(-1.f, 0.f, 0.f, _XMin);
736 count = clipper.clipPolygonBack(pPos0, pUV0, pPos1, pUV1, count);
737 std::swap(pPos0, pPos1);
738 std::swap(pUV0, pUV1);
740 if (qXMax > _XMax)
742 // clip right
743 CPlane clipper(1.f, 0.f, 0.f, -_XMax);
744 count = clipper.clipPolygonBack(pPos0, pUV0, pPos1, pUV1, count);
745 std::swap(pPos0, pPos1);
746 std::swap(pUV0, pUV1);
749 if (qYMin < _YMin)
751 // clip bottom
752 CPlane clipper(0.f, -1.f, 0.f, _YMin);
753 count = clipper.clipPolygonBack(pPos0, pUV0, pPos1, pUV1, count);
754 std::swap(pPos0, pPos1);
755 std::swap(pUV0, pUV1);
757 if (qYMax > _YMax)
759 // clip top
760 CPlane clipper(0.f, 1.f, 0.f, -_YMax);
761 count = clipper.clipPolygonBack(pPos0, pUV0, pPos1, pUV1, count);
762 std::swap(pPos0, pPos1);
763 std::swap(pUV0, pUV1);
766 nlassert(count <= (sint)maxNumCorners);
767 if (count >= 3)
769 count -= 2;
770 layerId+= VR_BIAS_LAYER;
771 nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
772 CLayer &layer = rImage.GlobalTexturePtr->Layers[layerId];
773 std::vector<NLMISC::CTriangleColorUV> *tris;
774 if (!filtered)
776 tris = &layer.Tris;
778 else
780 tris = additif ? &layer.FilteredAdditifTris : &layer.FilteredAlphaBlendedTris;
782 tris->resize(tris->size() + count);
783 CTriangleColorUV *lastTri = &tris->back() + 1;
784 CTriangleColorUV *currTri = lastTri - count;
785 const CVector *firstPos = pPos0++;
786 const CUV *firstUV = pUV0++;
789 currTri->V0 = *firstPos;
790 currTri->V1 = *pPos0;
791 currTri->V2 = *(pPos0 + 1);
792 currTri->Color0 = col;
793 currTri->Color1 = col;
794 currTri->Color2 = col;
795 currTri->Uv0 = *firstUV;
796 currTri->Uv1 = *pUV0;
797 currTri->Uv2 = *(pUV0 + 1);
799 pPos0 ++;
800 pUV0 ++;
801 ++currTri;
803 while (currTri != lastTri);
804 _EmptyLayer[layerId]= false;
810 void CViewRenderer::drawUnclippedTriangles(sint layerId, const std::vector<NLMISC::CTriangle> &tris, NLMISC::CRGBA col)
812 if (tris.empty()) return;
813 if (!_BlankGlobalTexture) return;
814 // primary goal here is batching, so we prefer to draw the triangle with a blank texture rather than
815 // switching material and having to flush all primitives .
816 layerId+= VR_BIAS_LAYER;
817 nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
818 CLayer &layer = _BlankGlobalTexture->Layers[layerId];
819 uint startCount = (uint)layer.FilteredAlphaBlendedTris.size();
820 layer.FilteredAlphaBlendedTris.resize(startCount + tris.size());
821 const NLMISC::CTriangle *src =&tris[0];
822 const NLMISC::CTriangle *last = src + tris.size();
823 NLMISC::CTriangleColorUV *dest = &layer.FilteredAlphaBlendedTris[0] + startCount;
824 _EmptyLayer[layerId]= false;
827 dest->V0.set(src->V0.x * _OneOverScreenW, src->V0.y * _OneOverScreenH, 0.f);
828 dest->V1.set(src->V1.x * _OneOverScreenW, src->V1.y * _OneOverScreenH, 0.f);
829 dest->V2.set(src->V2.x * _OneOverScreenW, src->V2.y * _OneOverScreenH, 0.f);
830 static volatile bool testOpaque = false;
831 if (testOpaque)
833 dest->Color0 = CRGBA::White;
834 dest->Color1 = CRGBA::White;
835 dest->Color2 = CRGBA::White;
836 dest->Uv0.set(0.f, 0.f);
837 dest->Uv1.set(1.f, 0.f);
838 dest->Uv2.set(1.f, 1.f);
840 else
842 dest->Color0 = col;
843 dest->Color1 = col;
844 dest->Color2 = col;
845 dest->Uv0 = _BlankUV;
846 dest->Uv1 = _BlankUV;
847 dest->Uv2 = _BlankUV;
849 ++ dest;
850 ++ src;
852 while (src != last);
856 * loadTextures
858 bool CViewRenderer::loadTextures (const std::string &textureFileName, const std::string &uvFileName, bool uploadDXTC)
860 SGlobalTexture gt;
861 // Load texture file
862 string filename = CPath::lookup (textureFileName, false);
863 if (filename.empty() )
864 return false;
866 CIFile ifTmp;
867 if (ifTmp.open(filename))
868 CBitmap::loadSize (ifTmp, gt.Width, gt.Height);
870 // extract textures scale from filename
871 // texture_interface_v3_2x.tga / texture_interface_v3_4x.tga
872 if (textureFileName.find("_2x.") != std::string::npos)
873 gt.Scale = 2.f;
874 else if (textureFileName.find("_4x.") != std::string::npos)
875 gt.Scale = 4.f;
877 gt.Texture = driver->createTextureFile (filename);
878 // Force to generate the texture now. This way we can extract the mouse bitmaps from it now without having to load it again.
879 // Its why we don't release it at the end, because it is likely to be uploaded soon)
880 CBitmap *texDatas = gt.Texture->generateDatas();
882 gt.Name = filename;
883 gt.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
884 if(uploadDXTC)
885 gt.Texture->setUploadFormat(UTexture::DXTC5);
887 // Load uv file
888 CIFile iFile;
889 filename = CPath::lookup (uvFileName, false);
890 if (filename.empty() )
891 return false;
892 if (!iFile.open(filename))
893 return false;
895 _GlobalTextures.push_back (gt);
897 driver->setCursorScale( CViewRenderer::hwCursorScale );
899 char bufTmp[256], tgaName[256];
900 tgaName[0] = 0;
901 string sTGAname;
902 float uvMinU, uvMinV, uvMaxU, uvMaxV;
903 while (!iFile.eof())
905 iFile.getline (bufTmp, 256);
906 sscanf (bufTmp, "%s %f %f %f %f", tgaName, &uvMinU, &uvMinV, &uvMaxU, &uvMaxV); // FIXME: Return value ignored, tgaName may be uninitialized
907 SImage image;
908 image.UVMin.U = uvMinU;
909 image.UVMin.V = uvMinV;
910 image.UVMax.U = uvMaxU;
911 image.UVMax.V = uvMaxV;
912 sTGAname = toLowerAscii(string(tgaName));
914 string::size_type stripPng = sTGAname.find(".png");
915 if (stripPng != string::npos)
917 sTGAname[stripPng + 1] = 't';
918 sTGAname[stripPng + 2] = 'g';
919 sTGAname[stripPng + 3] = 'a';
922 image.Name = sTGAname;
923 image.GlobalTexturePtr = &(_GlobalTextures.back());
924 if (getTextureIdFromName(sTGAname) != -1)
926 string tmp = string("duplicate texture name in ") + textureFileName + "(" + sTGAname + ")";
927 nlwarning(tmp.c_str());
929 else
931 sint32 textureId = addSImage(image);
932 //nlwarning("SIMAGE ADDED: id = %x, name = %s", textureId, image.Name.c_str());
933 // Insert in map.
934 _TextureMap.insert( make_pair(image.Name, textureId) );
937 // if this is a cursor texture, extract it now (supported for rgba only now, because of the blit)
938 if (texDatas && texDatas->getPixelFormat() == CBitmap::RGBA)
940 if( CViewRenderer::hwCursors->count( image.Name ) > 0 )
942 uint x0 = (uint) (image.UVMin.U * gt.Width);
943 uint y0 = (uint) (image.UVMin.V * gt.Height);
944 uint x1 = (uint) (image.UVMax.U * gt.Width);
945 uint y1 = (uint) (image.UVMax.V * gt.Height);
946 if (x1 != x0 && y1 != y0)
948 CBitmap curs;
949 curs.resize(x1 - x0, y1 - y0);
950 curs.blit(*texDatas, x0, y0, (x1 - x0), (y1 - y0), 0, 0);
951 // TODO: scaled cursors not supported
952 if (gt.Scale > 1.f) {
953 curs.resample((sint)(curs.getWidth() / gt.Scale), (sint)(curs.getHeight() / gt.Scale));
955 driver->addCursor(image.Name, curs);
961 initIndexesToTextureIds ();
962 initSystemTextures();
963 initTypo();
965 return true;
971 void CViewRenderer::setExternalTexture(const std::string &sGlobalTextureName,
972 NL3D::UTexture *externalTexture,
973 uint32 externalTexWidth,
974 uint32 externalTexHeight,
975 uint32 defaultTexWidth,
976 uint32 defaultTexHeight
979 if (sGlobalTextureName.empty())
981 nlwarning("Can't create aglobal texture with an empty name");
982 return;
984 // Look if already existing
985 string sLwrGTName = toLowerAscii(sGlobalTextureName);
986 TGlobalTextureList::iterator ite = _GlobalTextures.begin();
987 while (ite != _GlobalTextures.end())
989 if (toLowerAscii(ite->Name) == sLwrGTName)
990 break;
991 ite++;
993 if (ite == _GlobalTextures.end())
995 SGlobalTexture gtTmp;
996 gtTmp.FromGlobaleTexture = true;
998 gtTmp.Name = sLwrGTName;
999 _GlobalTextures.push_back(gtTmp);
1000 ite = _GlobalTextures.end();
1001 ite--;
1003 ite->Width = externalTexWidth;
1004 ite->Height = externalTexHeight;
1005 ite->DefaultWidth = defaultTexWidth;
1006 ite->DefaultHeight = defaultTexHeight;
1007 ite->Texture = externalTexture;
1010 bool CViewRenderer::loadTextureFromString(CViewRenderer::SGlobalTexture *gt, const std::string &data)
1012 size_t pos = data.find(";base64,");
1013 if (pos == std::string::npos)
1015 nlwarning("Data does not have 'data:image/...;base64,...' format '%s'", data.c_str());
1016 return false;
1019 std::string decoded = base64::decode(data.substr(pos + 8));
1020 if (decoded.empty())
1022 nlwarning("base64 decoding failed '%s", data.substr(pos + 8).c_str());
1023 return false;
1026 CMemStream buf;
1027 if (buf.isReading()) buf.invert();
1028 buf.serialBuffer((uint8 *)(decoded.data()), decoded.size());
1029 buf.invert();
1031 CBitmap btm;
1032 btm.load(buf);
1034 gt->Width = gt->DefaultWidth = btm.getWidth();;
1035 gt->Height = gt->DefaultHeight = btm.getHeight();
1037 if (gt->Width == 0 || gt->Height == 0)
1039 nlwarning("Decoded image has width==0 || height==0, check image format. '%s'", data.c_str());
1040 return false;
1043 UTextureMem *texture = driver->createTextureMem(btm.getWidth(), btm.getHeight(), CBitmap::RGBA);
1044 if (!texture)
1046 nlwarning("Failed to create mem texture (%d,%d)", btm.getWidth(), btm.getHeight());
1047 return false;
1050 memcpy(texture->getPointer(), btm.getPixels().getPtr(), btm.getSize() * 4);
1051 gt->Texture = texture;
1052 gt->FromGlobaleTexture = false;
1054 return true;
1057 bool CViewRenderer::loadTextureFromFile(CViewRenderer::SGlobalTexture *gt, const std::string &filename)
1059 // load new file
1060 CIFile ifTmp;
1061 if (ifTmp.open(filename))
1063 CBitmap::loadSize (ifTmp, gt->Width, gt->Height);
1064 gt->DefaultWidth = gt->Width;
1065 gt->DefaultHeight = gt->Height;
1066 if (gt->Width == 0 || gt->Height == 0)
1068 nlwarning("Failed to load the texture '%s', please check image format", filename.c_str());
1069 return false;
1073 gt->Texture = driver->createTextureFile(filename);
1074 gt->FromGlobaleTexture = false;
1076 return true;
1079 sint32 CViewRenderer::newTextureId(const std::string &name)
1081 SImage iTmp;
1082 iTmp.Name = toLowerAscii(name);
1083 iTmp.UVMin = CUV(0,0);
1084 iTmp.UVMax = CUV(1,1);
1086 // lookup global texture with same name
1087 TGlobalTextureList::iterator ite = _GlobalTextures.begin();
1088 while (ite != _GlobalTextures.end())
1090 std::string sText = toLowerAscii(ite->Name);
1091 if (sText == iTmp.Name)
1092 break;
1093 ite++;
1096 if (ite == _GlobalTextures.end())
1098 SGlobalTexture gtTmp;
1099 gtTmp.Name = iTmp.Name;
1100 gtTmp.FromGlobaleTexture = false;
1101 gtTmp.DefaultWidth = gtTmp.Width = 0;
1102 gtTmp.DefaultHeight = gtTmp.Height = 0;
1103 gtTmp.Texture = NULL;
1104 _GlobalTextures.push_back(gtTmp);
1105 ite = _GlobalTextures.end();
1106 ite--;
1108 iTmp.GlobalTexturePtr = &(*ite);
1110 // allocate new texture id
1111 return addSImage(iTmp);
1114 void CViewRenderer::reloadTexture(sint32 texId, const std::string &name, bool uploadDXTC, bool bReleasable)
1116 if ((uint)texId >= _SImageIterators.size())
1118 nlwarning("Invalid texture id %d, maximum is %u", texId, _SImageIterators.size());
1119 return;
1122 SImage *sImage = getSImage(texId);
1123 SGlobalTexture *gt = sImage->GlobalTexturePtr;
1124 if (!gt)
1126 nlwarning("Unknown texture id %d (file %s)", texId, name.c_str());
1127 return;
1130 // create new global texture if previous is atlas
1131 if (gt->FromGlobaleTexture)
1133 uint count = 0;
1134 TSImageList::iterator ite = _SImages.begin();
1135 while (ite != _SImages.end() && count != 2)
1137 // Same global texture ?
1138 if (ite->GlobalTexturePtr == gt)
1139 count++;
1141 ite++;
1144 // create new only when atlas is used by 2+ textures
1145 if (count == 2)
1147 SGlobalTexture gtTmp;
1148 gtTmp.Name = toLowerAscii(name);
1149 gtTmp.FromGlobaleTexture = false;
1150 gtTmp.DefaultWidth = gtTmp.Width = 0;
1151 gtTmp.DefaultHeight = gtTmp.Height = 0;
1152 gtTmp.Texture = NULL;
1153 _GlobalTextures.push_back(gtTmp);
1155 TGlobalTextureList::iterator ite = _GlobalTextures.end();
1156 ite--;
1158 sImage->GlobalTexturePtr = &(*ite);
1159 gt = sImage->GlobalTexturePtr;
1163 NL3D::UTexture *oldTexture = gt->Texture;
1165 std::string sLwrGTName;
1166 if (startsWith(name, "data:image/"))
1168 if (!loadTextureFromString(gt, name))
1169 return;
1171 sLwrGTName = getMD5((uint8 *)name.c_str(), (uint32)name.size()).toString();
1173 else
1175 sLwrGTName = toLowerAscii(name);
1176 std::string filename = CPath::lookup(sLwrGTName, false);
1177 if (filename.empty())
1179 nlwarning("Unable to find file '%s for texture %d", name.c_str(), texId);
1180 return;
1183 if (!loadTextureFromFile(gt, filename))
1185 nlwarning("Unable to load texture from file '%s'", filename.c_str());
1186 return;
1190 gt->Name = sLwrGTName;
1191 gt->Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
1192 gt->Texture->setUploadFormat(uploadDXTC ? UTexture::DXTC5 : UTexture::Auto);
1193 gt->Texture->setReleasable(bReleasable);
1195 // release previous only after successfully loading new one
1196 if (oldTexture)
1198 UTextureFile *tf = dynamic_cast<NL3D::UTextureFile *>(oldTexture);
1199 if (tf)
1201 driver->deleteTextureFile (tf);
1203 else
1205 UTextureMem *tf = dynamic_cast<NL3D::UTextureMem *>(oldTexture);
1206 if (tf) driver->deleteTextureMem(tf);
1212 * createTexture
1214 sint32 CViewRenderer::createTexture (const std::string &sGlobalTextureName,
1215 sint32 offsetX,
1216 sint32 offsetY,
1217 sint32 width,
1218 sint32 height,
1219 bool uploadDXTC,
1220 bool bReleasable
1223 if (sGlobalTextureName.empty()) return -1;
1225 if (startsWith(sGlobalTextureName, "data:image/"))
1226 return createTextureFromDataURL(sGlobalTextureName, uploadDXTC, bReleasable);
1228 // Look if already existing
1229 string sLwrGTName = toLowerAscii(sGlobalTextureName);
1230 TGlobalTextureList::iterator ite = _GlobalTextures.begin();
1231 while (ite != _GlobalTextures.end())
1233 std::string sText = toLowerAscii(ite->Name);
1234 if (sText == sLwrGTName)
1235 break;
1236 ite++;
1239 // If global texture not exists create it
1240 if (ite == _GlobalTextures.end())
1242 string filename = CPath::lookup (sLwrGTName, false);
1243 if (filename.empty() ) return -1;
1245 SGlobalTexture gtTmp;
1246 gtTmp.Name = sLwrGTName;
1248 if (!loadTextureFromFile(&gtTmp, filename))
1249 return -1;
1251 gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
1252 if(uploadDXTC)
1253 gtTmp.Texture->setUploadFormat(UTexture::DXTC5);
1254 gtTmp.Texture->setReleasable(bReleasable);
1256 _GlobalTextures.push_back(gtTmp);
1257 ite = _GlobalTextures.end();
1258 ite--;
1261 // Add a texture with reference to the i th global texture
1262 SImage iTmp;
1264 // Set default parameters
1265 if (width == -1)
1266 width = ite->DefaultWidth;
1267 if (height == -1)
1268 height = ite->DefaultHeight;
1270 iTmp.Name = sLwrGTName;
1271 iTmp.GlobalTexturePtr = &(*ite);
1272 iTmp.UVMin = CUV(((float)offsetX)/ite->Width , ((float)offsetY)/ite->Height);
1273 iTmp.UVMax = CUV(((float)offsetX+width)/ite->Width , ((float)offsetY+height)/ite->Height);
1274 sint32 TextID = addSImage(iTmp);
1275 //nlwarning("SIMAGE ADDED: id = %d, name = %s", TextID, iTmp.Name.c_str());
1277 // Insert / replace in map.
1278 // TMP TMP FIX NICO
1279 //_TextureMap.insert( make_pair(iTmp.Name, TextID) );
1282 return TextID;
1285 sint32 CViewRenderer::createTextureFromDataURL(const std::string &data, bool uploadDXTC, bool bReleasable)
1287 if (!startsWith(data, "data:image/"))
1288 return -1;
1290 size_t pos = data.find(";base64,");
1291 if (pos == std::string::npos)
1293 nlwarning("Failed to parse dataURL (not base64?) '%s'", data.c_str());
1294 return -1;
1297 std::string md5hash = getMD5((uint8 *)data.c_str(), (uint32)data.size()).toString();
1299 TGlobalTextureList::iterator ite = _GlobalTextures.begin();
1300 while (ite != _GlobalTextures.end())
1302 if (md5hash == ite->Name)
1303 break;
1304 ite++;
1307 // If global texture not exists create it
1308 if (ite == _GlobalTextures.end())
1310 SGlobalTexture gtTmp;
1311 if (!loadTextureFromString(&gtTmp, data))
1312 return -1;
1314 gtTmp.Name = md5hash;
1315 gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
1316 gtTmp.Texture->setReleasable(bReleasable);
1317 if(uploadDXTC)
1318 gtTmp.Texture->setUploadFormat(UTexture::DXTC5);
1320 _GlobalTextures.push_back(gtTmp);
1321 ite = _GlobalTextures.end();
1322 ite--;
1325 // Add a texture with reference to the i th global texture
1326 SImage iTmp;
1328 // Set default parameters
1329 iTmp.Name = data;
1330 iTmp.GlobalTexturePtr = &(*ite);
1331 iTmp.UVMin = CUV(0.f , 0.f);
1332 iTmp.UVMax = CUV(1.f , 1.f);
1333 sint32 TextID = addSImage(iTmp);
1335 return TextID;
1338 void CViewRenderer::updateTexturePos(const std::string &texturefileName, sint32 offsetX /*=0*/, sint32 offsetY /*=0*/, sint32 width /*=-1*/, sint32 height /*=-1*/)
1340 sint32 id = getTextureIdFromName (texturefileName);
1341 if (id == -1)
1343 nlwarning("Unknwown texture %s, can't update pos", texturefileName.c_str());
1344 return;
1346 SImage *im = getSImage(id);
1347 nlassert(im);
1348 // Set default parameters
1349 sint32 gw = im->GlobalTexturePtr->Width;
1350 sint32 gh = im->GlobalTexturePtr->Height;
1351 if (width == -1)
1352 width = gw;
1353 if (height == -1)
1354 height = gh;
1355 im->UVMin = CUV(((float)offsetX)/gw , ((float)offsetY)/gh);
1356 im->UVMax = CUV(((float)offsetX+width)/gw, ((float)offsetY+height)/gh);
1361 * getGlobalTexture
1363 NL3D::UTexture *CViewRenderer::getGlobalTexture(const std::string &name)
1365 string sLwrGTName = NLMISC::toLowerAscii(name);
1366 TGlobalTextureList::iterator ite = _GlobalTextures.begin();
1367 while (ite != _GlobalTextures.end())
1369 std::string sText = NLMISC::toLowerAscii(ite->Name);
1370 if (sText == sLwrGTName)
1371 break;
1372 ite++;
1374 if (ite != _GlobalTextures.end())
1376 return ite->Texture;
1378 return NULL;
1382 * deleteTexture
1384 void CViewRenderer::deleteTexture (sint32 textureId)
1386 // Checks
1387 nlassert ((uint)textureId < _SImageIterators.size());
1388 if (_SImageIterators[textureId] == _SImages.end())
1390 nlwarning("Can't delete texture with name %s", getTextureNameFromId(textureId).c_str());
1391 nlassert(0);
1392 return;
1395 // Backup global texture pointer
1396 SGlobalTexture *gt = getSImage(textureId)->GlobalTexturePtr;
1398 // Erase only texture from global texture
1399 if (!(gt->FromGlobaleTexture))
1401 //nlwarning("Removing texture with id %d", (int) textureId);
1402 // Erase the SImage
1403 //nlwarning("SIMAGE REMOVE : id = %x, name = %s", (int) textureId, getSImage(textureId)->Name.c_str());
1406 removeSImage (textureId);
1408 // Check if someone else use this global texture..
1409 TSImageList::iterator ite = _SImages.begin();
1410 while (ite != _SImages.end())
1412 // Same global texture ?
1413 if (ite->GlobalTexturePtr == gt)
1414 break;
1416 ite++;
1419 // Global texture still used ?
1420 if (ite == _SImages.end())
1422 //nlwarning("REMOVE GLOBAL TEXTURE : id of simage = %x", (int) textureId);
1423 // No, remove the global texture
1424 for (TGlobalTextureList::iterator iteGT = _GlobalTextures.begin(); iteGT != _GlobalTextures.end(); iteGT++)
1426 // This one ?
1427 if (&(*iteGT) == gt)
1429 if (iteGT->Texture == NULL)
1430 return;
1431 // Remove this global texture
1432 UTextureFile *tf = dynamic_cast<NL3D::UTextureFile *>(iteGT->Texture);
1433 if (tf)
1435 driver->deleteTextureFile (tf);
1437 else
1439 UTextureMem *tf = dynamic_cast<NL3D::UTextureMem *>(iteGT->Texture);
1440 if (tf) driver->deleteTextureMem(tf);
1442 _GlobalTextures.erase (iteGT);
1443 return;
1446 // Global texture has not been found
1447 nlstop;
1452 bool CViewRenderer::getTexture( NLMISC::CBitmap &bm, const std::string &name )
1454 TTextureMap::const_iterator itr = _TextureMap.find( name );
1455 if( itr == _TextureMap.end() )
1456 return false;
1458 sint32 id = itr->second;
1459 SImage *si = getSImage( id );
1460 NLMISC::CBitmap *src = si->GlobalTexturePtr->Texture->generateDatas();
1462 if( src->getPixelFormat() != NLMISC::CBitmap::RGBA )
1463 return false;
1465 uint x0 = (uint)( si->UVMin.U * si->GlobalTexturePtr->Width );
1466 uint y0 = (uint)( si->UVMin.V * si->GlobalTexturePtr->Height );
1467 uint x1 = (uint)( si->UVMax.U * si->GlobalTexturePtr->Width );
1468 uint y1 = (uint)( si->UVMax.V * si->GlobalTexturePtr->Height );
1470 if( x1 == x0 )
1471 return false;
1473 if( y1 == y0 )
1474 return false;
1476 bm.resize( x1 - x0, y1 - y0 );
1477 bm.blit( *src, x0, y0, ( x1 - x0 ), ( y1 - y0 ), 0, 0 );
1479 return true;
1482 void CViewRenderer::getTextureNames( std::vector< std::string > &textures )
1484 TTextureMap::const_iterator itr = _TextureMap.begin();
1485 while( itr != _TextureMap.end() )
1487 textures.push_back( itr->first );
1488 ++itr;
1493 * getTextureIdFromName
1495 sint32 CViewRenderer::getTextureIdFromName (const string &sName) const
1497 if(sName.empty())
1498 return -1;
1500 // convert to lowCase
1501 string nameLwr = toLowerAscii(sName);
1503 string::size_type stripPng = nameLwr.find(".png");
1504 if (stripPng != string::npos)
1506 nameLwr[stripPng + 1] = 't';
1507 nameLwr[stripPng + 2] = 'g';
1508 nameLwr[stripPng + 3] = 'a';
1511 // Search in map
1512 TTextureMap::const_iterator it= _TextureMap.find(nameLwr);
1513 if( it==_TextureMap.end() )
1514 return -1;
1515 else
1516 return it->second;
1520 * getTextureNameFromId
1522 std::string CViewRenderer::getTextureNameFromId (sint32 TxID)
1524 if ((TxID < 0) || (TxID >= (sint32)_SImageIterators.size()))
1525 return "";
1526 SImage *img = getSImage(TxID);
1527 return img->Name;
1531 * getTextureSizeFromName
1533 void CViewRenderer::getTextureSizeFromId (sint32 id, sint32 &width, sint32 &height)
1535 if ((id < 0) || (id >= (sint32)_SImageIterators.size()))
1537 width = height = 0;
1539 else
1541 SImage &rImage = *getSImage(id);
1542 width = (sint32)(((rImage.UVMax.U - rImage.UVMin.U)*rImage.GlobalTexturePtr->Width / rImage.GlobalTexturePtr->Scale)+0.5f);
1543 height = (sint32)(((rImage.UVMax.V - rImage.UVMin.V)*rImage.GlobalTexturePtr->Height / rImage.GlobalTexturePtr->Scale)+0.5f);
1547 * getTextureColor
1549 CRGBA CViewRenderer::getTextureColor(sint32 id, sint32 x, sint32 y)
1551 if ((id < 0) || (id >= (sint32)_SImageIterators.size()))
1553 return CRGBA(255,255,255);
1556 SImage &rImage = *getSImage(id);
1557 SGlobalTexture &rGT = *rImage.GlobalTexturePtr;
1558 // get (possibly) scaled width/height
1559 sint32 width, height;
1560 getTextureSizeFromId(id, width, height);
1561 if (width == 0 || height == 0)
1562 return CRGBA(255,255,255);
1563 float xRatio = ((float)x) / ((float)(width));
1564 float yRatio = ((float)y) / ((float)(height));
1565 UTexture *pTF = rGT.Texture;
1566 sint32 xConv = (sint32)((rImage.UVMin.U + xRatio * (rImage.UVMax.U - rImage.UVMin.U))*rGT.Width+0.5f);
1567 sint32 yConv = (rGT.Height-1)-(sint32)((rImage.UVMin.V + yRatio * (rImage.UVMax.V - rImage.UVMin.V))*rGT.Height+0.5f);
1568 return pTF->getPixelColor(xConv, yConv);
1571 // ***************************************************************************
1572 sint32 CViewRenderer::getTypoTextureW(char c)
1574 if ((c>=0) && (c<NumTypoChar))
1575 return _TypoCharWs[(uint)c];
1576 else
1577 return 1;
1580 // ***************************************************************************
1581 sint32 CViewRenderer::getTypoTextureH(char /* c */)
1583 return _TypoH;
1586 // ***************************************************************************
1587 sint32 CViewRenderer::getTypoTextureId(char c)
1589 if ((c>=0) && (c<NumTypoChar))
1590 return _TypoCharToTextureIds[(uint)c];
1591 else
1592 return -1;
1596 * flush
1598 void CViewRenderer::flush ()
1600 H_AUTO ( RZ_Interface_ViewRenderer_flush )
1602 // Run All layers.
1603 for(uint layerId=0;layerId<VR_NUM_LAYER;layerId++)
1605 if(_EmptyLayer[layerId])
1606 continue;
1608 // **** Run all Global Textures
1609 TGlobalTextureList::iterator ite = _GlobalTextures.begin();
1610 while (ite != _GlobalTextures.end())
1612 // texture not loaded yet
1613 if (ite->Texture == NULL)
1615 ++ite;
1616 continue;
1619 // TMP TMP
1620 // volatile SGlobalTexture *sg = &(*ite);
1621 CLayer &layer= ite->Layers[layerId];
1622 if(layer.NbQuads>0 || !layer.Tris.empty())
1624 // setup the global texture to material
1625 _Material.setTexture(0, ite->Texture);
1627 // Special Case if _WorldSpaceTransformation and _WorldSpaceScale, enable bilinear
1628 if(_Bilinear || (_WorldSpaceTransformation && _WorldSpaceScale))
1629 ite->Texture->setFilterMode(UTexture::Linear, UTexture::LinearMipMapOff);
1631 // draw quads and empty list
1632 if (layer.NbQuads != 0)
1634 driver->drawQuads (&(layer.Quads[0]), layer.NbQuads, _Material);
1635 layer.NbQuads = 0;
1638 if (!layer.Tris.empty())
1640 driver->drawTriangles(layer.Tris, _Material);
1641 layer.Tris.clear();
1644 // Special Case if _WorldSpaceTransformation and _WorldSpaceScale, reset
1645 if(_Bilinear || (_WorldSpaceTransformation && _WorldSpaceScale))
1646 ite->Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
1648 if (!layer.FilteredAlphaBlendedQuads.empty() ||
1649 !layer.FilteredAlphaBlendedTris.empty() ||
1650 !layer.FilteredAdditifQuads.empty() ||
1651 !layer.FilteredAdditifTris.empty())
1653 // setup the global texture to material
1654 _Material.setTexture(0, ite->Texture);
1656 // force filtering
1657 ite->Texture->setFilterMode(UTexture::Linear, UTexture::LinearMipMapOff);
1658 // alpha blended
1659 if (!layer.FilteredAlphaBlendedQuads.empty())
1661 driver->drawQuads (&(layer.FilteredAlphaBlendedQuads[0]), (uint32)layer.FilteredAlphaBlendedQuads.size(), _Material);
1662 layer.FilteredAlphaBlendedQuads.clear();
1664 if (!layer.FilteredAlphaBlendedTris.empty())
1666 driver->drawTriangles(layer.FilteredAlphaBlendedTris, _Material);
1667 layer.FilteredAlphaBlendedTris.clear();
1669 // additif
1670 if (!layer.FilteredAdditifQuads.empty() ||
1671 !layer.FilteredAdditifTris.empty())
1673 _Material.setBlendFunc (NL3D::UMaterial::one, NL3D::UMaterial::one);
1674 if (!layer.FilteredAdditifQuads.empty())
1676 driver->drawQuads (&(layer.FilteredAdditifQuads[0]), (uint32)layer.FilteredAdditifQuads.size(), _Material);
1677 layer.FilteredAdditifQuads.clear();
1679 if (!layer.FilteredAdditifTris.empty())
1681 driver->drawTriangles(layer.FilteredAdditifTris, _Material);
1682 layer.FilteredAdditifTris.clear();
1684 // restore alpha blend
1685 _Material.setBlendFunc (NL3D::UMaterial::srcalpha, NL3D::UMaterial::invsrcalpha);
1687 ite->Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
1689 ite++;
1692 // **** Display Computed Strings of this layer
1693 if (_WorldSpaceTransformation)
1694 textcontext->flushRenderBufferUnProjected(_StringRBLayers[layerId], false);
1695 else
1696 textcontext->flushRenderBuffer(_StringRBLayers[layerId]);
1698 // flushed
1699 _EmptyLayer[layerId]= true;
1705 * init the map _IndexesToTextures
1707 void CViewRenderer::initIndexesToTextureIds()
1709 char buf[20];
1710 _IndexesToTextureIds.clear();
1711 for (uint i = 0; i < 10; i++)
1713 sprintf (buf, "numbers_%d.tga", i);
1714 _IndexesToTextureIds.push_back (getTextureIdFromName(buf));
1716 _FigurSeparatorTextureId = getTextureIdFromName("Numbers_sep.tga");
1717 _FigurBlankId = getTextureIdFromName("numbers_blank.tga");
1718 _BlankId = getTextureIdFromName("blank.tga");
1720 SImage *blank = getSImage(_BlankId);
1721 if (blank)
1723 _BlankGlobalTexture = blank->GlobalTexturePtr;
1724 _BlankUV = 0.5f * (blank->UVMin + blank->UVMax);
1726 else
1728 _BlankUV.set(0.f, 0.f);
1732 // Init size
1733 if(_IndexesToTextureIds[0]!=-1)
1735 getTextureSizeFromId (_IndexesToTextureIds[0], _WFigurTexture, _HFigurTexture);
1737 if (_FigurSeparatorTextureId != -1)
1739 getTextureSizeFromId (_FigurSeparatorTextureId, _WFigurSeparatorTexture, _HFigurSeparatorTexture);
1747 void CViewRenderer::initTypo()
1749 _TypoH = 0;
1751 // since filename dose not support special char (?,. ....), specify a map from char to string.
1752 map<char, string> specialCharMap;
1753 specialCharMap['?']= "question";
1756 char buf[256];
1757 // For all supported chars (if tga exist)
1758 for (uint i = 0; i < NumTypoChar; i++)
1760 // Get the token string for this char.
1761 string token;
1762 map<char, string>::iterator it= specialCharMap.find(i);
1763 // General case
1764 if(it==specialCharMap.end())
1765 token= (char)i;
1766 else
1767 token= it->second;
1769 // get the fileName
1770 sprintf (buf, "typo_%s.tga", token.c_str());
1771 sint32 id = getTextureIdFromName(buf);
1772 if(id>=0)
1774 _TypoCharToTextureIds[i]= id;
1775 sint32 w,h;
1776 getTextureSizeFromId (id, w, h);
1777 _TypoCharWs[i]= w;
1778 _TypoH = h;
1780 else
1782 _TypoCharToTextureIds[i]= -1;
1783 // simulate a space.
1784 _TypoCharWs[i]= 1;
1791 * needClipping
1793 bool CViewRenderer::needClipping (const CQuad &q)
1795 if ((q.V0.x >= _XMin) && (q.V0.y >= _YMin) && (q.V2.x <= _XMax) && (q.V2.y <= _YMax))
1796 return false;
1797 else
1798 return true;
1802 * clip
1804 void CViewRenderer::clip (CQuadColorUV &qout, const CQuadColorUV &qin, uint rot)
1806 float ratio;
1808 qout = qin;
1810 if (rot & 1)
1812 // must reverse U & V during clipping
1813 if (qin.V0.x < _XMin)
1815 ratio = ((float)(_XMin - qin.V0.x))/((float)(qin.V1.x - qin.V0.x));
1816 qout.V3.x = qout.V0.x = _XMin;
1817 qout.Uv0.V += ratio*(qin.Uv1.V-qin.Uv0.V);
1818 qout.Uv3.V += ratio*(qin.Uv2.V-qin.Uv3.V);
1821 if (qin.V0.y < _YMin)
1823 ratio = ((float)(_YMin - qin.V0.y))/((float)(qin.V3.y - qin.V0.y));
1824 qout.V1.y = qout.V0.y = _YMin;
1825 qout.Uv0.U += ratio*(qin.Uv3.U-qin.Uv0.U);
1826 qout.Uv1.U += ratio*(qin.Uv2.U-qin.Uv1.U);
1829 if (qin.V2.x > _XMax)
1831 ratio = ((float)(_XMax - qin.V2.x))/((float)(qin.V3.x - qin.V2.x));
1832 qout.V2.x = qout.V1.x = _XMax;
1833 qout.Uv2.V += ratio*(qin.Uv3.V-qin.Uv2.V);
1834 qout.Uv1.V += ratio*(qin.Uv0.V-qin.Uv1.V);
1837 if (qin.V2.y > _YMax)
1839 ratio = ((float)(_YMax - qin.V2.y))/((float)(qin.V1.y - qin.V2.y));
1840 qout.V2.y = qout.V3.y = _YMax;
1841 qout.Uv2.U += ratio*(qin.Uv1.U-qin.Uv2.U);
1842 qout.Uv3.U += ratio*(qin.Uv0.U-qin.Uv3.U);
1845 else
1847 if (qin.V0.x < _XMin)
1849 ratio = ((float)(_XMin - qin.V0.x))/((float)(qin.V1.x - qin.V0.x));
1850 qout.V3.x = qout.V0.x = _XMin;
1851 qout.Uv0.U += ratio*(qin.Uv1.U-qin.Uv0.U);
1852 qout.Uv3.U += ratio*(qin.Uv2.U-qin.Uv3.U);
1855 if (qin.V0.y < _YMin)
1857 ratio = ((float)(_YMin - qin.V0.y))/((float)(qin.V3.y - qin.V0.y));
1858 qout.V1.y = qout.V0.y = _YMin;
1859 qout.Uv0.V += ratio*(qin.Uv3.V-qin.Uv0.V);
1860 qout.Uv1.V += ratio*(qin.Uv2.V-qin.Uv1.V);
1863 if (qin.V2.x > _XMax)
1865 ratio = ((float)(_XMax - qin.V2.x))/((float)(qin.V3.x - qin.V2.x));
1866 qout.V2.x = qout.V1.x = _XMax;
1867 qout.Uv2.U += ratio*(qin.Uv3.U-qin.Uv2.U);
1868 qout.Uv1.U += ratio*(qin.Uv0.U-qin.Uv1.U);
1871 if (qin.V2.y > _YMax)
1873 ratio = ((float)(_YMax - qin.V2.y))/((float)(qin.V1.y - qin.V2.y));
1874 qout.V2.y = qout.V3.y = _YMax;
1875 qout.Uv2.V += ratio*(qin.Uv1.V-qin.Uv2.V);
1876 qout.Uv3.V += ratio*(qin.Uv0.V-qin.Uv3.V);
1882 * clip with uv2
1884 void CViewRenderer::clip (CQuadColorUV2 &qout, const CQuadColorUV2 &qin)
1886 float ratio;
1888 qout = qin;
1890 if (qin.V0.x < _XMin)
1892 ratio = ((float)(_XMin - qin.V0.x))/((float)(qin.V1.x - qin.V0.x));
1893 qout.V3.x = qout.V0.x = _XMin;
1894 qout.Uv0.U += ratio*(qin.Uv1.U-qin.Uv0.U);
1895 qout.Uv3.U += ratio*(qin.Uv2.U-qin.Uv3.U);
1896 qout.Uv02.U += ratio*(qin.Uv12.U-qin.Uv02.U);
1897 qout.Uv32.U += ratio*(qin.Uv22.U-qin.Uv32.U);
1900 if (qin.V0.y < _YMin)
1902 ratio = ((float)(_YMin - qin.V0.y))/((float)(qin.V3.y - qin.V0.y));
1903 qout.V1.y = qout.V0.y = _YMin;
1904 qout.Uv0.V += ratio*(qin.Uv3.V-qin.Uv0.V);
1905 qout.Uv1.V += ratio*(qin.Uv2.V-qin.Uv1.V);
1906 qout.Uv02.V += ratio*(qin.Uv32.V-qin.Uv02.V);
1907 qout.Uv12.V += ratio*(qin.Uv22.V-qin.Uv12.V);
1910 if (qin.V2.x > _XMax)
1912 ratio = ((float)(_XMax - qin.V2.x))/((float)(qin.V3.x - qin.V2.x));
1913 qout.V2.x = qout.V1.x = _XMax;
1914 qout.Uv2.U += ratio*(qin.Uv3.U-qin.Uv2.U);
1915 qout.Uv1.U += ratio*(qin.Uv0.U-qin.Uv1.U);
1916 qout.Uv22.U += ratio*(qin.Uv32.U-qin.Uv22.U);
1917 qout.Uv12.U += ratio*(qin.Uv02.U-qin.Uv12.U);
1920 if (qin.V2.y > _YMax)
1922 ratio = ((float)(_YMax - qin.V2.y))/((float)(qin.V1.y - qin.V2.y));
1923 qout.V2.y = qout.V3.y = _YMax;
1924 qout.Uv2.V += ratio*(qin.Uv1.V-qin.Uv2.V);
1925 qout.Uv3.V += ratio*(qin.Uv0.V-qin.Uv3.V);
1926 qout.Uv22.V += ratio*(qin.Uv12.V-qin.Uv22.V);
1927 qout.Uv32.V += ratio*(qin.Uv02.V-qin.Uv32.V);
1932 // ***************************************************************************
1934 * putQuadInLayer : put a quad in a specific layer of a specific texture
1936 void CViewRenderer::putQuadInLayer (SGlobalTexture &gt, sint layerId, const NLMISC::CQuadColorUV &qcoluv, uint rot)
1938 layerId+= VR_BIAS_LAYER;
1939 nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
1940 CLayer &layer= gt.Layers[layerId];
1942 // Clipping part
1943 if (!needClipping(qcoluv))
1945 // World space transformation
1946 if (_WorldSpaceTransformation)
1948 NLMISC::CQuadColorUV qcolor = qcoluv;
1949 worldSpaceTransformation (qcolor);
1951 // No need to clip the quad
1952 if (layer.NbQuads == layer.Quads.size())
1953 layer.Quads.push_back (qcolor);
1954 else
1955 layer.Quads[layer.NbQuads] = qcolor;
1957 else
1959 // No need to clip the quad
1960 if (layer.NbQuads == layer.Quads.size())
1961 layer.Quads.push_back (qcoluv);
1962 else
1963 layer.Quads[layer.NbQuads] = qcoluv;
1966 ++layer.NbQuads;
1968 else
1970 CQuadColorUV qclipped;
1971 clip (qclipped, qcoluv, rot);
1973 // World space transformation
1974 if (_WorldSpaceTransformation)
1975 worldSpaceTransformation (qclipped);
1977 if (layer.NbQuads == layer.Quads.size())
1978 layer.Quads.push_back (qclipped);
1979 else
1980 layer.Quads[layer.NbQuads] = qclipped;
1981 ++layer.NbQuads;
1984 // layer filled
1985 _EmptyLayer[layerId]= false;
1989 // ***************************************************************************
1990 void CViewRenderer::addSystemTexture(TSystemTexture e, const char *s)
1992 _SystemTextures[e].Id= getTextureIdFromName(s);
1993 if(_SystemTextures[e].Id!=-1)
1995 getTextureSizeFromId(_SystemTextures[e].Id, _SystemTextures[e].W, _SystemTextures[e].H);
1999 // ***************************************************************************
2000 void CViewRenderer::initSystemTextures()
2002 addSystemTexture(QuantityCrossTexture, "w_quantity.tga");
2003 addSystemTexture(DefaultBrickTexture, "brick_default.tga");
2004 addSystemTexture(DefaultItemTexture, "item_default.tga");
2005 addSystemTexture(ItemPlanTexture, "item_plan_over.tga");
2006 addSystemTexture(SkillTexture, "skill.tga");
2007 addSystemTexture(ItemEnchantedTexture, "sapload.tga");
2008 addSystemTexture(DragCopyTexture, "W_copy.tga");
2009 addSystemTexture(ItemWornedTexture, "ico_task_failed.tga");
2010 addSystemTexture(OutOfRangeTexture, "ico_out_of_range.tga");
2011 addSystemTexture(RegenTexture, "regen.tga");
2012 addSystemTexture(RegenBackTexture, "regen_back.tga");
2013 addSystemTexture(GlowStarTexture, "glow_star_24.tga");
2014 addSystemTexture(ItemLockedByOwnerTexture, "r2ed_toolbar_lock_small.tga");
2018 // ***************************************************************************
2019 URenderStringBuffer *CViewRenderer::getStringRenderBuffer(sint layerId)
2021 layerId+= VR_BIAS_LAYER;
2022 nlassert(layerId>=0 && layerId<VR_NUM_LAYER);
2024 return _StringRBLayers[layerId];
2027 // ***************************************************************************
2028 void CViewRenderer::drawWiredQuad(sint32 x, sint32 y, sint32 width, sint32 height, NLMISC::CRGBA col /*=NLMISC::CRGBA::White*/)
2030 driver->drawWiredQuad(x * _OneOverScreenW, y * _OneOverScreenH, (x + width) * _OneOverScreenW, (y + height) * _OneOverScreenH, col);
2033 // ***************************************************************************
2034 void CViewRenderer::drawFilledQuad(sint32 x, sint32 y, sint32 width, sint32 height, NLMISC::CRGBA col /*=NLMISC::CRGBA::White*/)
2036 driver->drawQuad(x * _OneOverScreenW, y * _OneOverScreenH, (x + width) * _OneOverScreenW, (y + height) * _OneOverScreenH, col);
2041 // ***************************************************************************
2042 void CViewRenderer::drawCustom (sint32 x, sint32 y, sint32 width, sint32 height, CRGBA col, UMaterial Mat)
2044 float dstXmin, dstYmin, dstXmax, dstYmax;
2046 // Is totally clipped ?
2047 if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
2048 (y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
2049 return;
2051 flush();
2053 // Initialize quad
2054 dstXmin = (float)(x) * _OneOverScreenW;
2055 dstYmin = (float)(y) * _OneOverScreenH;
2056 dstXmax = (float)(x + width) * _OneOverScreenW;
2057 dstYmax = (float)(y + height) * _OneOverScreenH;
2059 CQuadColorUV2 qcoluv2;
2060 qcoluv2.V0.set (dstXmin, dstYmin, 0);
2061 qcoluv2.V1.set (dstXmax, dstYmin, 0);
2062 qcoluv2.V2.set (dstXmax, dstYmax, 0);
2063 qcoluv2.V3.set (dstXmin, dstYmax, 0);
2065 qcoluv2.Color0 = qcoluv2.Color1 = qcoluv2.Color2 = qcoluv2.Color3 = col;
2067 qcoluv2.Uv0.U = 0; qcoluv2.Uv0.V = 1;
2068 qcoluv2.Uv1.U = 1; qcoluv2.Uv1.V = 1;
2069 qcoluv2.Uv2.U = 1; qcoluv2.Uv2.V = 0;
2070 qcoluv2.Uv3.U = 0; qcoluv2.Uv3.V = 0;
2072 qcoluv2.Uv02.U = 0; qcoluv2.Uv02.V = 1;
2073 qcoluv2.Uv12.U = 1; qcoluv2.Uv12.V = 1;
2074 qcoluv2.Uv22.U = 1; qcoluv2.Uv22.V = 0;
2075 qcoluv2.Uv32.U = 0; qcoluv2.Uv32.V = 0;
2077 // Clipping part
2078 CQuadColorUV2 qcoluv2_clipped;
2079 if (!needClipping(qcoluv2))
2081 // No need to clip the quad
2082 qcoluv2_clipped = qcoluv2;
2084 else
2086 clip (qcoluv2_clipped, qcoluv2);
2089 // World space transformation
2090 if (_WorldSpaceTransformation)
2091 worldSpaceTransformation (qcoluv2_clipped);
2093 // Draw clipped quad
2094 driver->drawQuads (&qcoluv2_clipped, 1, Mat);
2097 // ***************************************************************************
2098 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)
2100 float dstXmin, dstYmin, dstXmax, dstYmax;
2102 // Is totally clipped ?
2103 if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
2104 (y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
2105 return;
2107 flush();
2109 // Initialize quad
2110 dstXmin = (float)(x) * _OneOverScreenW;
2111 dstYmin = (float)(y) * _OneOverScreenH;
2112 dstXmax = (float)(x + width) * _OneOverScreenW;
2113 dstYmax = (float)(y + height) * _OneOverScreenH;
2115 CQuadColorUV qcoluv;
2116 qcoluv.V0.set (dstXmin, dstYmin, 0);
2117 qcoluv.V1.set (dstXmax, dstYmin, 0);
2118 qcoluv.V2.set (dstXmax, dstYmax, 0);
2119 qcoluv.V3.set (dstXmin, dstYmax, 0);
2121 qcoluv.Color0 = qcoluv.Color1 = qcoluv.Color2 = qcoluv.Color3 = col;
2123 qcoluv.Uv0.U = uv0Min.U; qcoluv.Uv0.V = uv0Max.V;
2124 qcoluv.Uv1.U = uv0Max.U; qcoluv.Uv1.V = uv0Max.V;
2125 qcoluv.Uv2.U = uv0Max.U; qcoluv.Uv2.V = uv0Min.V;
2126 qcoluv.Uv3.U = uv0Min.U; qcoluv.Uv3.V = uv0Min.V;
2129 // Clipping part
2130 CQuadColorUV qcoluv_clipped;
2131 if (!needClipping(qcoluv))
2133 // No need to clip the quad
2134 qcoluv_clipped = qcoluv;
2136 else
2138 clip (qcoluv_clipped, qcoluv, 0);
2141 // Draw clipped quad
2142 driver->drawQuads (&qcoluv_clipped, 1, Mat);
2145 // ***************************************************************************
2146 void CViewRenderer::drawCustom (sint32 x, sint32 y, sint32 width, sint32 height,
2147 const CUV &uv0Min, const CUV &uv0Max, const CUV &uv1Min, const CUV &uv1Max,
2148 NLMISC::CRGBA col, NL3D::UMaterial Mat)
2150 float dstXmin, dstYmin, dstXmax, dstYmax;
2152 // Is totally clipped ?
2153 if ((x > (_ClipX+_ClipW)) || ((x+width) < _ClipX) ||
2154 (y > (_ClipY+_ClipH)) || ((y+height) < _ClipY))
2155 return;
2157 flush();
2159 // Initialize quad
2160 dstXmin = (float)(x) * _OneOverScreenW;
2161 dstYmin = (float)(y) * _OneOverScreenH;
2162 dstXmax = (float)(x + width) * _OneOverScreenW;
2163 dstYmax = (float)(y + height) * _OneOverScreenH;
2165 CQuadColorUV2 qcoluv2;
2166 qcoluv2.V0.set (dstXmin, dstYmin, 0);
2167 qcoluv2.V1.set (dstXmax, dstYmin, 0);
2168 qcoluv2.V2.set (dstXmax, dstYmax, 0);
2169 qcoluv2.V3.set (dstXmin, dstYmax, 0);
2171 qcoluv2.Color0 = qcoluv2.Color1 = qcoluv2.Color2 = qcoluv2.Color3 = col;
2173 qcoluv2.Uv0.U = uv0Min.U; qcoluv2.Uv0.V = uv0Max.V;
2174 qcoluv2.Uv1.U = uv0Max.U; qcoluv2.Uv1.V = uv0Max.V;
2175 qcoluv2.Uv2.U = uv0Max.U; qcoluv2.Uv2.V = uv0Min.V;
2176 qcoluv2.Uv3.U = uv0Min.U; qcoluv2.Uv3.V = uv0Min.V;
2178 qcoluv2.Uv02.U = uv1Min.U; qcoluv2.Uv02.V = uv1Max.V;
2179 qcoluv2.Uv12.U = uv1Max.U; qcoluv2.Uv12.V = uv1Max.V;
2180 qcoluv2.Uv22.U = uv1Max.U; qcoluv2.Uv22.V = uv1Min.V;
2181 qcoluv2.Uv32.U = uv1Min.U; qcoluv2.Uv32.V = uv1Min.V;
2183 // Clipping part
2184 CQuadColorUV2 qcoluv2_clipped;
2185 if (!needClipping(qcoluv2))
2187 // No need to clip the quad
2188 qcoluv2_clipped = qcoluv2;
2190 else
2192 clip (qcoluv2_clipped, qcoluv2);
2195 // World space transformation
2196 if (_WorldSpaceTransformation)
2197 worldSpaceTransformation (qcoluv2_clipped);
2199 // Draw clipped quad
2200 driver->drawQuads (&qcoluv2_clipped, 1, Mat);
2203 // ***************************************************************************
2205 CViewRenderer::CTextureId::~CTextureId ()
2207 if (_TextureId>=0)
2208 CViewRenderer::getInstance()->deleteTexture(_TextureId);
2209 _TextureId = -1;
2212 // ***************************************************************************
2214 bool CViewRenderer::CTextureId::setTexture (const char *textureName, sint32 offsetX, sint32 offsetY, sint32 width, sint32 height,
2215 bool uploadDXTC, bool bReleasable)
2217 CViewRenderer &rVR = *CViewRenderer::getInstance();
2218 if (_TextureId>=0)
2219 rVR.deleteTexture(_TextureId);
2220 _TextureId = rVR.getTextureIdFromName(textureName);
2221 if (_TextureId<0)
2222 _TextureId = rVR.createTexture (textureName, offsetX, offsetY, width, height, uploadDXTC, bReleasable);
2224 return _TextureId >= 0;
2227 // ***************************************************************************
2228 void CViewRenderer::CTextureId::clear()
2230 if (_TextureId >= 0)
2232 CViewRenderer::getInstance()->deleteTexture(_TextureId);
2233 _TextureId = -1;
2237 // ***************************************************************************
2238 void CViewRenderer::CTextureId::serial(NLMISC::IStream &f)
2240 std::string texName;
2241 if (f.isReading())
2243 f.serial(texName);
2244 setTexture(texName.c_str());
2246 else
2248 CViewRenderer &rVR = *CViewRenderer::getInstance();
2249 texName = rVR.getTextureNameFromId(_TextureId);
2250 f.serial(texName);
2254 // ***************************************************************************
2256 void CViewRenderer::worldSpaceTransformation (NLMISC::CQuadColorUV &qcoluv)
2258 // set the world Z
2259 qcoluv.V0.z = _CurrentZ;
2260 qcoluv.V1.z = _CurrentZ;
2261 qcoluv.V2.z = _CurrentZ;
2262 qcoluv.V3.z = _CurrentZ;
2264 // for scaled interface, apply the scale matrix
2265 qcoluv.V0= _WorldSpaceMatrix * qcoluv.V0;
2266 qcoluv.V1= _WorldSpaceMatrix * qcoluv.V1;
2267 qcoluv.V2= _WorldSpaceMatrix * qcoluv.V2;
2268 qcoluv.V3= _WorldSpaceMatrix * qcoluv.V3;
2270 // unproject
2271 qcoluv.V0 = _CameraFrustum.unProjectZ(qcoluv.V0);
2272 qcoluv.V1 = _CameraFrustum.unProjectZ(qcoluv.V1);
2273 qcoluv.V2 = _CameraFrustum.unProjectZ(qcoluv.V2);
2274 qcoluv.V3 = _CameraFrustum.unProjectZ(qcoluv.V3);
2277 // ***************************************************************************
2279 void CViewRenderer::setWorldSpaceFrustum (const NL3D::CFrustum &cameraFrustum)
2281 _CameraFrustum = cameraFrustum;
2284 // ***************************************************************************
2286 void CViewRenderer::activateWorldSpaceMatrix (bool activate)
2288 _WorldSpaceTransformation = activate;
2289 if (!_Material.empty())
2290 _Material.setZFunc(activate?UMaterial::lessequal:UMaterial::always);
2293 // ***************************************************************************
2295 void CViewRenderer::drawText (sint layerId, float x, float y, uint wordIndex, float xmin, float ymin, float xmax, float ymax, UTextContext &textContext)
2297 xmin = xmin * _OneOverScreenW;
2298 ymin = ymin * _OneOverScreenH;
2299 xmax = xmax * _OneOverScreenW;
2300 ymax = ymax * _OneOverScreenH;
2302 if (_InterfaceScale != 1.0f && _InterfaceScale != 2.0f)
2304 // align to screen pixel
2305 x *= _OneOverScreenW * _ScreenW;
2306 y *= _OneOverScreenH * _ScreenH;
2307 x = floorf(x) * 1.f / (float) _ScreenW;
2308 y = floorf(y) * 1.f / (float) _ScreenH;
2310 else
2312 x = floorf(x) * _OneOverScreenW;
2313 y = floorf(y) * _OneOverScreenH;
2316 if (_WorldSpaceTransformation)
2318 textContext.printClipAtUnProjected(*getStringRenderBuffer(layerId), _CameraFrustum, _WorldSpaceMatrix, x, y, _CurrentZ, wordIndex, xmin, ymin, xmax, ymax);
2320 else
2322 textContext.printClipAt(*getStringRenderBuffer(layerId), x, y, wordIndex, xmin, ymin, xmax, ymax);
2325 // layer is no more empty
2326 _EmptyLayer[layerId + VR_BIAS_LAYER]= false;
2329 // ***************************************************************************
2331 void CViewRenderer::setInterfaceDepth (const NLMISC::CVector &projCenter, float scale)
2333 _CurrentZ = projCenter.z;
2335 // no scale? => identity matrix (faster)
2336 if(scale==1)
2338 _WorldSpaceMatrix.identity();
2339 _WorldSpaceScale= false;
2341 else
2343 _WorldSpaceMatrix.identity();
2344 // must be in 0..1 coordinate here...
2345 CVector pos= projCenter;
2346 pos.x*= _OneOverScreenW;
2347 pos.y*= _OneOverScreenH;
2348 // set a pivoted scale matrix
2349 _WorldSpaceMatrix.translate(pos);
2350 _WorldSpaceMatrix.scale(scale);
2351 _WorldSpaceMatrix.translate(-pos);
2352 _WorldSpaceScale= true;