2dmaterial.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  1. #include "testUtils.h"
  2. #include <map>
  3. using namespace irr;
  4. namespace
  5. {
  6. // don't use this code! It lacks many checks and is for testing
  7. // purposes only!!!
  8. // based on code and media from SuperTuxKart
  9. class ScalableFont : public gui::IGUIFontBitmap
  10. {
  11. float m_scale;
  12. struct TextureInfo
  13. {
  14. irr::core::stringc m_file_name;
  15. bool m_has_alpha;
  16. float m_scale;
  17. TextureInfo()
  18. {
  19. m_has_alpha = false;
  20. m_scale = 1.0f;
  21. }
  22. };
  23. std::map<int /* texture file ID */, TextureInfo> m_texture_files;
  24. void lazyLoadTexture(int texID)
  25. {
  26. const bool mipmap = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
  27. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
  28. // load texture
  29. SpriteBank->setTexture(texID, Driver->getTexture( m_texture_files[texID].m_file_name ));
  30. // set previous mip-map+filter state
  31. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, mipmap);
  32. // couldn't load texture, abort.
  33. if (!SpriteBank->getTexture(texID))
  34. {
  35. return;
  36. }
  37. else
  38. {
  39. // colorkey texture rather than alpha channel?
  40. if (! m_texture_files[texID].m_has_alpha)
  41. {
  42. Driver->makeColorKeyTexture(SpriteBank->getTexture(texID), core::position2di(0,0));
  43. }
  44. }
  45. }
  46. void doReadXmlFile(io::IXMLReader* xml)
  47. {
  48. while (xml->read())
  49. {
  50. if (io::EXN_ELEMENT == xml->getNodeType())
  51. {
  52. if (core::stringw(L"include") == xml->getNodeName())
  53. {
  54. core::stringc filename = xml->getAttributeValue(L"file");
  55. io::IXMLReader* included = Environment->getFileSystem()->createXMLReader(filename.c_str());
  56. if (included != NULL)
  57. {
  58. doReadXmlFile(included);
  59. included->drop();
  60. }
  61. }
  62. else if (core::stringw(L"Texture") == xml->getNodeName())
  63. {
  64. // add a texture
  65. core::stringc filename = xml->getAttributeValue(L"filename");
  66. core::stringc fn = filename;
  67. u32 i = (u32)xml->getAttributeValueAsInt(L"index");
  68. float scale=1.0f;
  69. if (xml->getAttributeValue(L"scale"))
  70. scale = xml->getAttributeValueAsFloat(L"scale");
  71. //std::cout << "scale = " << scale << std::endl;
  72. core::stringw alpha = xml->getAttributeValue(L"hasAlpha");
  73. //std::cout << "---- Adding font texture " << fn.c_str() << "; alpha=" << alpha.c_str() << std::endl;
  74. // make sure the sprite bank has enough textures in it
  75. while (i+1 > SpriteBank->getTextureCount())
  76. {
  77. SpriteBank->addTexture(NULL);
  78. }
  79. TextureInfo info;
  80. info.m_file_name = fn;
  81. info.m_has_alpha = (alpha == core::stringw("true"));
  82. info.m_scale = scale;
  83. m_texture_files[i] = info;
  84. }
  85. else if (core::stringw(L"c") == xml->getNodeName())
  86. {
  87. // adding a character to this font
  88. SFontArea a;
  89. gui::SGUISpriteFrame f;
  90. gui::SGUISprite s;
  91. core::rect<s32> rectangle;
  92. a.underhang = xml->getAttributeValueAsInt(L"u");
  93. a.overhang = xml->getAttributeValueAsInt(L"o");
  94. a.spriteno = SpriteBank->getSprites().size();
  95. s32 texno = xml->getAttributeValueAsInt(L"i");
  96. // parse rectangle
  97. core::stringc rectstr = xml->getAttributeValue(L"r");
  98. wchar_t ch = xml->getAttributeValue(L"c")[0];
  99. const c8 *c = rectstr.c_str();
  100. s32 val;
  101. val = 0;
  102. while (*c >= '0' && *c <= '9')
  103. {
  104. val *= 10;
  105. val += *c - '0';
  106. c++;
  107. }
  108. rectangle.UpperLeftCorner.X = val;
  109. while (*c == L' ' || *c == L',') c++;
  110. val = 0;
  111. while (*c >= '0' && *c <= '9')
  112. {
  113. val *= 10;
  114. val += *c - '0';
  115. c++;
  116. }
  117. rectangle.UpperLeftCorner.Y = val;
  118. while (*c == L' ' || *c == L',') c++;
  119. val = 0;
  120. while (*c >= '0' && *c <= '9')
  121. {
  122. val *= 10;
  123. val += *c - '0';
  124. c++;
  125. }
  126. rectangle.LowerRightCorner.X = val;
  127. while (*c == L' ' || *c == L',') c++;
  128. val = 0;
  129. while (*c >= '0' && *c <= '9')
  130. {
  131. val *= 10;
  132. val += *c - '0';
  133. c++;
  134. }
  135. rectangle.LowerRightCorner.Y = val;
  136. CharacterMap[ch] = Areas.size();
  137. // make frame
  138. f.rectNumber = SpriteBank->getPositions().size();
  139. f.textureNumber = texno;
  140. // add frame to sprite
  141. s.Frames.push_back(f);
  142. s.frameTime = 0;
  143. // add rectangle to sprite bank
  144. SpriteBank->getPositions().push_back(rectangle);
  145. a.width = rectangle.getWidth();
  146. // add sprite to sprite bank
  147. SpriteBank->getSprites().push_back(s);
  148. // add character to font
  149. Areas.push_back(a);
  150. }
  151. }
  152. }
  153. }
  154. public:
  155. bool m_black_border;
  156. ScalableFont* m_fallback_font;
  157. float m_fallback_font_scale;
  158. int m_fallback_kerning_width;
  159. //! constructor
  160. ScalableFont(gui::IGUIEnvironment *env, const io::path& filename)
  161. : Driver(0), SpriteBank(0), Environment(env), WrongCharacter(0),
  162. MaxHeight(0), GlobalKerningWidth(0), GlobalKerningHeight(0)
  163. {
  164. #ifdef _DEBUG
  165. setDebugName("ScalableFont");
  166. #endif
  167. m_fallback_font = NULL;
  168. m_fallback_kerning_width = 0;
  169. m_fallback_font_scale = 1.0f;
  170. m_scale = 0.37f;
  171. m_black_border = false;
  172. if (Environment)
  173. {
  174. // don't grab environment, to avoid circular references
  175. Driver = Environment->getVideoDriver();
  176. SpriteBank = Environment->addEmptySpriteBank(filename);
  177. if (SpriteBank)
  178. SpriteBank->grab();
  179. }
  180. if (Driver)
  181. Driver->grab();
  182. setInvisibleCharacters ( L" " );
  183. io::IXMLReader* reader = env->getFileSystem()->createXMLReader(filename.c_str());
  184. if (reader)
  185. {
  186. load( reader );
  187. reader->drop();
  188. }
  189. assert_log(Areas.size() > 0);
  190. }
  191. //! destructor
  192. virtual ~ScalableFont()
  193. {
  194. if (Driver)
  195. Driver->drop();
  196. if (SpriteBank)
  197. SpriteBank->drop();
  198. }
  199. //! loads a font from an XML file
  200. bool load(io::IXMLReader* xml)
  201. {
  202. if (!SpriteBank)
  203. return false;
  204. doReadXmlFile(xml);
  205. // set bad character
  206. WrongCharacter = getAreaIDFromCharacter(L' ', NULL);
  207. setMaxHeight();
  208. for(wchar_t c='0'; c<='9'; c++)
  209. {
  210. SFontArea a = getAreaFromCharacter(c, NULL);
  211. if (a.overhang > m_max_digit_area.overhang ) m_max_digit_area.overhang = a.overhang;
  212. if (a.underhang > m_max_digit_area.underhang) m_max_digit_area.underhang = a.underhang;
  213. if (a.width > m_max_digit_area.width) m_max_digit_area.width = a.width;
  214. }
  215. m_max_digit_area.overhang = 0;
  216. m_max_digit_area.underhang=0;
  217. return true;
  218. }
  219. //! draws an text and clips it to the specified rectangle if wanted
  220. virtual void draw(const core::stringw& text, const core::rect<s32>& position,
  221. video::SColor color, bool hcenter=false,
  222. bool vcenter=false, const core::rect<s32>* clip=0)
  223. {
  224. if (!Driver) return;
  225. core::position2d<s32> offset = position.UpperLeftCorner;
  226. core::dimension2d<s32> text_dimension;
  227. // When we use the "tab" hack, disable right-alignment, it messes up everything
  228. // bool has_tab = (text.findFirst(L'\t') != -1);
  229. // ---- collect character locations
  230. const unsigned int text_size = text.size();
  231. core::array<s32> indices(text_size);
  232. core::array<core::position2di> offsets(text_size);
  233. core::array<bool> fallback;
  234. fallback.set_used(text_size);
  235. for (u32 i = 0; i<text_size; i++)
  236. {
  237. wchar_t c = text[i];
  238. //hack: one tab character is supported, it moves the cursor to the middle of the area
  239. if (c == L'\t')
  240. {
  241. offset.X = position.UpperLeftCorner.X + position.getWidth()/2;
  242. continue;
  243. }
  244. if (c == L'\r' || // Windows breaks
  245. c == L'\n') // Unix breaks
  246. {
  247. if (c==L'\r' && text[i+1]==L'\n')
  248. c = text[++i];
  249. offset.Y += (int)(MaxHeight*m_scale);
  250. offset.X = position.UpperLeftCorner.X;
  251. if (hcenter)
  252. offset.X += (position.getWidth() - text_dimension.Width) >> 1;
  253. continue;
  254. } // if lineBreak
  255. bool use_fallback_font = false;
  256. const SFontArea &area = getAreaFromCharacter(c, &use_fallback_font);
  257. fallback[i] = use_fallback_font;
  258. offset.X += area.underhang;
  259. offsets.push_back(offset);
  260. // Invisible character. add something to the array anyway so that
  261. // indices from the various arrays remain in sync
  262. indices.push_back((Invisible.findFirst(c) < 0) ? (int)area.spriteno
  263. : -1);
  264. offset.X += getCharWidth(area, fallback[i]);
  265. } // for i<text_size
  266. // ---- do the actual rendering
  267. const int indiceAmount = indices.size();
  268. core::array< gui::SGUISprite >& sprites = SpriteBank->getSprites();
  269. core::array< core::rect<s32> >& positions = SpriteBank->getPositions();
  270. core::array< gui::SGUISprite >* fallback_sprites;
  271. core::array< core::rect<s32> >* fallback_positions;
  272. if (m_fallback_font!=NULL)
  273. {
  274. fallback_sprites = &m_fallback_font->SpriteBank->getSprites();
  275. fallback_positions = &m_fallback_font->SpriteBank->getPositions();
  276. }
  277. else
  278. {
  279. fallback_sprites = NULL;
  280. fallback_positions = NULL;
  281. }
  282. video::IVideoDriver* driver = Environment->getVideoDriver();
  283. const int spriteAmount = sprites.size();
  284. for (int n=0; n<indiceAmount; n++)
  285. {
  286. const int spriteID = indices[n];
  287. if (!fallback[n] && (spriteID < 0 || spriteID >= spriteAmount))
  288. continue;
  289. if (indices[n] == -1)
  290. continue;
  291. //assert_log(sprites[spriteID].Frames.size() > 0);
  292. const int texID = (fallback[n] ?
  293. (*fallback_sprites)[spriteID].Frames[0].textureNumber :
  294. sprites[spriteID].Frames[0].textureNumber);
  295. core::rect<s32> source = (fallback[n] ?
  296. (*fallback_positions)[(*fallback_sprites)[spriteID].Frames[0].rectNumber] :
  297. positions[sprites[spriteID].Frames[0].rectNumber]);
  298. const TextureInfo& info = (fallback[n] ?
  299. (*(m_fallback_font->m_texture_files.find(texID))).second :
  300. (*(m_texture_files.find(texID))).second);
  301. float char_scale = info.m_scale;
  302. core::dimension2d<s32> size = source.getSize();
  303. float scale = (fallback[n] ? m_scale*m_fallback_font_scale : m_scale);
  304. size.Width = (int)(size.Width * scale * char_scale);
  305. size.Height = (int)(size.Height * scale * char_scale);
  306. // align vertically if character is smaller
  307. int y_shift = (size.Height < MaxHeight*m_scale ? (int)((MaxHeight*m_scale - size.Height)/2.0f) : 0);
  308. core::rect<s32> dest(offsets[n] + core::position2di(0, y_shift), size);
  309. video::SColor colors[] = {color, color, color, color};
  310. video::ITexture* texture = (fallback[n] ?
  311. m_fallback_font->SpriteBank->getTexture(texID) :
  312. SpriteBank->getTexture(texID) );
  313. if (texture == NULL)
  314. {
  315. // perform lazy loading
  316. if (fallback[n])
  317. {
  318. m_fallback_font->lazyLoadTexture(texID);
  319. texture = m_fallback_font->SpriteBank->getTexture(texID);
  320. }
  321. else
  322. {
  323. lazyLoadTexture(texID);
  324. texture = SpriteBank->getTexture(texID);
  325. }
  326. if (texture == NULL)
  327. {
  328. continue; // no such character
  329. }
  330. }
  331. if (m_black_border)
  332. {
  333. // draw black border
  334. video::SColor black(color.getAlpha(),0,0,0);
  335. video::SColor black_colors[] = {black, black, black, black};
  336. for (int x_delta=-2; x_delta<=2; x_delta++)
  337. {
  338. for (int y_delta=-2; y_delta<=2; y_delta++)
  339. {
  340. if (x_delta == 0 || y_delta == 0) continue;
  341. driver->draw2DImage(texture,
  342. dest + core::position2d<s32>(x_delta, y_delta),
  343. source,
  344. clip,
  345. black_colors, true);
  346. }
  347. }
  348. }
  349. if (fallback[n])
  350. {
  351. // draw text over
  352. static video::SColor orange(color.getAlpha(), 255, 100, 0);
  353. static video::SColor yellow(color.getAlpha(), 255, 220, 15);
  354. video::SColor title_colors[] = {yellow, orange, orange, yellow};
  355. driver->draw2DImage(texture,
  356. dest,
  357. source,
  358. clip,
  359. title_colors, true);
  360. }
  361. else
  362. {
  363. driver->draw2DImage(texture,
  364. dest,
  365. source,
  366. clip,
  367. colors, true);
  368. }
  369. }
  370. }
  371. //! returns the dimension of a text
  372. virtual core::dimension2d<u32> getDimension(const wchar_t* text) const
  373. {
  374. assert_log(Areas.size() > 0);
  375. core::dimension2d<u32> dim(0, 0);
  376. core::dimension2d<u32> thisLine(0, (int)(MaxHeight*m_scale));
  377. for (const wchar_t* p = text; *p; ++p)
  378. {
  379. if (*p == L'\r' || // Windows breaks
  380. *p == L'\n') // Unix breaks
  381. {
  382. if (*p==L'\r' && p[1] == L'\n') // Windows breaks
  383. ++p;
  384. dim.Height += thisLine.Height;
  385. if (dim.Width < thisLine.Width)
  386. dim.Width = thisLine.Width;
  387. thisLine.Width = 0;
  388. continue;
  389. }
  390. bool fallback = false;
  391. const SFontArea &area = getAreaFromCharacter(*p, &fallback);
  392. thisLine.Width += area.underhang;
  393. thisLine.Width += getCharWidth(area, fallback);
  394. }
  395. dim.Height += thisLine.Height;
  396. if (dim.Width < thisLine.Width) dim.Width = thisLine.Width;
  397. // std::cout << "ScalableFont::getDimension returns : " << dim.Width << ", " << dim.Height << " --> ";
  398. dim.Width = (int)(dim.Width + 0.9f); // round up
  399. dim.Height = (int)(dim.Height + 0.9f);
  400. //std::cout << dim.Width << ", " << dim.Height << std::endl;
  401. return dim;
  402. }
  403. //! Calculates the index of the character in the text which is on a specific position.
  404. virtual s32 getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
  405. {
  406. s32 x = 0;
  407. s32 idx = 0;
  408. while (text[idx])
  409. {
  410. const SFontArea& a = Areas[getAreaIDFromCharacter(text[idx], NULL)];
  411. x += a.width + a.overhang + a.underhang + GlobalKerningWidth;
  412. if (x >= pixel_x)
  413. return idx;
  414. ++idx;
  415. }
  416. return -1;
  417. }
  418. //! Returns the type of this font
  419. virtual gui::EGUI_FONT_TYPE getType() const { return gui::EGFT_BITMAP; }
  420. //! set an Pixel Offset on Drawing ( scale position on width )
  421. virtual void setKerningWidth (s32 kerning)
  422. {
  423. GlobalKerningWidth = kerning;
  424. }
  425. virtual void setKerningHeight (s32 kerning)
  426. {
  427. GlobalKerningHeight = kerning;
  428. }
  429. //! set an Pixel Offset on Drawing ( scale position on width )
  430. virtual s32 getKerningWidth(const wchar_t* thisLetter=0, const wchar_t* previousLetter=0) const
  431. {
  432. s32 ret = GlobalKerningWidth;
  433. if (thisLetter)
  434. {
  435. ret += Areas[getAreaIDFromCharacter(*thisLetter, NULL)].overhang;
  436. if (previousLetter)
  437. {
  438. ret += Areas[getAreaIDFromCharacter(*previousLetter, NULL)].underhang;
  439. }
  440. }
  441. return ret;
  442. }
  443. virtual s32 getKerningHeight() const
  444. {
  445. return GlobalKerningHeight;
  446. }
  447. //! gets the sprite bank
  448. virtual gui::IGUISpriteBank* getSpriteBank() const
  449. {
  450. return SpriteBank;
  451. }
  452. //! returns the sprite number from a given character
  453. virtual u32 getSpriteNoFromChar(const wchar_t *c) const
  454. {
  455. return Areas[getAreaIDFromCharacter(*c, NULL)].spriteno;
  456. }
  457. virtual void setInvisibleCharacters( const wchar_t *s )
  458. {
  459. Invisible = s;
  460. }
  461. private:
  462. struct SFontArea
  463. {
  464. SFontArea() : underhang(0), overhang(0), width(0), spriteno(0) {}
  465. s32 underhang;
  466. s32 overhang;
  467. s32 width;
  468. u32 spriteno;
  469. };
  470. int getCharWidth(const SFontArea& area, const bool fallback) const
  471. {
  472. core::array< gui::SGUISprite >& sprites = SpriteBank->getSprites();
  473. core::array< gui::SGUISprite >* fallback_sprites = (m_fallback_font != NULL ?
  474. &m_fallback_font->SpriteBank->getSprites() :
  475. NULL);
  476. const int texID = (fallback ?
  477. (*fallback_sprites)[area.spriteno].Frames[0].textureNumber :
  478. sprites[area.spriteno].Frames[0].textureNumber);
  479. const TextureInfo& info = (fallback ?
  480. (*(m_fallback_font->m_texture_files.find(texID))).second :
  481. (*(m_texture_files.find(texID))).second);
  482. const float char_scale = info.m_scale;
  483. //std::cout << "area.spriteno=" << area.spriteno << ", char_scale=" << char_scale << std::endl;
  484. if (fallback)
  485. return (int)(((area.width + area.overhang)*m_fallback_font_scale + m_fallback_kerning_width) * m_scale * char_scale);
  486. else
  487. return (int)((area.width + area.overhang + GlobalKerningWidth) * m_scale * char_scale);
  488. }
  489. s32 getAreaIDFromCharacter(const wchar_t c, bool* fallback_font) const
  490. {
  491. std::map<wchar_t, s32>::const_iterator n = CharacterMap.find(c);
  492. if (n != CharacterMap.end())
  493. {
  494. if (fallback_font != NULL)
  495. *fallback_font = false;
  496. return (*n).second;
  497. }
  498. else if (m_fallback_font != NULL && fallback_font != NULL)
  499. {
  500. *fallback_font = true;
  501. return m_fallback_font->getAreaIDFromCharacter(c, NULL);
  502. }
  503. else
  504. {
  505. // std::cout << "The font does not have this character : <" << (int)c << ">" << std::endl;
  506. if (fallback_font != NULL)
  507. *fallback_font = false;
  508. return WrongCharacter;
  509. }
  510. }
  511. const SFontArea &getAreaFromCharacter(const wchar_t c, bool* fallback_font) const
  512. {
  513. const int area_id = getAreaIDFromCharacter(c, fallback_font);
  514. const bool use_fallback_font = (fallback_font && *fallback_font);
  515. // Note: fallback_font can be NULL
  516. return ( use_fallback_font ? m_fallback_font->Areas[area_id] : Areas[area_id]);
  517. } // getAreaFromCharacter
  518. void setMaxHeight()
  519. {
  520. // FIXME: should consider per-texture scaling
  521. MaxHeight = 0;
  522. s32 t;
  523. core::array< core::rect<s32> >& p = SpriteBank->getPositions();
  524. for (u32 i=0; i<p.size(); ++i)
  525. {
  526. t = p[i].getHeight();
  527. if (t>MaxHeight)
  528. MaxHeight = t;
  529. }
  530. }
  531. core::array<SFontArea> Areas;
  532. /** The maximum values of all digits, used in monospace_digits. */
  533. mutable SFontArea m_max_digit_area;
  534. std::map<wchar_t, s32> CharacterMap;
  535. video::IVideoDriver* Driver;
  536. gui::IGUISpriteBank* SpriteBank;
  537. gui::IGUIEnvironment* Environment;
  538. u32 WrongCharacter;
  539. s32 MaxHeight;
  540. s32 GlobalKerningWidth, GlobalKerningHeight;
  541. core::stringw Invisible;
  542. };
  543. }
  544. // The actual bug that was behind this issue was the combination of
  545. // 2d rendering and mipmaps. The issue was reproduced using the special
  546. // draw2dimage version, hence the name.
  547. static bool draw2DImage4c(video::E_DRIVER_TYPE type)
  548. {
  549. IrrlichtDevice *device = createDevice(type, core::dimension2d<u32>(240, 120));
  550. if (!device)
  551. return true; // could not create selected driver.
  552. video::IVideoDriver* driver = device->getVideoDriver();
  553. if (!driver->queryFeature(video::EVDF_BILINEAR_FILTER))
  554. {
  555. device->closeDevice();
  556. device->run();
  557. device->drop();
  558. return true;
  559. }
  560. stabilizeScreenBackground(driver);
  561. logTestString("Testing driver %ls\n", driver->getName());
  562. driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS,true);
  563. driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_QUALITY,true);
  564. video::ITexture* images = driver->getTexture("../media/2ddemo.png");
  565. driver->makeColorKeyTexture(images, core::position2d<s32>(0,0));
  566. core::rect<s32> imp1(349,15,385,78);
  567. core::rect<s32> imp2(387,15,423,78);
  568. // font cannot handle loading from sub-dirs
  569. io::path cwd = device->getFileSystem()->getWorkingDirectory();
  570. device->getFileSystem()->changeWorkingDirectoryTo("media");
  571. ScalableFont* font = new ScalableFont(device->getGUIEnvironment(), "title_font.xml");
  572. font->m_fallback_font_scale = 4.0f;
  573. font->m_fallback_kerning_width = 15;
  574. font->setKerningWidth(-18);
  575. font->m_black_border = true;
  576. /*
  577. Prepare a nicely filtering 2d render mode for special cases.
  578. */
  579. driver->getMaterial2D().UseMipMaps = true;
  580. driver->getMaterial2D().TextureLayer[0].BilinearFilter = true;
  581. {
  582. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(255,120,102,136));
  583. driver->enableMaterial2D();
  584. // draw fire & dragons background world
  585. driver->draw2DImage(images, core::position2di(),
  586. core::rect<s32>(0,0,342,224), 0,
  587. video::SColor(255,255,255,255), true);
  588. // draw flying imp
  589. driver->draw2DImage(images, core::position2d<s32>(114,75),
  590. imp1, 0, video::SColor(255,255,255,255), true);
  591. // draw second flying imp
  592. driver->draw2DImage(images, core::position2d<s32>(220,55),
  593. imp2, 0, video::SColor(255,255,255,255), true);
  594. driver->draw2DImage(images, core::rect<s32>(10,10,108,48),
  595. core::rect<s32>(354,87,442,118));
  596. video::SColor colors[] = {0xff00ffff, 0xff00ffff, 0xffffff00, 0xffffff00};
  597. driver->draw2DImage(images, core::recti(10,50,108,88),
  598. core::recti(354,87,442,118), 0, colors, true);
  599. font->draw( L"WXYZsSdDrRjJbB", core::rect<s32>(30,20,300,300),
  600. video::SColor(255,255,255,255) );
  601. driver->enableMaterial2D(false);
  602. driver->draw2DImage(images, core::recti(10,90,108,128),
  603. core::recti(354,87,442,118), 0, colors, true);
  604. font->draw( L"WXYZsSdDrRjJbB", core::rect<s32>(30,60,300,400),
  605. video::SColor(255,255,255,255) );
  606. driver->endScene();
  607. }
  608. font->drop();
  609. device->getFileSystem()->changeWorkingDirectoryTo(cwd);
  610. // don't go under 99% as the difference is not very large
  611. bool result = takeScreenshotAndCompareAgainstReference(driver, "-draw2DImage4cFilter.png");
  612. device->closeDevice();
  613. device->run();
  614. device->drop();
  615. return result;
  616. }
  617. // This test renders a 3d scene and a gui on top of it. The GUI is
  618. // filtered via 2dmaterial (blurred).
  619. // TODO: Works only for OpenGL right now
  620. static bool addBlend2d(video::E_DRIVER_TYPE type)
  621. {
  622. SIrrlichtCreationParameters params;
  623. params.AntiAlias = 0;
  624. params.Bits = 32;
  625. params.WindowSize = core::dimension2d<u32>(160, 120);
  626. params.DriverType = type;
  627. IrrlichtDevice *device = createDeviceEx(params);
  628. if (!device)
  629. return true; // in case the driver type does not exist
  630. video::IVideoDriver* driver = device->getVideoDriver();
  631. scene::ISceneManager* smgr = device->getSceneManager();
  632. if (!driver->queryFeature(video::EVDF_BILINEAR_FILTER))
  633. {
  634. device->closeDevice();
  635. device->run();
  636. device->drop();
  637. return true;
  638. }
  639. logTestString("Testing driver %ls\n", driver->getName());
  640. scene::IAnimatedMesh* mesh = smgr->getMesh("../media/sydney.md2");
  641. if (!mesh)
  642. {
  643. device->closeDevice();
  644. device->run();
  645. device->drop();
  646. return false;
  647. }
  648. stabilizeScreenBackground(driver);
  649. scene::IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
  650. if (node)
  651. {
  652. node->setMaterialFlag(video::EMF_LIGHTING, false);
  653. node->setMD2Animation(scene::EMAT_STAND);
  654. node->setMaterialTexture( 0, driver->getTexture("../media/sydney.bmp") );
  655. }
  656. smgr->addCameraSceneNode(0, core::vector3df(0,30,-40), core::vector3df(0,5,0));
  657. gui::IGUIEnvironment* env = device->getGUIEnvironment();
  658. {
  659. // create the toolbox window
  660. gui::IGUIWindow* wnd = env->addWindow(core::rect<s32>(0,0,800,480),
  661. false, L"Toolset", 0, 100);
  662. // create tab control and tabs
  663. gui::IGUITabControl* tab = env->addTabControl(
  664. core::rect<s32>(2,20,800-602,480-7), wnd, true, true);
  665. gui::IGUITab* t1 = tab->addTab(L"Config");
  666. // add some edit boxes and a button to tab one
  667. env->addImage(driver->getTexture("../media/tools.png"), core::vector2d<s32>(10,20), true, t1);
  668. env->addStaticText(L"X:", core::rect<s32>(22,48,40,66), false, false, t1);
  669. env->addEditBox(L"1.0", core::rect<s32>(40,46,130,66), true, t1, 201);
  670. // quick scale buttons
  671. env->addButton(core::rect<s32>(65,20,95,40), t1, 102, L"* 10");
  672. env->addButton(core::rect<s32>(100,20,130,40), t1, 103, L"* 0.1");
  673. }
  674. video::SMaterial& material2D = driver->getMaterial2D();
  675. for (unsigned int n=0; n<video::MATERIAL_MAX_TEXTURES; n++)
  676. {
  677. material2D.TextureLayer[n].BilinearFilter = true;
  678. material2D.TextureLayer[n].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
  679. material2D.TextureLayer[n].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
  680. }
  681. material2D.AntiAliasing=video::EAAM_FULL_BASIC;
  682. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(255,100,101,140));
  683. smgr->drawAll();
  684. driver->enableMaterial2D();
  685. env->drawAll();
  686. driver->enableMaterial2D(false);
  687. driver->endScene();
  688. bool result = takeScreenshotAndCompareAgainstReference(driver, "-addBlend2D.png", 98.2f);
  689. device->closeDevice();
  690. device->run();
  691. device->drop();
  692. return result;
  693. }
  694. // This test renders 4 times the same image. Two via IGUIImage, two via draw2DImage
  695. // 3 of the 4 images are filtered via 2dmaterial and bilinear filter, only the one
  696. // at the bottom left is not.
  697. static bool moreFilterTests(video::E_DRIVER_TYPE type)
  698. {
  699. IrrlichtDevice* device = irr::createDevice(type, core::dimension2du(160,120));
  700. if (!device)
  701. return true;
  702. video::IVideoDriver* driver = device->getVideoDriver();
  703. gui::IGUIEnvironment* gui = device->getGUIEnvironment();
  704. if (!driver->queryFeature(video::EVDF_BILINEAR_FILTER))
  705. {
  706. device->closeDevice();
  707. device->run();
  708. device->drop();
  709. return true;
  710. }
  711. stabilizeScreenBackground(driver);
  712. logTestString("Testing driver %ls\n", driver->getName());
  713. driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
  714. video::ITexture* tex = driver->getTexture("../media/irrlichtlogo.jpg");
  715. gui::IGUIImage* image = gui->addImage(core::recti(0,0,64,64));
  716. image->setScaleImage(true);
  717. image->setImage(tex);
  718. image->setUseAlphaChannel(true);
  719. driver->getMaterial2D().TextureLayer[0].BilinearFilter=true;
  720. driver->getMaterial2D().TextureLayer[0].TrilinearFilter=true;
  721. {
  722. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, irr::video::SColor(255,255,255,255));
  723. // all three logos should be with filtering
  724. driver->enableMaterial2D();
  725. driver->getMaterial2D().setTexture(0, 0);
  726. driver->draw2DImage(tex, irr::core::rect<irr::s32>(64, 64, 128, 128), irr::core::rect<irr::s32>(0, 0, 88, 31));
  727. driver->getMaterial2D().setTexture(0, tex);
  728. driver->draw2DImage(tex, irr::core::rect<irr::s32>(64, 0, 128, 64), irr::core::rect<irr::s32>(0, 0, 88, 31));
  729. gui->drawAll();
  730. // the next gui image should be without filter
  731. driver->enableMaterial2D(false);
  732. image->setRelativePosition(core::recti(0,64,64,128));
  733. gui->drawAll();
  734. driver->endScene();
  735. }
  736. bool result = takeScreenshotAndCompareAgainstReference(driver, "-2dmatFilter.png");
  737. device->closeDevice();
  738. device->run();
  739. device->drop();
  740. return result;
  741. }
  742. bool twodmaterial()
  743. {
  744. bool result = true;
  745. TestWithAllDrivers(addBlend2d);
  746. TestWithAllDrivers(moreFilterTests);
  747. #ifdef _IRR_COMPILE_WITH_XML_
  748. TestWithAllDrivers(draw2DImage4c);
  749. #endif
  750. return result;
  751. }