textureloader.cpp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "textureloader.h"
  19. #include "mutex.h"
  20. #include "thread.h"
  21. #include "wwdebug.h"
  22. #include "texture.h"
  23. #include "ffactory.h"
  24. #include "wwstring.h"
  25. #include "bufffile.h"
  26. #include "ww3d.h"
  27. #include "texfcach.h"
  28. #include "assetmgr.h"
  29. #include "dx8wrapper.h"
  30. #include "dx8caps.h"
  31. #include "missingtexture.h"
  32. #include "targa.h"
  33. #include <D3dx8tex.h>
  34. #include <cstdio>
  35. #include "wwmemlog.h"
  36. #include "texture.h"
  37. #include "formconv.h"
  38. #include "texturethumbnail.h"
  39. #include "ddsfile.h"
  40. #include "bitmaphandler.h"
  41. static TextureLoadTaskClass* LoadListHead;
  42. static TextureLoadTaskClass* DeferredListHead;
  43. static TextureLoadTaskClass* FinishedListHead;
  44. static TextureLoadTaskClass* ThumbnailListHead;
  45. static TextureLoadTaskClass* DeleteTaskListHead;
  46. TextureLoadTaskClass* TextureLoadTaskClass::FreeTaskListHead;
  47. static bool Is_Format_Compressed(WW3DFormat texture_format,bool allow_compression)
  48. {
  49. // Verify that the user isn't requesting compressed texture without hardware support
  50. bool compressed=false;
  51. if (texture_format!=WW3D_FORMAT_UNKNOWN) {
  52. if (!DX8Caps::Support_DXTC() || !allow_compression) {
  53. WWASSERT(texture_format!=WW3D_FORMAT_DXT1);
  54. WWASSERT(texture_format!=WW3D_FORMAT_DXT2);
  55. WWASSERT(texture_format!=WW3D_FORMAT_DXT3);
  56. WWASSERT(texture_format!=WW3D_FORMAT_DXT4);
  57. WWASSERT(texture_format!=WW3D_FORMAT_DXT5);
  58. }
  59. if (texture_format==WW3D_FORMAT_DXT1 ||
  60. texture_format==WW3D_FORMAT_DXT2 ||
  61. texture_format==WW3D_FORMAT_DXT3 ||
  62. texture_format==WW3D_FORMAT_DXT4 ||
  63. texture_format==WW3D_FORMAT_DXT5) {
  64. compressed=true;
  65. }
  66. }
  67. // If hardware supports DXTC compression, load a compressed texture. Proceed only if the texture format hasn't been
  68. // defined as non-compressed.
  69. compressed|=(
  70. texture_format==WW3D_FORMAT_UNKNOWN &&
  71. DX8Caps::Support_DXTC() &&
  72. WW3D::Get_Texture_Compression_Mode()==WW3D::TEXTURE_COMPRESSION_ENABLE &&
  73. allow_compression);
  74. return compressed;
  75. }
  76. // ----------------------------------------------------------------------------
  77. static CriticalSectionClass mutex;
  78. static class LoaderThreadClass : public ThreadClass
  79. {
  80. TextureLoadTaskClass* Get_Task_From_Load_List();
  81. static void Add_Task_To_Finished_List(TextureLoadTaskClass* task);
  82. public:
  83. LoaderThreadClass::LoaderThreadClass() : ThreadClass() {}
  84. void Thread_Function();
  85. static void Add_Task_To_Load_List(TextureLoadTaskClass* task);
  86. } thread;
  87. // ----------------------------------------------------------------------------
  88. void TextureLoader::Init()
  89. {
  90. WWASSERT(!thread.Is_Running());
  91. ThumbnailClass::Init();
  92. thread.Execute();
  93. thread.Set_Priority(-3);
  94. }
  95. // ----------------------------------------------------------------------------
  96. void TextureLoader::Deinit()
  97. {
  98. CriticalSectionClass::LockClass m(mutex);
  99. thread.Stop();
  100. ThumbnailClass::Deinit();
  101. }
  102. // ----------------------------------------------------------------------------
  103. //
  104. // Modify given texture size to nearest valid size on current hardware.
  105. //
  106. // ----------------------------------------------------------------------------
  107. void TextureLoader::Validate_Texture_Size(unsigned& width, unsigned& height)
  108. {
  109. const D3DCAPS8& dx8caps=DX8Caps::Get_Default_Caps();
  110. unsigned poweroftwowidth = 1;
  111. while (poweroftwowidth < width) {
  112. poweroftwowidth <<= 1;
  113. }
  114. unsigned poweroftwoheight = 1;
  115. while (poweroftwoheight < height) {
  116. poweroftwoheight <<= 1;
  117. }
  118. // unsigned size = MAX (width, height);
  119. // unsigned poweroftwosize = 1;
  120. // while (poweroftwosize < size) {
  121. // poweroftwosize <<= 1;
  122. // }
  123. if (poweroftwowidth>dx8caps.MaxTextureWidth) {
  124. poweroftwowidth=dx8caps.MaxTextureWidth;
  125. }
  126. if (poweroftwoheight>dx8caps.MaxTextureHeight) {
  127. poweroftwoheight=dx8caps.MaxTextureHeight;
  128. }
  129. if (poweroftwowidth>poweroftwoheight) {
  130. while (poweroftwowidth/poweroftwoheight>8) {
  131. poweroftwoheight*=2;
  132. }
  133. }
  134. else {
  135. while (poweroftwoheight/poweroftwowidth>8) {
  136. poweroftwowidth*=2;
  137. }
  138. }
  139. // width = height = poweroftwosize;
  140. width=poweroftwowidth;
  141. height=poweroftwoheight;
  142. }
  143. IDirect3DTexture8* TextureLoader::Load_Thumbnail(const StringClass& filename,WW3DFormat texture_format)
  144. {
  145. ThumbnailClass* thumb=ThumbnailClass::Peek_Instance(filename);
  146. if (!thumb) {
  147. thumb=W3DNEW ThumbnailClass(filename);
  148. // If load failed, return missing texture
  149. if (!thumb->Peek_Bitmap()) {
  150. delete thumb;
  151. return MissingTexture::_Get_Missing_Texture();
  152. }
  153. }
  154. unsigned src_pitch=thumb->Get_Width()*4; // Thumbs are always 32 bits
  155. WW3DFormat dest_format;
  156. if (texture_format==WW3D_FORMAT_UNKNOWN) {
  157. dest_format=Get_Valid_Texture_Format(WW3D_FORMAT_A8R8G8B8,false); // no compressed formats please
  158. }
  159. else {
  160. dest_format=Get_Valid_Texture_Format(texture_format,false); // no compressed formats please
  161. WWASSERT(dest_format==texture_format);
  162. }
  163. IDirect3DTexture8* d3d_texture = DX8Wrapper::_Create_DX8_Texture(
  164. thumb->Get_Width(),
  165. thumb->Get_Height(),
  166. dest_format,
  167. TextureClass::MIP_LEVELS_ALL);
  168. unsigned level=0;
  169. D3DLOCKED_RECT locked_rects[12];
  170. WWASSERT(d3d_texture->GetLevelCount()<=12);
  171. // Lock all surfaces
  172. for (level=0;level<d3d_texture->GetLevelCount();++level) {
  173. DX8_ErrorCode(
  174. d3d_texture->LockRect(
  175. level,
  176. &locked_rects[level],
  177. NULL,
  178. 0));
  179. }
  180. unsigned char* src_surface=thumb->Peek_Bitmap();
  181. WW3DFormat src_format=WW3D_FORMAT_A8R8G8B8;
  182. unsigned width=thumb->Get_Width();
  183. unsigned height=thumb->Get_Height();
  184. for (level=0;level<d3d_texture->GetLevelCount()-1;++level) {
  185. BitmapHandlerClass::Copy_Image_Generate_Mipmap(
  186. width,
  187. height,
  188. (unsigned char*)locked_rects[level].pBits,
  189. locked_rects[level].Pitch,
  190. dest_format,
  191. src_surface,
  192. src_pitch,
  193. src_format,
  194. (unsigned char*)locked_rects[level+1].pBits, // mipmap
  195. locked_rects[level+1].Pitch);// mipmap
  196. src_format=dest_format;
  197. src_surface=(unsigned char*)locked_rects[level].pBits;
  198. src_pitch=locked_rects[level].Pitch;
  199. width>>=1;
  200. height>>=1;
  201. }
  202. // Unlock all surfaces
  203. for (level=0;level<d3d_texture->GetLevelCount();++level) {
  204. DX8_ErrorCode(d3d_texture->UnlockRect(level));
  205. }
  206. return d3d_texture;
  207. }
  208. static bool Is_Power_Of_Two(unsigned i)
  209. {
  210. if (!i) return false;
  211. unsigned n=i;
  212. unsigned shift=0;
  213. while (n) {
  214. shift++;
  215. n>>=1;
  216. }
  217. return ((i>>(shift-1))<<(shift-1))==i;
  218. }
  219. // ----------------------------------------------------------------------------
  220. // TODO: Legacy - remove this call!
  221. IDirect3DTexture8* Load_Compressed_Texture(
  222. const StringClass& filename,
  223. unsigned reduction_factor,
  224. TextureClass::MipCountType mip_level_count,
  225. WW3DFormat dest_format)
  226. {
  227. // If DDS file isn't available, use TGA file to convert to DDS.
  228. DDSFileClass dds_file(filename,reduction_factor);
  229. if (!dds_file.Is_Available()) return NULL;
  230. if (!dds_file.Load()) return NULL;
  231. unsigned width=dds_file.Get_Width(0);
  232. unsigned height=dds_file.Get_Height(0);
  233. unsigned mips=dds_file.Get_Mip_Level_Count();
  234. // If format isn't defined get the nearest valid texture format to the compressed file format
  235. // Note that the nearest valid format could be anything, even uncompressed.
  236. if (dest_format==WW3D_FORMAT_UNKNOWN) dest_format=Get_Valid_Texture_Format(dds_file.Get_Format(),true);
  237. IDirect3DTexture8* d3d_texture = DX8Wrapper::_Create_DX8_Texture(
  238. width,
  239. height,
  240. dest_format,
  241. (TextureClass::MipCountType)mips);
  242. for (unsigned level=0;level<mips;++level) {
  243. IDirect3DSurface8* d3d_surface=NULL;
  244. WWASSERT(d3d_texture);
  245. DX8_ErrorCode(d3d_texture->GetSurfaceLevel(level/*-reduction_factor*/,&d3d_surface));
  246. dds_file.Copy_Level_To_Surface(level,d3d_surface);
  247. d3d_surface->Release();
  248. }
  249. return d3d_texture;
  250. }
  251. // ----------------------------------------------------------------------------
  252. //
  253. // Load image to a surface. The function tries to create texture that matches
  254. // targa format. If suitable format is not available, it selects closest matching
  255. // format and performs color space conversion.
  256. //
  257. // ----------------------------------------------------------------------------
  258. IDirect3DSurface8* TextureLoader::Load_Surface_Immediate(
  259. const StringClass& filename,
  260. WW3DFormat texture_format,
  261. bool allow_compression)
  262. {
  263. bool compressed=Is_Format_Compressed(texture_format,allow_compression);
  264. if (compressed) {
  265. IDirect3DTexture8* comp_tex=Load_Compressed_Texture(filename,0,TextureClass::MIP_LEVELS_1,WW3D_FORMAT_UNKNOWN);
  266. if (comp_tex) {
  267. IDirect3DSurface8* d3d_surface=NULL;
  268. DX8_ErrorCode(comp_tex->GetSurfaceLevel(0,&d3d_surface));
  269. comp_tex->Release();
  270. return d3d_surface;
  271. }
  272. }
  273. // Make sure the file can be opened. If not, return missing texture.
  274. Targa targa;
  275. if (TARGA_ERROR_HANDLER(targa.Open(filename, TGA_READMODE),filename)) return MissingTexture::_Create_Missing_Surface();
  276. // DX8 uses image upside down compared to TGA
  277. targa.Header.ImageDescriptor ^= TGAIDF_YORIGIN;
  278. WW3DFormat src_format,dest_format;
  279. unsigned src_bpp=0;
  280. Get_WW3D_Format(dest_format,src_format,src_bpp,targa);
  281. if (texture_format!=WW3D_FORMAT_UNKNOWN) {
  282. dest_format=texture_format;
  283. }
  284. // Destination size will be the next power of two square from the larger width and height...
  285. unsigned width, height;
  286. width=targa.Header.Width;
  287. height=targa.Header.Height;
  288. unsigned src_width=targa.Header.Width;
  289. unsigned src_height=targa.Header.Height;
  290. // NOTE: We load the palette but we do not yet support paletted textures!
  291. char palette[256*4];
  292. targa.SetPalette(palette);
  293. if (TARGA_ERROR_HANDLER(targa.Load(filename, TGAF_IMAGE, false),filename)) return MissingTexture::_Create_Missing_Surface();
  294. unsigned char* src_surface=(unsigned char*)targa.GetImage();
  295. // No paletted destination format allowed
  296. unsigned char* converted_surface=NULL;
  297. if (src_format==WW3D_FORMAT_A1R5G5B5 || src_format==WW3D_FORMAT_R5G6B5 || src_format==WW3D_FORMAT_A4R4G4B4 ||
  298. src_format==WW3D_FORMAT_P8 || src_format==WW3D_FORMAT_L8 || src_width!=width || src_height!=height) {
  299. converted_surface=W3DNEWARRAY unsigned char[width*height*4];
  300. dest_format=Get_Valid_Texture_Format(WW3D_FORMAT_A8R8G8B8,false);
  301. BitmapHandlerClass::Copy_Image(
  302. converted_surface,
  303. width,
  304. height,
  305. width*4,
  306. WW3D_FORMAT_A8R8G8B8,//dest_format,
  307. src_surface,
  308. src_width,
  309. src_height,
  310. src_width*src_bpp,
  311. src_format,
  312. (unsigned char*)targa.GetPalette(),
  313. targa.Header.CMapDepth>>3,
  314. false);
  315. src_surface=converted_surface;
  316. src_format=WW3D_FORMAT_A8R8G8B8;//dest_format;
  317. src_width=width;
  318. src_height=height;
  319. src_bpp=Get_Bytes_Per_Pixel(src_format);
  320. }
  321. unsigned src_pitch=src_width*src_bpp;
  322. IDirect3DSurface8* d3d_surface = DX8Wrapper::_Create_DX8_Surface(width,height,dest_format);
  323. WWASSERT(d3d_surface);
  324. D3DLOCKED_RECT locked_rect;
  325. DX8_ErrorCode(
  326. d3d_surface->LockRect(
  327. &locked_rect,
  328. NULL,
  329. 0));
  330. BitmapHandlerClass::Copy_Image(
  331. (unsigned char*)locked_rect.pBits,
  332. width,
  333. height,
  334. locked_rect.Pitch,
  335. dest_format,
  336. src_surface,
  337. src_width,
  338. src_height,
  339. src_pitch,
  340. src_format,
  341. (unsigned char*)targa.GetPalette(),
  342. targa.Header.CMapDepth>>3,
  343. false); // No mipmap
  344. DX8_ErrorCode(d3d_surface->UnlockRect());
  345. if (converted_surface) delete[] converted_surface;
  346. return d3d_surface;
  347. }
  348. // ----------------------------------------------------------------------------
  349. //
  350. // Load mipmap levels to a pre-generated and locked texture object based on
  351. // information in load task object. Try loading from a DDS file first and if
  352. // that fails try a TGA.
  353. //
  354. // ----------------------------------------------------------------------------
  355. void TextureLoader::Load_Mipmap_Levels(TextureLoadTaskClass* task)
  356. {
  357. WWASSERT(task->Peek_D3D_Texture());
  358. WWMEMLOG(MEM_TEXTURE);
  359. if (task->Peek_Texture()->Is_Compression_Allowed()) {
  360. DDSFileClass dds_file(task->Peek_Texture()->Get_Full_Path(),task->Get_Reduction());
  361. if (dds_file.Is_Available() && dds_file.Load()) {
  362. unsigned width=task->Get_Width();
  363. unsigned height=task->Get_Height();
  364. for (unsigned level=0;level<task->Get_Mip_Level_Count();++level) {
  365. WWASSERT(width && height);
  366. dds_file.Copy_Level_To_Surface(
  367. level,
  368. task->Get_Format(),
  369. width,
  370. height,
  371. task->Get_Locked_Surface_Ptr(level),
  372. task->Get_Locked_Surface_Pitch(level));
  373. width>>=1;
  374. height>>=1;
  375. }
  376. return;
  377. }
  378. }
  379. if (Load_Uncompressed_Mipmap_Levels_From_TGA(task)) return;
  380. }
  381. // ----------------------------------------------------------------------------
  382. //
  383. // Use load task to load texture surface from a targa file. Calculate mipmaps
  384. // if needed.
  385. //
  386. // ----------------------------------------------------------------------------
  387. bool TextureLoader::Load_Uncompressed_Mipmap_Levels_From_TGA(TextureLoadTaskClass* task)
  388. {
  389. if (!task->Get_Mip_Level_Count()) return false;
  390. TextureClass* texture=task->Peek_Texture();
  391. Targa targa;
  392. if (TARGA_ERROR_HANDLER(targa.Open(texture->Get_Full_Path(), TGA_READMODE),texture->Get_Full_Path())) {
  393. task->Set_Fail(true);
  394. return false;
  395. }
  396. // DX8 uses image upside down compared to TGA
  397. targa.Header.ImageDescriptor ^= TGAIDF_YORIGIN;
  398. WW3DFormat src_format,dest_format;
  399. unsigned src_bpp=0;
  400. Get_WW3D_Format(dest_format,src_format,src_bpp,targa);
  401. // WWASSERT(task->Get_Format()==dest_format);
  402. dest_format=task->Get_Format(); // Texture can be requested in different format than the most obvious from the TGA
  403. char palette[256*4];
  404. targa.SetPalette(palette);
  405. unsigned src_width=targa.Header.Width;
  406. unsigned src_height=targa.Header.Height;
  407. unsigned width=task->Get_Width();
  408. unsigned height=task->Get_Height();
  409. // NOTE: We load the palette but we do not yet support paletted textures!
  410. if (TARGA_ERROR_HANDLER(targa.Load(texture->Get_Full_Path(), TGAF_IMAGE, false),texture->Get_Full_Path())) {
  411. task->Set_Fail(true);
  412. return false;
  413. }
  414. unsigned char* src_surface=(unsigned char*)targa.GetImage();
  415. // No paletted format allowed when generating mipmaps
  416. unsigned char* converted_surface=NULL;
  417. if (src_format==WW3D_FORMAT_A1R5G5B5 || src_format==WW3D_FORMAT_R5G6B5 || src_format==WW3D_FORMAT_A4R4G4B4 ||
  418. src_format==WW3D_FORMAT_P8 || src_format==WW3D_FORMAT_L8 || src_width!=width || src_height!=height) {
  419. converted_surface=W3DNEWARRAY unsigned char[width*height*4];
  420. dest_format=Get_Valid_Texture_Format(WW3D_FORMAT_A8R8G8B8,false);
  421. BitmapHandlerClass::Copy_Image(
  422. converted_surface,
  423. width,
  424. height,
  425. width*4,
  426. WW3D_FORMAT_A8R8G8B8, //dest_format,
  427. src_surface,
  428. src_width,
  429. src_height,
  430. src_width*src_bpp,
  431. src_format,
  432. (unsigned char*)targa.GetPalette(),
  433. targa.Header.CMapDepth>>3,
  434. false);
  435. src_surface=converted_surface;
  436. src_format=WW3D_FORMAT_A8R8G8B8; //dest_format;
  437. src_width=width;
  438. src_height=height;
  439. src_bpp=Get_Bytes_Per_Pixel(src_format);
  440. }
  441. unsigned src_pitch=src_width*src_bpp;
  442. for (unsigned level=0;level<task->Get_Mip_Level_Count();++level) {
  443. WWASSERT(task->Get_Locked_Surface_Ptr(level));
  444. BitmapHandlerClass::Copy_Image(
  445. task->Get_Locked_Surface_Ptr(level),
  446. width,
  447. height,
  448. task->Get_Locked_Surface_Pitch(level),
  449. task->Get_Format(),
  450. src_surface,
  451. src_width,
  452. src_height,
  453. src_pitch,
  454. src_format,
  455. NULL,
  456. 0,
  457. true);
  458. width>>=1;
  459. height>>=1;
  460. src_width>>=1;
  461. src_height>>=1;
  462. if (!width || !height || !src_width || !src_height) break;
  463. }
  464. if (converted_surface) delete[] converted_surface;
  465. return true;
  466. }
  467. // ----------------------------------------------------------------------------
  468. //
  469. // Return a task from the load list head. The loading thread uses this function
  470. // to retrieve tasks from the load list.
  471. //
  472. // ----------------------------------------------------------------------------
  473. TextureLoadTaskClass* LoaderThreadClass::Get_Task_From_Load_List()
  474. {
  475. CriticalSectionClass::LockClass m(mutex);
  476. TextureLoadTaskClass* task=LoadListHead;
  477. if (task) {
  478. LoadListHead=task->Peek_Succ();
  479. task->Set_Succ(NULL);
  480. }
  481. return task;
  482. }
  483. // ----------------------------------------------------------------------------
  484. //
  485. // This function adds a load task to the head of the loading thread task list.
  486. // The latest added task will be the next processed (There are good reasons
  487. // for such ordering). The loading thread will process tasks from this list
  488. // as soons as it can and then move the tasks to finished list.
  489. //
  490. // ----------------------------------------------------------------------------
  491. void LoaderThreadClass::Add_Task_To_Load_List(TextureLoadTaskClass* task)
  492. {
  493. CriticalSectionClass::LockClass m(mutex);
  494. WWASSERT(task->Peek_Succ()==NULL);
  495. task->Set_Succ(LoadListHead);
  496. LoadListHead=task;
  497. }
  498. // ----------------------------------------------------------------------------
  499. //
  500. // After the loading thread is done with the texture, it is moved to the list
  501. // of finished tasks so that the main thread can then finish up by unlocking
  502. // the surfaces and applying the changes to the texture class object.
  503. //
  504. // ----------------------------------------------------------------------------
  505. void LoaderThreadClass::Add_Task_To_Finished_List(TextureLoadTaskClass* task)
  506. {
  507. CriticalSectionClass::LockClass m(mutex);
  508. WWASSERT(task->Peek_Succ()==NULL);
  509. task->Set_Succ(FinishedListHead);
  510. FinishedListHead=task;
  511. }
  512. // ----------------------------------------------------------------------------
  513. //
  514. // If we need to find out if the load task list is empty this is the function
  515. // to use. We can't use Get_Task_From_Load_List() as if the list isn't empty
  516. // it also removes the head node from the list.
  517. //
  518. // ----------------------------------------------------------------------------
  519. bool Is_Load_List_Empty()
  520. {
  521. return !LoadListHead;
  522. }
  523. // ----------------------------------------------------------------------------
  524. //
  525. // Texture loading thread loads textures that appear in loading_task_list.
  526. // If the list is empty the thread sleeps.
  527. //
  528. // ----------------------------------------------------------------------------
  529. void LoaderThreadClass::Thread_Function()
  530. {
  531. while (running) {
  532. TextureLoadTaskClass* task=Get_Task_From_Load_List();
  533. if (task) {
  534. TextureLoader::Load_Mipmap_Levels(task);
  535. Add_Task_To_Finished_List(task);
  536. }
  537. Switch_Thread();
  538. }
  539. }
  540. // ----------------------------------------------------------------------------
  541. //
  542. // Update refreshes all completed texture loading tasks
  543. //
  544. // ----------------------------------------------------------------------------
  545. TextureLoadTaskClass* Get_Finished_Task()
  546. {
  547. CriticalSectionClass::LockClass m(mutex);
  548. TextureLoadTaskClass* task=FinishedListHead;
  549. if (task) {
  550. FinishedListHead=task->Peek_Succ();
  551. task->Set_Succ(NULL);
  552. }
  553. return task;
  554. }
  555. TextureLoadTaskClass* Get_Thumbnail_Task()
  556. {
  557. CriticalSectionClass::LockClass m(mutex);
  558. TextureLoadTaskClass* task=ThumbnailListHead;
  559. if (task) {
  560. ThumbnailListHead=task->Peek_Succ();
  561. task->Set_Succ(NULL);
  562. }
  563. return task;
  564. }
  565. void Add_Thumbnail_Task(TextureLoadTaskClass* task)
  566. {
  567. CriticalSectionClass::LockClass m(mutex);
  568. WWASSERT(task->Peek_Succ()==NULL);
  569. task->Set_Succ(ThumbnailListHead);
  570. ThumbnailListHead=task;
  571. }
  572. // ----------------------------------------------------------------------------
  573. //
  574. // The main thread's update function deletes tasks from the load task list
  575. // once a frame.
  576. //
  577. // ----------------------------------------------------------------------------
  578. TextureLoadTaskClass* Get_Task_From_Delete_List()
  579. {
  580. WWASSERT(ThreadClass::_Get_Current_Thread_ID()==DX8Wrapper::_Get_Main_Thread_ID());
  581. TextureLoadTaskClass* task=DeleteTaskListHead;
  582. if (task) {
  583. DeleteTaskListHead=task->Peek_Succ();
  584. task->Set_Succ(NULL);
  585. }
  586. return task;
  587. }
  588. // ----------------------------------------------------------------------------
  589. //
  590. // When task wants to delete itself it adds itself to a delete list. This list
  591. // can only be accessed from the main thread.
  592. //
  593. // ----------------------------------------------------------------------------
  594. void Add_Task_To_Delete_List(TextureLoadTaskClass* task)
  595. {
  596. WWASSERT(ThreadClass::_Get_Current_Thread_ID()==DX8Wrapper::_Get_Main_Thread_ID());
  597. WWASSERT(task->Peek_Succ()==NULL);
  598. task->Set_Succ(DeleteTaskListHead);
  599. DeleteTaskListHead=task;
  600. }
  601. TextureLoadTaskClass* Get_Deferred_Task()
  602. {
  603. CriticalSectionClass::LockClass m(mutex);
  604. TextureLoadTaskClass* task=DeferredListHead;
  605. if (task) {
  606. DeferredListHead=task->Peek_Succ();
  607. task->Set_Succ(NULL);
  608. }
  609. return task;
  610. }
  611. void Add_Deferred_Task(TextureLoadTaskClass* task)
  612. {
  613. CriticalSectionClass::LockClass m(mutex);
  614. WWASSERT(task->Peek_Succ()==NULL);
  615. task->Set_Succ(DeferredListHead);
  616. DeferredListHead=task;
  617. }
  618. void TextureLoader::Flush_Pending_Load_Tasks()
  619. {
  620. while (!Is_Load_List_Empty()) {
  621. Update();
  622. ThreadClass::Switch_Thread();
  623. }
  624. }
  625. void TextureLoader::Update()
  626. {
  627. WWASSERT_PRINT(DX8Wrapper::_Get_Main_Thread_ID()==ThreadClass::_Get_Current_Thread_ID(),"TextureLoader::Update must be called from the main thread!");
  628. while (TextureLoadTaskClass* task=Get_Deferred_Task()) {
  629. task->Begin_Texture_Load(); // This will add the task to load list
  630. }
  631. while (TextureLoadTaskClass* task=Get_Finished_Task()) {
  632. task->End_Load();
  633. task->Apply(true);
  634. TextureLoadTaskClass::Release_Instance(task);
  635. }
  636. while (TextureLoadTaskClass* task=Get_Thumbnail_Task()) {
  637. task->Begin_Thumbnail_Load();
  638. }
  639. while (TextureLoadTaskClass* task=Get_Task_From_Delete_List()) {
  640. // delete task;
  641. TextureLoadTaskClass::Release_Instance(task);
  642. }
  643. }
  644. // ----------------------------------------------------------------------------
  645. static DWORD VectortoRGBA( D3DXVECTOR3* v, FLOAT fHeight )
  646. {
  647. DWORD r = (DWORD)( 127.0f * v->x + 128.0f );
  648. DWORD g = (DWORD)( 127.0f * v->y + 128.0f );
  649. DWORD b = (DWORD)( 127.0f * v->z + 128.0f );
  650. DWORD a = (DWORD)( 255.0f * fHeight );
  651. return( (a<<24L) + (r<<16L) + (g<<8L) + (b<<0L) );
  652. }
  653. IDirect3DTexture8* TextureLoader::Generate_Bumpmap(TextureClass* texture)
  654. {
  655. WW3DFormat bump_format=WW3D_FORMAT_U8V8;
  656. if (!DX8Caps::Support_Texture_Format(bump_format)) {
  657. return MissingTexture::_Get_Missing_Texture();
  658. }
  659. D3DSURFACE_DESC desc;
  660. IDirect3DTexture8* src_d3d_tex=texture->Peek_DX8_Texture();
  661. WWASSERT(src_d3d_tex);
  662. DX8_ErrorCode(src_d3d_tex->GetLevelDesc(0,&desc));
  663. unsigned width=desc.Width;
  664. unsigned height=desc.Height;
  665. IDirect3DTexture8* d3d_texture = DX8Wrapper::_Create_DX8_Texture(
  666. width,
  667. height,
  668. bump_format,
  669. TextureClass::MIP_LEVELS_1);
  670. D3DLOCKED_RECT src_locked_rect;
  671. DX8_ErrorCode(
  672. texture->Peek_DX8_Texture()->LockRect(
  673. 0,
  674. &src_locked_rect,
  675. NULL,
  676. D3DLOCK_READONLY));
  677. D3DLOCKED_RECT dest_locked_rect;
  678. DX8_ErrorCode(
  679. d3d_texture->LockRect(
  680. 0,
  681. &dest_locked_rect,
  682. NULL,
  683. 0));
  684. WW3DFormat format=D3DFormat_To_WW3DFormat(desc.Format);
  685. unsigned bpp=Get_Bytes_Per_Pixel(format);
  686. for( unsigned y=0; y<desc.Height; y++ )
  687. {
  688. unsigned char* dest_ptr = (unsigned char*)dest_locked_rect.pBits;
  689. dest_ptr+=y*dest_locked_rect.Pitch;
  690. unsigned char* src_ptr_mid = (unsigned char*)src_locked_rect.pBits;
  691. src_ptr_mid+=y*src_locked_rect.Pitch;
  692. unsigned char* src_ptr_next_line = ( src_ptr_mid + src_locked_rect.Pitch );
  693. unsigned char* src_ptr_prev_line = ( src_ptr_mid - src_locked_rect.Pitch );
  694. if( y == desc.Height-1 ) // Don't go past the last line
  695. src_ptr_next_line = src_ptr_mid;
  696. if( y == 0 ) // Don't go before first line
  697. src_ptr_prev_line = src_ptr_mid;
  698. for( unsigned x=0; x<desc.Width; x++ )
  699. {
  700. unsigned pixel00;
  701. unsigned pixel01;
  702. unsigned pixelM1;
  703. unsigned pixel10;
  704. unsigned pixel1M;
  705. BitmapHandlerClass::Read_B8G8R8A8(pixel00,src_ptr_mid,format,NULL,0);
  706. BitmapHandlerClass::Read_B8G8R8A8(pixel01,src_ptr_mid+bpp,format,NULL,0);
  707. BitmapHandlerClass::Read_B8G8R8A8(pixelM1,src_ptr_mid-bpp,format,NULL,0);
  708. BitmapHandlerClass::Read_B8G8R8A8(pixel10,src_ptr_prev_line,format,NULL,0);
  709. BitmapHandlerClass::Read_B8G8R8A8(pixel1M,src_ptr_next_line,format,NULL,0);
  710. // Convert to luminance
  711. unsigned char bv00;
  712. unsigned char bv01;
  713. unsigned char bvM1;
  714. unsigned char bv10;
  715. unsigned char bv1M;
  716. BitmapHandlerClass::Write_B8G8R8A8(&bv00,WW3D_FORMAT_L8,pixel00);
  717. BitmapHandlerClass::Write_B8G8R8A8(&bv01,WW3D_FORMAT_L8,pixel01);
  718. BitmapHandlerClass::Write_B8G8R8A8(&bvM1,WW3D_FORMAT_L8,pixelM1);
  719. BitmapHandlerClass::Write_B8G8R8A8(&bv10,WW3D_FORMAT_L8,pixel10);
  720. BitmapHandlerClass::Write_B8G8R8A8(&bv1M,WW3D_FORMAT_L8,pixel1M);
  721. int v00=bv00,v01=bv01,vM1=bvM1,v10=bv10,v1M=bv1M;
  722. int iDu = (vM1-v01); // The delta-u bump value
  723. int iDv = (v1M-v10); // The delta-v bump value
  724. if( (v00 < vM1) && (v00 < v01) ) // If we are at valley
  725. {
  726. iDu = vM1-v00; // Choose greater of 1st order diffs
  727. if( iDu < v00-v01 )
  728. iDu = v00-v01;
  729. }
  730. // The luminance bump value (land masses are less shiny)
  731. unsigned short uL = ( v00>1 ) ? 63 : 127;
  732. switch( bump_format )
  733. {
  734. case WW3D_FORMAT_U8V8:
  735. *dest_ptr++ = (unsigned char)iDu;
  736. *dest_ptr++ = (unsigned char)iDv;
  737. break;
  738. case WW3D_FORMAT_L6V5U5:
  739. *(unsigned short*)dest_ptr = (unsigned short)( ( (iDu>>3) & 0x1f ) << 0 );
  740. *(unsigned short*)dest_ptr |= (unsigned short)( ( (iDv>>3) & 0x1f ) << 5 );
  741. *(unsigned short*)dest_ptr |= (unsigned short)( ( ( uL>>2) & 0x3f ) << 10 );
  742. dest_ptr += 2;
  743. break;
  744. case WW3D_FORMAT_X8L8V8U8:
  745. *dest_ptr++ = (unsigned char)iDu;
  746. *dest_ptr++ = (unsigned char)iDv;
  747. *dest_ptr++ = (unsigned char)uL;
  748. *dest_ptr++ = (unsigned char)0L;
  749. break;
  750. }
  751. // Move one pixel to the left (src is 32-bpp)
  752. src_ptr_mid+=bpp;
  753. src_ptr_prev_line+=bpp;
  754. src_ptr_next_line+=bpp;
  755. }
  756. }
  757. DX8_ErrorCode(d3d_texture->UnlockRect(0));
  758. DX8_ErrorCode(texture->Peek_DX8_Texture()->UnlockRect(0));
  759. return d3d_texture;
  760. }
  761. // ----------------------------------------------------------------------------
  762. //
  763. // Public texture request functions. These functions can be used to request
  764. // texture loading.
  765. //
  766. // ----------------------------------------------------------------------------
  767. void TextureLoader::Add_Load_Task(TextureClass* tc)
  768. {
  769. // If the texture is already being loaded we just exit here.
  770. if (tc->TextureLoadTask) return;
  771. TextureLoadTaskClass* task=TextureLoadTaskClass::Get_Instance(tc,false);
  772. task->Begin_Texture_Load();
  773. }
  774. // ----------------------------------------------------------------------------
  775. void TextureLoader::Request_High_Priority_Loading(
  776. TextureClass* tc,
  777. TextureClass::MipCountType mip_level_count)
  778. {
  779. TextureLoadTaskClass* task=TextureLoadTaskClass::Get_Instance(tc,true);
  780. task->Begin_Texture_Load();
  781. }
  782. // ----------------------------------------------------------------------------
  783. void TextureLoader::Request_Thumbnail(TextureClass* tc)
  784. {
  785. // If the texture is already being loaded we just exit here.
  786. if (tc->TextureLoadTask) return;
  787. TextureLoadTaskClass* task=TextureLoadTaskClass::Get_Instance(tc,false);
  788. task->Begin_Thumbnail_Load();
  789. }
  790. // ----------------------------------------------------------------------------
  791. //
  792. // Texture loader task handler
  793. //
  794. // ----------------------------------------------------------------------------
  795. TextureLoadTaskClass::TextureLoadTaskClass()
  796. :
  797. Texture(0),
  798. Succ(0),
  799. D3DTexture(0),
  800. Width(0),
  801. Height(0),
  802. Format(WW3D_FORMAT_UNKNOWN),
  803. IsLoading(false),
  804. HasFailed(false),
  805. MipLevelCount(0),
  806. HighPriorityRequested(false),
  807. Reduction(0)
  808. {
  809. }
  810. // ----------------------------------------------------------------------------
  811. //
  812. // Destruct texture load task. The load task deinitialization will fail from
  813. // any other than the main thread so the task must be either deleted from the
  814. // main thread or deinitialized in the main thread prior to deleting it in
  815. // another thread.
  816. //
  817. // ----------------------------------------------------------------------------
  818. TextureLoadTaskClass::~TextureLoadTaskClass()
  819. {
  820. Deinit();
  821. }
  822. void TextureLoadTaskClass::Init(TextureClass* tc,bool high_priority)
  823. {
  824. // Make sure texture has a filename.
  825. REF_PTR_SET(Texture,tc);
  826. WWASSERT(Texture->Get_Full_Path() != NULL);
  827. Reduction=Texture->Get_Reduction();
  828. HighPriorityRequested=high_priority;
  829. IsLoading=false;
  830. HasFailed=false;
  831. MipLevelCount=tc->MipLevelCount;
  832. D3DTexture=0;
  833. Width=0;
  834. Height=0;
  835. Format=Texture->Get_Texture_Format();
  836. Texture->TextureLoadTask=this;
  837. for (int i=0;i<TextureClass::MIP_LEVELS_MAX;++i) {
  838. LockedSurfacePtr[i]=NULL;
  839. LockedSurfacePitch[i]=0;
  840. }
  841. }
  842. void TextureLoadTaskClass::Deinit()
  843. {
  844. WWASSERT(Succ==NULL);
  845. WWASSERT(D3DTexture==NULL);
  846. WWASSERT(IsLoading==false);
  847. for (int i=0;i<TextureClass::MIP_LEVELS_MAX;++i) {
  848. WWASSERT(LockedSurfacePtr[i]==NULL);
  849. }
  850. if (Texture) {
  851. WWASSERT(Texture->TextureLoadTask==this);
  852. Texture->TextureLoadTask=NULL;
  853. }
  854. REF_PTR_RELEASE(Texture);
  855. WWASSERT(!D3DTexture);
  856. }
  857. // ----------------------------------------------------------------------------
  858. //
  859. //
  860. //
  861. // ----------------------------------------------------------------------------
  862. void TextureLoadTaskClass::Begin_Texture_Load()
  863. {
  864. // If we're in main thread, init for loading and add to the load list
  865. if (ThreadClass::_Get_Current_Thread_ID()==DX8Wrapper::_Get_Main_Thread_ID()) {
  866. bool loaded=false;
  867. if (Texture->Is_Compression_Allowed()) {
  868. DDSFileClass dds_file(Texture->Get_Full_Path(),Get_Reduction());
  869. if (dds_file.Is_Available()) {
  870. // Destination size will be the next power of two square from the larger width and height...
  871. unsigned width, height;
  872. width=dds_file.Get_Width(0);
  873. height=dds_file.Get_Height(0);
  874. TextureLoader::Validate_Texture_Size(width,height);
  875. // If the size doesn't match, try and see if texture reduction would help... (mainly for
  876. // cases where loaded texture is larger than hardware limit)
  877. if (width!=dds_file.Get_Width(0) || height!=dds_file.Get_Height(0)) {
  878. for (unsigned i=1;i<dds_file.Get_Mip_Level_Count();++i) {
  879. unsigned w=dds_file.Get_Width(i);
  880. unsigned h=dds_file.Get_Height(i);
  881. TextureLoader::Validate_Texture_Size(width,height);
  882. if (w==dds_file.Get_Width(i) || h==dds_file.Get_Height(i)) {
  883. Reduction+=i;
  884. width=w;
  885. height=h;
  886. break;
  887. }
  888. }
  889. }
  890. IsLoading=true;
  891. Width=width;
  892. Height=height;
  893. Format=Get_Valid_Texture_Format(dds_file.Get_Format(),Texture->Is_Compression_Allowed());
  894. unsigned mip_level_count=Get_Mip_Level_Count();
  895. // If texture wants all mip levels, take as many as the file contains (not necessarily all)
  896. // Otherwise take as many mip levels as the texture wants, not to exceed the count in file...
  897. if (!mip_level_count) mip_level_count=dds_file.Get_Mip_Level_Count();
  898. else if (mip_level_count>dds_file.Get_Mip_Level_Count()) mip_level_count=dds_file.Get_Mip_Level_Count();
  899. // Once more, verify that the mip level count is correct (in case it was changed here it might not
  900. // match the size...well actually it doesn't have to match but it can't be bigger than the size)
  901. unsigned max_mip_level_count=1;
  902. unsigned w=4;
  903. unsigned h=4;
  904. while (w<Width && h<Height) {
  905. w+=w;
  906. h+=h;
  907. max_mip_level_count++;
  908. }
  909. if (mip_level_count>max_mip_level_count) mip_level_count=max_mip_level_count;
  910. D3DTexture=DX8Wrapper::_Create_DX8_Texture(Width,Height,Format,(TextureClass::MipCountType)mip_level_count);
  911. MipLevelCount=mip_level_count;
  912. //Texture->MipLevelCount);
  913. loaded=true;
  914. }
  915. }
  916. if (!loaded) {
  917. Targa targa;
  918. if (TARGA_ERROR_HANDLER(targa.Open(Texture->Get_Full_Path(), TGA_READMODE),Texture->Get_Full_Path())) {
  919. D3DTexture=MissingTexture::_Get_Missing_Texture();
  920. HasFailed=true;
  921. IsLoading=false;
  922. End_Load();
  923. Apply(true);
  924. Add_Task_To_Delete_List(this);
  925. return;
  926. }
  927. unsigned bpp;
  928. WW3DFormat src_format,dest_format;
  929. Get_WW3D_Format(dest_format,src_format,bpp,targa);
  930. if (src_format!=WW3D_FORMAT_A8R8G8B8 &&
  931. src_format!=WW3D_FORMAT_R8G8B8 &&
  932. src_format!=WW3D_FORMAT_X8R8G8B8) {
  933. WWDEBUG_SAY(("Invalid TGA format used in %s - only 24 and 32 bit formats should be used!\n",Texture->Get_Full_Path()));
  934. }
  935. // Destination size will be the next power of two square from the larger width and height...
  936. unsigned width=targa.Header.Width, height=targa.Header.Height;
  937. int ReductionFactor=Get_Reduction();
  938. int MipLevels=0;
  939. //Figure out how many mip levels this texture will occupy
  940. for (int i=width, j=height; i > 0 && j > 0; i>>=1, j>>=1)
  941. MipLevels++;
  942. //Adjust the reduction factor to keep textures above some minimum dimensions
  943. if (MipLevels <= WW3D::Get_Texture_Min_Mip_Levels())
  944. ReductionFactor=0;
  945. else
  946. { int mipToDrop=MipLevels-WW3D::Get_Texture_Min_Mip_Levels();
  947. if (ReductionFactor >= mipToDrop)
  948. ReductionFactor=mipToDrop;
  949. }
  950. width=targa.Header.Width>>ReductionFactor;
  951. height=targa.Header.Height>>ReductionFactor;
  952. unsigned ow=width;
  953. unsigned oh=height;
  954. TextureLoader::Validate_Texture_Size(width,height);
  955. if (width!=ow || height!=oh) {
  956. WWDEBUG_SAY(("Invalid texture size, scaling required. Texture: %s, size: %d x %d -> %d x %d\n",Texture->Get_Full_Path(),ow,oh,width,height));
  957. }
  958. IsLoading=true;
  959. Width=width;
  960. Height=height;
  961. if (Format==WW3D_FORMAT_UNKNOWN) {
  962. Format=Get_Valid_Texture_Format(dest_format,false);
  963. }
  964. else {
  965. Format=Get_Valid_Texture_Format(Format,false);
  966. }
  967. D3DTexture=DX8Wrapper::_Create_DX8_Texture(Width,Height,Format,Texture->MipLevelCount);
  968. }
  969. MipLevelCount=D3DTexture->GetLevelCount();
  970. for (unsigned i=0;i<MipLevelCount;++i) {
  971. D3DLOCKED_RECT locked_rect;
  972. DX8_ErrorCode(
  973. D3DTexture->LockRect(
  974. i,
  975. &locked_rect,
  976. NULL,
  977. 0));
  978. LockedSurfacePtr[i]=(unsigned char*)locked_rect.pBits;
  979. LockedSurfacePitch[i]=locked_rect.Pitch;
  980. }
  981. if (HighPriorityRequested) {
  982. TextureLoader::Load_Mipmap_Levels(this);
  983. End_Load();
  984. Apply(true);
  985. Add_Task_To_Delete_List(this);
  986. }
  987. else {
  988. LoaderThreadClass::Add_Task_To_Load_List(this);
  989. }
  990. }
  991. // Otherwise add to deferred list which will be handled by main thread
  992. else {
  993. Add_Deferred_Task(this);
  994. }
  995. }
  996. /* file_auto_ptr my_tga_file(_TheFileFactory,Texture->Get_Full_Path());
  997. if (my_tga_file->Is_Available()) {
  998. my_tga_file->Open();
  999. unsigned size=my_tga_file->Size();
  1000. char* tga_memory=W3DNEWARRAY char[size];
  1001. my_tga_file->Read(tga_memory,size);
  1002. my_tga_file->Close();
  1003. StringClass pth("data\\");
  1004. pth+=Texture->Get_Texture_Name();
  1005. RawFileClass tmp_tga_file(pth);
  1006. tmp_tga_file.Create();
  1007. tmp_tga_file.Write(tga_memory,size);
  1008. tmp_tga_file.Close();
  1009. delete[] tga_memory;
  1010. }
  1011. */
  1012. // ----------------------------------------------------------------------------
  1013. //
  1014. //
  1015. //
  1016. // ----------------------------------------------------------------------------
  1017. void TextureLoadTaskClass::Begin_Thumbnail_Load()
  1018. {
  1019. // CriticalSectionClass::LockClass m(mutex);
  1020. unsigned thread_id=ThreadClass::_Get_Current_Thread_ID();
  1021. if (thread_id==DX8Wrapper::_Get_Main_Thread_ID()) {
  1022. WW3DFormat format=Texture->Get_Texture_Format();
  1023. // No compressed thumbnails
  1024. switch (format) {
  1025. case WW3D_FORMAT_DXT1:
  1026. case WW3D_FORMAT_DXT2:
  1027. case WW3D_FORMAT_DXT3:
  1028. case WW3D_FORMAT_DXT4:
  1029. case WW3D_FORMAT_DXT5:
  1030. format=WW3D_FORMAT_A8R8G8B8; break;
  1031. default:
  1032. break;
  1033. }
  1034. D3DTexture=TextureLoader::Load_Thumbnail(Texture->Get_Full_Path(),format);
  1035. // Thumbnail loads are always high priority, so apply immediatelly
  1036. End_Load();
  1037. Apply(false);
  1038. Add_Task_To_Delete_List(this);
  1039. return;
  1040. }
  1041. else {
  1042. Add_Thumbnail_Task(this);
  1043. }
  1044. }
  1045. // ----------------------------------------------------------------------------
  1046. //
  1047. // Deinit can be called multiple times. If any surfaces are locked this call
  1048. // can only be called from the main thread.
  1049. //
  1050. // ----------------------------------------------------------------------------
  1051. void TextureLoadTaskClass::End_Load()
  1052. {
  1053. for (unsigned i=0;i<MipLevelCount;++i) {
  1054. if (LockedSurfacePtr[i]) {
  1055. WWASSERT(ThreadClass::_Get_Current_Thread_ID()==DX8Wrapper::_Get_Main_Thread_ID());
  1056. DX8_ErrorCode(D3DTexture->UnlockRect(i));
  1057. }
  1058. LockedSurfacePtr[i]=NULL;
  1059. }
  1060. IsLoading=false;
  1061. }
  1062. // ----------------------------------------------------------------------------
  1063. //
  1064. // Link the node to another task node. This can only be done if the node isn't
  1065. // linked to something else already. If the node is linked to some other node,
  1066. // the only acceptable parameter to this function is NULL, which will unlink
  1067. // the connection.
  1068. //
  1069. // ----------------------------------------------------------------------------
  1070. void TextureLoadTaskClass::Set_Succ(TextureLoadTaskClass* succ)
  1071. {
  1072. WWASSERT((succ && !Succ) || (!succ)); // Can't set successor pointer if it has been set already
  1073. Succ=succ;
  1074. }
  1075. // ----------------------------------------------------------------------------
  1076. //
  1077. //
  1078. //
  1079. // ----------------------------------------------------------------------------
  1080. void TextureLoadTaskClass::Set_D3D_Texture(IDirect3DTexture8* texture)
  1081. {
  1082. WWASSERT(D3DTexture==0);
  1083. D3DTexture=texture;
  1084. }
  1085. // ----------------------------------------------------------------------------
  1086. //
  1087. // Apply the D3D texture surface to the client texture. The parameter 'initialize'
  1088. // determines whether the client texture will be set to initialized state or
  1089. // not. Generally thumbnail tasks will not set texture to initialised state but
  1090. // all other loads do.
  1091. //
  1092. // ----------------------------------------------------------------------------
  1093. void TextureLoadTaskClass::Apply(bool initialize)
  1094. {
  1095. WWASSERT(D3DTexture);
  1096. // Verify that none of the mip levels are locked
  1097. for (unsigned i=0;i<MipLevelCount;++i) {
  1098. WWASSERT(LockedSurfacePtr[i]==NULL);
  1099. }
  1100. Texture->Apply_New_Surface(initialize);
  1101. D3DTexture->Release();
  1102. D3DTexture=NULL;
  1103. }
  1104. // ----------------------------------------------------------------------------
  1105. //
  1106. // Return locked surface pointer at a specific level. The call will
  1107. // assert if level is greater or equal to the number of mip levels or if the
  1108. // requested level has not been locked.
  1109. //
  1110. // ----------------------------------------------------------------------------
  1111. unsigned char* TextureLoadTaskClass::Get_Locked_Surface_Ptr(unsigned level)
  1112. {
  1113. WWASSERT(level<MipLevelCount);
  1114. WWASSERT(LockedSurfacePtr[level]);
  1115. return LockedSurfacePtr[level];
  1116. }
  1117. // ----------------------------------------------------------------------------
  1118. //
  1119. // Return locked surface pitch (in bytes) at a specific level. The call will
  1120. // assert if level is greater or equal to the number of mip levels or if the
  1121. // requested level has not been locked.
  1122. //
  1123. // ----------------------------------------------------------------------------
  1124. unsigned TextureLoadTaskClass::Get_Locked_Surface_Pitch(unsigned level) const
  1125. {
  1126. WWASSERT(level<MipLevelCount);
  1127. WWASSERT(LockedSurfacePtr[level]);
  1128. return LockedSurfacePitch[level];
  1129. }
  1130. // ----------------------------------------------------------------------------
  1131. //
  1132. // Load tasks are stored in a pool when they are not used. If the pool is empty
  1133. // a new task is created.
  1134. //
  1135. // ----------------------------------------------------------------------------
  1136. TextureLoadTaskClass* TextureLoadTaskClass::Get_Instance(TextureClass* tc, bool high_priority)
  1137. {
  1138. CriticalSectionClass::LockClass m(mutex);
  1139. TextureLoadTaskClass* task=FreeTaskListHead;
  1140. if (task) {
  1141. FreeTaskListHead=task->Peek_Succ();
  1142. task->Set_Succ(NULL);
  1143. }
  1144. else {
  1145. task=W3DNEW TextureLoadTaskClass();
  1146. }
  1147. task->Init(tc,high_priority);
  1148. return task;
  1149. }
  1150. // ----------------------------------------------------------------------------
  1151. //
  1152. // When task is no longer needed it is returned to the pool.
  1153. //
  1154. // ----------------------------------------------------------------------------
  1155. void TextureLoadTaskClass::Release_Instance(TextureLoadTaskClass* task)
  1156. {
  1157. if (!task) return;
  1158. CriticalSectionClass::LockClass m(mutex);
  1159. task->Deinit();
  1160. // Task must not be in any list when it is being freed
  1161. WWASSERT(task->Peek_Succ()==NULL);
  1162. task->Set_Succ(FreeTaskListHead);
  1163. FreeTaskListHead=task;
  1164. }