Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
texturefile.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(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
19#include "texturefile.h"
20#include "textureloader.h"
21#include "ww3d.h"
22#include "wwstring.h"
23#include "rawfile.h"
24#include "ffactory.h"
25#include "nstrdup.h"
26#include "texfcach.h"
27#include "assetmgr.h"
28
29#ifdef WW3D_DX8
30#include <srCore.hpp>
31#include <srSurfaceIOManager.hpp>
32#include "texture.h"
33
34static TextureFileClass* head;
35static bool mipmaps=true;
36static int texture_count;
37static int locked_surface_count;
38static int texture_id;
39
40/*
41** Definitions of static members:
42*/
43unsigned int TextureFileClass::_CurrentTimeStamp = 0U;
44float TextureFileClass::_SwitchThreshold = 0.75f; // Default value
45
46// ----------------------------------------------------------------------------
47
48static int Calculate_Size(srColorSurfaceIFace* surface,int red_factor=0)
49{
50 // Set performance statistics
51 int p=surface->getPitch();
52 int h=surface->getHeight();
53 int size=0;
54 p<<=red_factor;
55 h<<=red_factor;
56 while (h) {
57 size+=p*h;
58 p>>=1;
59 h>>=1;
60 }
61 return size;
62}
63
64// ----------------------------------------------------------------------------
65
66void TextureFileClass::Switch_Mipmaping_Debug()
67{
68 mipmaps=!mipmaps;
69 TextureFileClass* n=head;
70 while (n) {
71 if (mipmaps) n->params.setMipmap(MIPMAP_DEFAULT);
72 else n->params.setMipmap(MIPMAP_NONE);
73 n=n->Succ;
74 }
75}
76
77// ----------------------------------------------------------------------------
78
79int TextureFileClass::Get_Total_Locked_Surface_Size()
80{
81 int total_locked_surface_size=0;
82 TextureFileClass* n=head;
83 while (n) {
84 total_locked_surface_size+=n->Get_Locked_Surface_Size();
85 n=n->Succ;
86 }
87 return total_locked_surface_size;
88}
89
90// ----------------------------------------------------------------------------
91
92int TextureFileClass::Get_Total_Texture_Size()
93{
94 int total_texture_size=0;
95 TextureFileClass* n=head;
96 while (n) {
97 total_texture_size+=n->Get_Texture_Size();
98 n=n->Succ;
99 }
100 return total_texture_size;
101}
102
103// ----------------------------------------------------------------------------
104
105int TextureFileClass::Get_Total_Non_Reduced_Texture_Size()
106{
107 int total_texture_size=0;
108 TextureFileClass* n=head;
109 while (n) {
110 total_texture_size+=n->Get_Non_Reduced_Texture_Size();
111 n=n->Succ;
112 }
113 return total_texture_size;
114}
115
116// ----------------------------------------------------------------------------
117
118int TextureFileClass::Get_Total_Texture_Count()
119{
120 return texture_count;
121}
122
123// ----------------------------------------------------------------------------
124
125int TextureFileClass::Get_Total_Locked_Surface_Count()
126{
127 return locked_surface_count;
128}
129
130StringClass TextureFileClass::List_Missing_Files() // Print out a list of missing files
131{
132 StringClass s(0,true);
133
134 TextureFileClass* n=head;
135 s=
136 "Missing textures\n\n"
137 "id name\n"
138 "------------------------\n";
139 int missing_count=0;
140 while (n) {
141 if (n->Get_File_Error()) {
142 StringClass tmp;
143 tmp.Format("%4.4d %s\n",n->ID(),n->Get_File_Name());
144 s+=tmp;
145 }
146 n=n->Succ;
147 }
148 if (!missing_count) s="No missing textures\n\n";
149 else {
150 StringClass tmp;
151 tmp.Format("\nTotal missing textures: %d",missing_count);
152 s+=tmp;
153 }
154 return s;
155}
156
157// ----------------------------------------------------------------------------
158
159TextureFileClass::TextureFileClass(const char * filename) :
160 LockedSurface(0),
161 LockedSurfaceReductionFactor(0),
162 LockedSurfaceSize(0),
163 TextureSize(0),
164 NonReducedTextureSize(0),
165 CurrentReductionFactor(0),
166 DesiredReductionFactor(0.0f),
167 TimeStampOfLastRequestReductionCall(_CurrentTimeStamp - 1),
168 TimeStampOfLastProcessReductionCall(_CurrentTimeStamp - 1),
169 FileName(0),
170 TextureFrameHandle(getNewFrameHandle()),
171 TempSurfacePtr(0),
172 id(texture_id++),
173 flash(false),
174 flash_store_surface(NULL),
175 file_error(false)
176{
177 if(filename && *filename) {
178 FileName = nstrdup(filename);
179 } else {
180 FileName = 0;
181 }
182
183 if(FileName) {
184 setName(FileName);
185 if (!TextureLoader::Texture_File_Exists(FileName)) {
186 file_error=true;
187 }
188 }
189
190 // Load locked surface (or not) according to current locked surface reduction factor set in
191 // texture loader
192 Load_Locked_Surface();
193
194 texture_count++;
195 Succ=head;
196 head=this;
197
198 flags.clear(GENERATESURFACE_FAILURE);
199 flags.set(DIRTY_DEFAULTS);
200
201 if (WW3D::Get_Texture_Thumbnail_Mode()!=WW3D::TEXTURE_THUMBNAIL_MODE_RESIZING) {
202 ReductionEnabled=false;
203 }
204
205}
206
207TextureFileClass::~TextureFileClass()
208{
209 texture_count--;
210 if (flash_store_surface) flash_store_surface->release();
211 if (LockedSurface) locked_surface_count--;
212 TextureFileClass* n=head;
213 if (n==this) {
214 head=Succ;
215 }
216 else {
217 while (n) {
218 if (n->Succ==this) {
219 n->Succ=Succ;
220 break;
221 }
222 n=n->Succ;
223 }
224 }
225
226 invalidate();
227
228 if (FileName) delete [] FileName;
229 if (LockedSurface) LockedSurface->release();
230}
231
232// Copy CTor and assignment operator assert for now - if anyone hits the
233// assert we might need to actually implement them 8^).
234TextureFileClass::TextureFileClass(const TextureFileClass & src)
235{
236 WWASSERT(0);
237}
238
239TextureFileClass & TextureFileClass::operator = (const TextureFileClass &that)
240{
241 if (this != &that) {
242
243 srTexture::operator = (that);
244
245 WWASSERT(0);
246
247 }
248 return *this;
249}
250
251srTextureIFace::FrameHandle TextureFileClass::getTextureFrameHandle(void)
252{
253 if (flags.test(GENERATESURFACE_FAILURE))
254 return 0;
255
256 return TextureFrameHandle;
257}
258
259void TextureFileClass::getMipmapData(MultiRequest& m)
260{
261 // First some assertions...
262 WWASSERT(m.largeLOD <= m.smallLOD && m.smallLOD < MAX_LOD);
263 WWASSERT_PRINT(m.levels[m.largeLOD],"TextureFileClass::getMipmapData() -- NULL surface passed in!\n");
264 if (!m.levels[m.largeLOD]) return;
265
266 /*
267 ** If the temporary surface pointer points to a surface (this will be a new surface which just
268 ** got loaded in this frame) use it and release it - done.
269 ** Otherwise, if we have a locked surface, then normally the current reduction factor should
270 ** be equal or greater to the locked surface reduction factor so we can use the locked surface
271 ** (possibly scaled down). If the current reduction factor is less than that of the locked
272 ** surface, this means that in the past we have loaded up a surface larger than the locked
273 ** surface, and it has just been thrown out of the API/GERD/HW texture cache. In this case we
274 ** have no choice but to scale the locked surface up to fill the surface handed to us, and we
275 ** will request a background load task for the real surface.
276 ** If there is no locked surface, this means that the texture is never reduced or loaded in
277 ** the background - we perform a normal blocking load of the surface, use it, and release it
278 ** (if you want behavior similar to "cached mode" in srTextureFile you should set the locked
279 ** surface reduction factor to 0, instead of having no locked surface).
280 */
281 if (TempSurfacePtr) {
282 Fill_Multi_Request_From_Surface(m, TempSurfacePtr);
283 Release_Temp_Surface();
284 } else {
285 TextureSize=0;
286 NonReducedTextureSize=0;
287 if (LockedSurface) {
288 Fill_Multi_Request_From_Surface(m, LockedSurface);
289 if (CurrentReductionFactor < LockedSurfaceReductionFactor) {
290 WWDEBUG_SAY(("Scaling locked surface up!\n"));
291 CurrentReductionFactor = LockedSurfaceReductionFactor;
292 float des_red_factor = floor(DesiredReductionFactor + 0.5f);
293 WWASSERT(des_red_factor >= 0.0f);
294 unsigned int int_desired_reduction_factor = (int)des_red_factor;
295
296 // If int_desired_reduction_factor is smaller than the locked surface reduction
297 // factor (meaning we want a larger surface), request to load it.
298 if (int_desired_reduction_factor < LockedSurfaceReductionFactor) {
299 texture_loader_info.reduction_factor = int_desired_reduction_factor;
300 TextureLoader::Add_Load_Task(this);
301 }
302 }
303 else {
304 // This else clause should only be entered in cases where Process_Reduction() is not
305 // called every frame on the texture for some reason. Such textures will not resize,
306 // but this at least ensures that they will get loaded.
307 Process_Reduction();
308 }
309 } else {
310 Load_Temp_Surface();
311 if (TempSurfacePtr) {
312 TextureSize=Calculate_Size(TempSurfacePtr);
313 NonReducedTextureSize=TextureSize;
314
315 Fill_Multi_Request_From_Surface(m, TempSurfacePtr);
316 Release_Temp_Surface();
317 }
318 }
319 }
320}
321
322void TextureFileClass::invalidate(void)
323{
324 Release_Temp_Surface();
325 invalidateFrameHandle(TextureFrameHandle);
326 flags.clear(GENERATESURFACE_FAILURE);
327}
328
329void TextureFileClass::setupDefaultValues(void)
330{
331 if (flags.test(DIRTY_DEFAULTS))
332 {
333 flags.clear (DIRTY_DEFAULTS);
334
335 /*
336 ** If the temporary surface pointer points to a surface (this will be a new surface which
337 ** just got loaded in this frame by Apply_New_Surface) get defaults from it - done.
338 ** Otherwise, if we have a locked surface, then normally the current reduction factor should
339 ** be equal or greater to the locked surface reduction factor so we can get defaults from
340 ** the locked surface (possibly scaled down). If the current reduction factor is less than
341 ** that of the locked surface, this means that in the past we have loaded up a surface
342 ** larger than the locked surface, and it has just been thrown out of the API/GERD/HW
343 ** texture cache. In this case we set the current reduction factor to be equal to that of
344 ** the locked surface, and get defaults from the locked surface.
345 ** If there is no locked surface, this means that the texture is never reduced or loaded in
346 ** the background - we perform a normal blocking load of the surface, store it in the temp
347 ** surface pointer (so getMipmapData can also use it), and get defaults from it.
348 */
349 if (TempSurfacePtr) {
350 setupDefaultValuesFromSurface(TempSurfacePtr);
351 TextureSize=Calculate_Size(TempSurfacePtr);
352 NonReducedTextureSize=Calculate_Size(TempSurfacePtr,CurrentReductionFactor);
353 } else {
354 if (LockedSurface) {
355 setupDefaultValuesFromSurface(LockedSurface);
356 if (CurrentReductionFactor >= LockedSurfaceReductionFactor) {
357 // Scale size down by difference between current and locked reduction factors
358 unsigned int diff = CurrentReductionFactor - LockedSurfaceReductionFactor;
359 defaultDimensions.width = defaultDimensions.width >> diff;
360 if (defaultDimensions.width == 0) defaultDimensions.width = 1;
361 defaultDimensions.height = defaultDimensions.height >> diff;
362 if (defaultDimensions.height == 0) defaultDimensions.height = 1;
363 } else {
364 CurrentReductionFactor = LockedSurfaceReductionFactor;
365 }
366 TextureSize=0;
367 NonReducedTextureSize=0;
368 } else {
369 Load_Temp_Surface();
370 if (TempSurfacePtr) {
371 setupDefaultValuesFromSurface(TempSurfacePtr);
372 TextureSize=Calculate_Size(TempSurfacePtr);
373 NonReducedTextureSize=TextureSize;
374 }
375 }
376 }
377 }
378}
379
380void TextureFileClass::Apply_New_Surface()
381{
382 invalidate();
383 TempSurfacePtr = texture_loader_info.new_surface;
384 texture_loader_info.new_surface = 0;
385 flags.set(DIRTY_DEFAULTS);
386 CurrentReductionFactor = texture_loader_info.reduction_factor;
387}
388
389// This is used by an object to request a reduction factor on all its textures. Only the smallest
390// reduction factor requested in a given frame is preserved.
391void TextureFileClass::Request_Reduction(float reduction_factor)
392{
393 if (!ReductionEnabled) return;
394
395 if (reduction_factor < 0.0f) reduction_factor = 0.0f;
396
397 // If this is the first request in this time-stamp, overwrite desired reduction factor -
398 // otherwise current factor is the lesser of current and requested.
399 if (TimeStampOfLastRequestReductionCall != _CurrentTimeStamp) {
400
401 DesiredReductionFactor = reduction_factor;
402
403 // Update time stamp
404 TimeStampOfLastRequestReductionCall = _CurrentTimeStamp;
405
406 } else {
407 DesiredReductionFactor = MIN(reduction_factor, DesiredReductionFactor);
408 }
409}
410
411// This is used during rendering - if a textures desired reduction level is "different enough"
412// from its current one, we fix it: either by updating from the locked surface (if the reduction
413// factor is equal or greater to that of the locked surface) or by asking the texture loader to
414// load a new reduction level of the texture in the background.
415// NOTE - if there is no locked surface, we do nothing (since textures without locked surfaces do
416// not perform reduction).
417void TextureFileClass::Process_Reduction(void)
418{
419 // Check if this is the first Process_Reduction() call this frame - otherwise do nothing
420 if (TimeStampOfLastProcessReductionCall != _CurrentTimeStamp) {
421
422 // If we are doing reductions and the difference between the current and desired reduction
423 // factors is above the threshold, fix it.
424 if (LockedSurface && fabs(float(CurrentReductionFactor) - DesiredReductionFactor) > _SwitchThreshold) {
425
426 // Find the integer desired reduction factor
427 float des_red_factor = floor(DesiredReductionFactor + 0.5f);
428 WWASSERT(des_red_factor >= 0.0f);
429 unsigned int int_desired_reduction_factor = (int)des_red_factor;
430 if (int_desired_reduction_factor >= LockedSurfaceReductionFactor) {
431 // Can update from the locked surface:
432 CurrentReductionFactor = int_desired_reduction_factor;
433 invalidate();
434 } else {
435 // Need to load bigger surface
436 texture_loader_info.reduction_factor = int_desired_reduction_factor;
437 TextureLoader::Add_Load_Task(this);
438 }
439 }
440
441 // Update time stamp
442 TimeStampOfLastProcessReductionCall = _CurrentTimeStamp;
443
444 }
445}
446
447void TextureFileClass::_Set_Switch_Threshold(float switch_threshold)
448{
449 _SwitchThreshold = MIN(switch_threshold, 0.999);
450 _SwitchThreshold = MAX(_SwitchThreshold, 0.5);
451}
452
453float TextureFileClass::_Get_Switch_Threshold(void)
454{
455 return _SwitchThreshold;
456}
457
458void TextureFileClass::Load_Temp_Surface(void)
459{
460 if (TempSurfacePtr) invalidate();
461
462 if (!FileName) return;
463
464 TempSurfacePtr = 0;
465
466 //
467 // Use the texture file caching mechanism to load the surface. If no cache
468 // is initialized, then simply load the surface from the file.
469 //
470 TextureFileCache* cache=WW3DAssetManager::Get_Instance()->Texture_File_Cache();
471 if (cache != NULL) {
472 cache->Validate_Texture(FileName);
473 TempSurfacePtr = cache->Get_Surface(
474 Get_File_Name(),
475 0); // No reduction!
476 } else {
477 TempSurfacePtr = ::Load_Surface(FileName);
478 }
479}
480
481void TextureFileClass::Release_Temp_Surface(void)
482{
483 if (TempSurfacePtr)
484 {
485 TempSurfacePtr->release();
486 TempSurfacePtr = 0;
487 }
488}
489
490void TextureFileClass::Fill_Multi_Request_From_Surface(MultiRequest& m, srColorSurfaceIFace* surface)
491{
492 m.levels[m.largeLOD]->copy (*surface);
493 for (LOD i = m.largeLOD+1; i <= m.smallLOD; i++)
494 {
495 WWASSERT(m.levels[i] && m.levels[i-1]);
496 if (m.levels[i] && m.levels[i-1]) // just debug check
497 m.levels[i]->copy (*(m.levels[i-1]));
498 }
499}
500
501// Debug features
502
503// Make texture flash (Warning! Slow, for debug only!!!)
504void TextureFileClass::Set_Texture_Flash(bool b)
505{
506 flash=b;
507 if (flash) {
508 Load_Temp_Surface();
509 flash_store_surface=TempSurfacePtr;
510 flash_store_surface->addReference();
511 }
512 else {
513 // Restore the original surface... the surface will be released after applying to use
514 invalidateFrameHandle(TextureFrameHandle);
515 Release_Temp_Surface();
516 TempSurfacePtr=flash_store_surface;
517 flash_store_surface=NULL;
518 }
519}
520
521// Return texture flash state
522bool TextureFileClass::Get_Texture_Flash() const
523{
524 return flash;
525}
526
527TextureFileClass* TextureFileClass::Get_Texture(int id)
528{
529 TextureFileClass* n=head;
530 while (n) {
531 if (n->ID()==id) return n;
532 n=n->Succ;
533 }
534 return NULL;
535}
536
537static unsigned latest_time;
538static bool flash_state;
539static unsigned flash_counter;
540const int flash_time=500;
541
542void TextureFileClass::Update_Texture_Flash()
543{
544 int cur_time=WW3D::Get_Sync_Time();
545 int delta=cur_time-latest_time;
546 latest_time=cur_time;
547 flash_counter+=delta;
548 if (flash_counter>flash_time) {
549 flash_counter-=flash_time;
550 if (flash_counter>flash_time) flash_counter=0;
551 flash_state=!flash_state;
552 }
553 else return;
554
555 srColorSurface* s=NULL;
556 if (!flash_state) {
557 int w=srCore.getSurface()->getWidth();
558 srColorSurfaceIFace::PixelFormat pf;
559 srCore.getSurface()->getPixelFormat(pf);
560 s=W3DNEW srColorSurface(pf,w,w);
561 s->copy(*srCore.getSurface());
562 }
563
564 TextureFileClass* n=head;
565 while (n) {
566 if (n->flash) {
567 n->invalidateFrameHandle(n->TextureFrameHandle);
568 n->Release_Temp_Surface();
569 if (s) {
570 n->TempSurfacePtr=s;
571 s->addReference(); // Surface will be release after the use
572 }
573 else {
574 n->TempSurfacePtr=n->flash_store_surface;
575 n->TempSurfacePtr->addReference(); // Surface will be release after the use
576 }
577 }
578 n=n->Succ;
579 }
580 s->release();
581}
582
583// If false is passed, the texture will load the largest LOD size and stick to that
584
585void TextureFileClass::Enable_Reduction(bool b)
586{
587 if (ReductionEnabled==b) return;
588 ReductionEnabled=b;
589 if (b) {
590 Load_Locked_Surface();
591 }
592 else {
593 if (LockedSurface) LockedSurface->release();
594 LockedSurface=NULL;
595 LockedSurfaceReductionFactor=0;
596 CurrentReductionFactor=0;
597 DesiredReductionFactor=0;
598
599 Load_Temp_Surface();
600 if (TempSurfacePtr) {
601 setupDefaultValuesFromSurface(TempSurfacePtr);
602 TextureSize=Calculate_Size(TempSurfacePtr);
603 NonReducedTextureSize=TextureSize;
604 }
605 }
606}
607
608void TextureFileClass::Load_Locked_Surface()
609{
610 if (LockedSurface) LockedSurface->release();
611 LockedSurface=NULL;
612 LockedSurfaceReductionFactor=0;
613 if (FileName) {
614 TextureLoader::Load_Locked_Surface_Immediate(FileName, LockedSurface, LockedSurfaceReductionFactor);
615
616 if (LockedSurface) {
617
618 // If we have a locked surface, set the current reduction factor to be equal to the
619 // locked surface reduction factor (this is the highest resolution that we can render
620 // with at this time)
621 CurrentReductionFactor = LockedSurfaceReductionFactor;
622
623 LockedSurfaceSize=Calculate_Size(LockedSurface);
624 locked_surface_count++;
625
626 }
627 }
628}
629
630#endif //WW3D_DX8
#define NULL
Definition BaseType.h:92
#define WWASSERT
#define MIN(a, b)
Definition always.h:189
#define W3DNEW
Definition always.h:109
#define MAX(a, b)
Definition always.h:185
@ false
Definition bool.h:59
int _cdecl Format(const TCHAR *format,...)
Definition wwstring.cpp:273
static WW3DAssetManager * Get_Instance(void)
Definition assetmgr.h:205
static unsigned int Get_Sync_Time(void)
Definition ww3d.h:172
char * nstrdup(const char *str)
Definition nstrdup.cpp:56
#define WWASSERT_PRINT(expr, string)
Definition wwdebug.h:135
#define WWDEBUG_SAY(x)
Definition wwdebug.h:114