1 /*
2
3 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 and the "Aleph One" developers.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 This license is contained in the file "COPYING",
17 which is included with this source code; it is available online at
18 http://www.gnu.org/licenses/gpl.html
19
20 OpenGL Texture Manager,
21 by Loren Petrich,
22 March 12, 2000
23
24 This implements texture handling for OpenGL.
25
26 May 2, 2000:
27
28 Fixed silhouette-texture bug: color 0 is now transparent
29
30 May 24, 2000:
31
32 Added support for setting landscape aspect ratios from outside;
33 also added more graceful degradation for mis-sized textures.
34 Walls must be a power of 2 horizontally and vertical;
35 landscapes must be a power of 2 horizontally
36 in order for the tiling to work properly.
37
38 June 11, 2000:
39
40 Added support for opacity shift factor (OpacityShift alongside OpacityScale);
41 should be good for making dark colors somewhat opaque.
42
43 Jul 10, 2000:
44
45 Fixed crashing bug when OpenGL is inactive with ResetTextures()
46
47 Sep 9, 2000:
48
49 Restored old fix for AppleGL texturing as an option; this fix consists of setting
50 the minimum size of a texture to be 128.
51
52 Nov 12, 2000 (Loren Petrich):
53 Cleaned up some of the code to avoid explicit endianness usage;
54 also implemented texture substitution.
55
56 Nov 18, 2000 (Loren Petrich):
57 Added support for landscape vertical repeats;
58 also added support for glow mapping of wall textures
59
60 Dec 16, 2000 (Loren Petrich):
61 Fixed substitution of landscape textures
62
63 June 14, 2001 (Loren Petrich):
64 Changed Width*Height to TxtrWidth*TxtrHeight in some places to ensure that some operations
65 are done over complete textures
66
67 Nov 30, 2001 (Alexander Strange):
68 Added Ian Rickard's texture purging to save VRAM.
69
70 Jan 25, 2002 (Br'fin (Jeremy Parsons)):
71 Added TARGET_API_MAC_CARBON for AGL.h
72
73 May 3, 2003 (Br'fin (Jeremy Parsons))
74 Added LowLevelShape workaround for passing LowLevelShape info of sprites
75 instead of abusing/overflowing shape_descriptors
76 */
77
78 #include <string.h>
79 #include <stdlib.h>
80 #include <stdarg.h>
81 #include <math.h>
82 #include <list>
83
84 #include "cseries.h"
85
86 #ifdef HAVE_OPENGL
87
88 #include "OGL_Headers.h"
89
90 #include "preferences.h"
91
92 #include "SDL.h"
93 #include "SDL_endian.h"
94 #include "interface.h"
95 #include "render.h"
96 #include "map.h"
97 #include "collection_definition.h"
98 #include "OGL_Blitter.h"
99 #include "OGL_Setup.h"
100 #include "OGL_Render.h"
101 #include "OGL_Textures.h"
102 #include "screen.h"
103
104 using std::min;
105 using std::max;
106
107 OGL_TexturesStats gGLTxStats = {0,0,0,500000,0,0, 0};
108
109 // Texture mapping
110 struct TxtrTypeInfoData
111 {
112 GLenum NearFilter; // OpenGL parameter for near filter (GL_NEAREST, etc.)
113 GLenum FarFilter; // OpenGL parameter for far filter (GL_NEAREST, etc.)
114 int Resolution; // 0 is full-sized, 1 is half-sized, 2 is fourth-sized
115 GLenum ColorFormat; // OpenGL parameter for stored color format (RGBA8, etc.)
116 };
117
118
119 static TxtrTypeInfoData TxtrTypeInfoList[OGL_NUMBER_OF_TEXTURE_TYPES];
120 static TxtrTypeInfoData ModelSkinInfo;
121
122 static bool useSGISMipmaps = false;
123 static bool useMirroredRepeat = false;
124
125 // Infravision: use algorithm (red + green + blue)/3 to compose intensity,
126 // then shade with these colors, one color for each collection.
127
128 struct InfravisionData
129 {
130 GLfloat Red, Green, Blue; // Infravision tint components: 0 to 1
131 bool IsTinted; // whether to use infravision with this collection
132 };
133
134 struct InfravisionData IVDataList[NUMBER_OF_COLLECTIONS] =
135 {
136 {1,1,1,false},
137 {1,1,1,false},
138 {1,1,1,false},
139 {1,1,1,false},
140 {1,1,1,false},
141 {1,1,1,false},
142 {1,1,1,false},
143 {1,1,1,false},
144 {1,1,1,false},
145 {1,1,1,false},
146 {1,1,1,false},
147 {1,1,1,false},
148 {1,1,1,false},
149 {1,1,1,false},
150 {1,1,1,false},
151 {1,1,1,false},
152 {1,1,1,false},
153 {1,1,1,false},
154 {1,1,1,false},
155 {1,1,1,false},
156 {1,1,1,false},
157 {1,1,1,false},
158 {1,1,1,false},
159 {1,1,1,false},
160 {1,1,1,false},
161 {1,1,1,false},
162 {1,1,1,false},
163 {1,1,1,false},
164 {1,1,1,false},
165 {1,1,1,false},
166 {1,1,1,false},
167 {1,1,1,false}
168 };
169
170 // Is infravision currently active?
171 static bool InfravisionActive = false;
172
173 static std::list<TextureState*> sgActiveTextureStates;
174
175
176 // Allocate some textures and indicate whether an allocation had happened.
Allocate(short txType)177 bool TextureState::Allocate(short txType)
178 {
179 TextureType = txType;
180 if (!IsUsed)
181 {
182 sgActiveTextureStates.push_front(this);
183 gGLTxStats.inUse++;
184 glGenTextures(NUMBER_OF_TEXTURES,IDs);
185 IsUsed = true;
186 unusedFrames=0;
187 return true;
188 }
189 return false;
190 }
191
192 // Use a texture and indicate whether to load it
Use(int Which)193 bool TextureState::Use(int Which)
194 {
195 glBindTexture(GL_TEXTURE_2D,IDs[Which]);
196 bool result = !TexGened[Which];
197 TexGened[Which] = true;
198 IDUsage[Which]++;
199 return result;
200 }
201
202
203 // Resets the object's texture state
Reset()204 void TextureState::Reset()
205 {
206 if (IsUsed)
207 {
208 sgActiveTextureStates.remove(this);
209 gGLTxStats.inUse--;
210 glDeleteTextures(NUMBER_OF_TEXTURES,IDs);
211 }
212 IsUsed = IsGlowing = IsBumped = TexGened[Normal] = TexGened[Glowing] = TexGened[Bump] = false;
213 IDUsage[Normal] = IDUsage[Glowing] = IDUsage[Bump] = unusedFrames = 0;
214 }
215
FrameTick()216 void TextureState::FrameTick() {
217 if (!IsUsed) return;
218
219 gGLTxStats.totalAge += (TextureType!=OGL_Txtr_Landscape)?unusedFrames:0;
220
221 bool used = false;
222
223 for (int i=0 ; i<NUMBER_OF_TEXTURES ; i++)
224 if (IDUsage[i] != 0) used = true;
225
226 if (used) {
227 IDUsage[Normal] = IDUsage[Glowing] = unusedFrames = 0;
228
229 } else {
230 unusedFrames++;
231 assert(TextureType != NONE);
232 switch (TextureType) {
233 case OGL_Txtr_Wall:
234 if (unusedFrames > 300) Reset(); // at least 10 seconds till wall textures are released
235 break;
236 case OGL_Txtr_Landscape:
237 // never release landscapes
238 break;
239 case OGL_Txtr_Inhabitant:
240 if (unusedFrames > 450) Reset(); // release unused sprites in 15 seconds
241 break;
242 case OGL_Txtr_WeaponsInHand:
243 if (unusedFrames > 600) Reset(); // release weapons in hand in 20 seconds
244 break;
245 }
246 }
247 }
248
249 // Will distinguish by texture type as well as by collection;
250 // this is because different rendering modes deserve different treatment.
251 static CollBitmapTextureState* TextureStateSets[OGL_NUMBER_OF_TEXTURE_TYPES][MAXIMUM_COLLECTIONS];
252
253 static GLuint flatBumpTextureID = 0;
FlatBumpTexture()254 void FlatBumpTexture() {
255
256 if (flatBumpTextureID == 0)
257 {
258 glGenTextures(1, &flatBumpTextureID);
259 glBindTexture(GL_TEXTURE_2D, flatBumpTextureID);
260
261 GLubyte flatTextureData[4] = {0x80, 0x80, 0xFF, 0x80};
262
263 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
264 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
265 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
266 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
267 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
268 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, flatTextureData);
269 }
270 else
271 glBindTexture(GL_TEXTURE_2D, flatBumpTextureID);
272 }
273
274
275 // Initialize the texture accounting
OGL_StartTextures()276 void OGL_StartTextures()
277 {
278 // Initialize the texture accounting proper
279 for (int it=0; it<OGL_NUMBER_OF_TEXTURE_TYPES; it++)
280 for (int ic=0; ic<MAXIMUM_COLLECTIONS; ic++)
281 {
282 bool CollectionPresent = is_collection_present(ic);
283 short NumberOfBitmaps =
284 CollectionPresent ? get_number_of_collection_bitmaps(ic) : 0;
285 TextureStateSets[it][ic] =
286 (CollectionPresent && NumberOfBitmaps) ?
287 (new CollBitmapTextureState[NumberOfBitmaps]) : 0;
288 }
289
290 // Initialize the texture-type info
291 const int NUMBER_OF_NEAR_FILTERS = 2;
292 const GLenum NearFilterList[NUMBER_OF_NEAR_FILTERS] =
293 {
294 GL_NEAREST,
295 GL_LINEAR
296 };
297 const int NUMBER_OF_FAR_FILTERS = 6;
298 const GLenum FarFilterList[NUMBER_OF_FAR_FILTERS] =
299 {
300 GL_NEAREST,
301 GL_LINEAR,
302 GL_NEAREST_MIPMAP_NEAREST,
303 GL_LINEAR_MIPMAP_NEAREST,
304 GL_NEAREST_MIPMAP_LINEAR,
305 GL_LINEAR_MIPMAP_LINEAR
306 };
307 const int NUMBER_OF_COLOR_FORMATS = 3;
308 const GLenum ColorFormatList[NUMBER_OF_COLOR_FORMATS] =
309 {
310 GL_RGBA8,
311 GL_RGBA4,
312 GL_RGBA2
313 };
314
315 OGL_ConfigureData& ConfigureData = Get_OGL_ConfigureData();
316
317 for (int k=0; k<OGL_NUMBER_OF_TEXTURE_TYPES; k++)
318 {
319 OGL_Texture_Configure& TxtrConfigure = ConfigureData.TxtrConfigList[k];
320 TxtrTypeInfoData& TxtrTypeInfo = TxtrTypeInfoList[k];
321
322 short NearFilter = TxtrConfigure.NearFilter;
323 if (NearFilter < NUMBER_OF_NEAR_FILTERS)
324 TxtrTypeInfo.NearFilter = NearFilterList[NearFilter];
325 else
326 TxtrTypeInfo.NearFilter = GL_NEAREST;
327
328 short FarFilter = TxtrConfigure.FarFilter;
329 if (FarFilter < NUMBER_OF_FAR_FILTERS)
330 TxtrTypeInfo.FarFilter = FarFilterList[FarFilter];
331 else
332 TxtrTypeInfo.FarFilter = GL_NEAREST;
333
334 TxtrTypeInfo.Resolution = TxtrConfigure.Resolution;
335
336 short ColorFormat = TxtrConfigure.ColorFormat;
337 if (ColorFormat < NUMBER_OF_COLOR_FORMATS)
338 TxtrTypeInfo.ColorFormat = ColorFormatList[ColorFormat];
339 else
340 TxtrTypeInfo.ColorFormat = GL_RGBA8;
341 }
342
343 // Model skin
344 {
345 OGL_Texture_Configure& TxtrConfigure = ConfigureData.ModelConfig;
346 TxtrTypeInfoData& TxtrTypeInfo = ModelSkinInfo;
347
348 short NearFilter = TxtrConfigure.NearFilter;
349 if (NearFilter < NUMBER_OF_NEAR_FILTERS)
350 TxtrTypeInfo.NearFilter = NearFilterList[NearFilter];
351 else
352 TxtrTypeInfo.NearFilter = GL_NEAREST;
353
354 short FarFilter = TxtrConfigure.FarFilter;
355 if (FarFilter < NUMBER_OF_FAR_FILTERS)
356 TxtrTypeInfo.FarFilter = FarFilterList[FarFilter];
357 else
358 TxtrTypeInfo.FarFilter = GL_NEAREST;
359
360 TxtrTypeInfo.Resolution = TxtrConfigure.Resolution;
361
362 short ColorFormat = TxtrConfigure.ColorFormat;
363 if (ColorFormat < NUMBER_OF_COLOR_FORMATS)
364 TxtrTypeInfo.ColorFormat = ColorFormatList[ColorFormat];
365 else
366 TxtrTypeInfo.ColorFormat = GL_RGBA8;
367 }
368
369 #if defined GL_SGIS_generate_mipmap
370 useSGISMipmaps = OGL_CheckExtension("GL_SGIS_generate_mipmap");
371 #endif
372 #if defined GL_ARB_texture_mirrored_repeat
373 useMirroredRepeat = OGL_CheckExtension("GL_ARB_texture_mirrored_repeat");
374 #endif
375 }
376
377
378 // Done with the texture accounting
OGL_StopTextures()379 void OGL_StopTextures()
380 {
381 // Clear the texture accounting
382 for (int it=0; it<OGL_NUMBER_OF_TEXTURE_TYPES; it++)
383 for (int ic=0; ic<MAXIMUM_COLLECTIONS; ic++)
384 if (TextureStateSets[it][ic]) delete []TextureStateSets[it][ic];
385
386 // clear blitters and fonts
387 OGL_Blitter::StopTextures();
388 FontSpecifier::OGL_ResetFonts(false);
389
390 glDeleteTextures(1, &flatBumpTextureID);
391 flatBumpTextureID = 0;
392
393 // clear leftover infravision
394 InfravisionActive = false;
395 }
396
OGL_FrameTickTextures()397 void OGL_FrameTickTextures()
398 {
399 std::list<TextureState*>::iterator i;
400
401 for (i=sgActiveTextureStates.begin() ; i!= sgActiveTextureStates.end() ; i++) {
402 (*i)->FrameTick();
403 }
404 }
405
406 // Find an OpenGL-friendly color table from a Marathon shading table
FindOGLColorTable(int NumSrcBytes,byte * OrigColorTable,uint32 * ColorTable)407 static void FindOGLColorTable(int NumSrcBytes, byte *OrigColorTable, uint32 *ColorTable)
408 {
409 // Stretch the original color table to 4 bytes per value for OpenGL convenience;
410 // all the intermediate calculations will be done in RGBA 8888 form,
411 // because that is what OpenGL prefers as a texture input
412 switch(NumSrcBytes) {
413 case 2:
414 for (int k=0; k<MAXIMUM_SHADING_TABLE_INDEXES; k++)
415 {
416 byte *OrigPtr = OrigColorTable + NumSrcBytes*k;
417 uint32 &Color = ColorTable[k];
418
419 // Convert from ARGB 5551 to RGBA 8888; make opaque
420 uint16 Intmd;
421 uint8 *IntmdPtr = (uint8 *)(&Intmd);
422 IntmdPtr[0] = OrigPtr[0];
423 IntmdPtr[1] = OrigPtr[1];
424 Color = Convert_16to32(Intmd);
425 }
426 break;
427
428 case 4:
429 for (int k=0; k<MAXIMUM_SHADING_TABLE_INDEXES; k++)
430 {
431 byte *OrigPtr = OrigColorTable + NumSrcBytes*k;
432 uint32 &Color = ColorTable[k];
433
434 // Convert from ARGB 8888 to RGBA 8888; make opaque
435 uint8 *ColorPtr = (uint8 *)(&Color);
436 if (PlatformIsLittleEndian()) {
437 // the compiler will do the right thing and only emit
438 // code for the correct path. In C++17 we can do constexpr if
439 // to make that requirement explicit.
440 ColorPtr[0] = OrigPtr[2];
441 ColorPtr[1] = OrigPtr[1];
442 ColorPtr[2] = OrigPtr[0];
443 ColorPtr[3] = 0xff;
444 } else {
445 ColorPtr[0] = OrigPtr[1];
446 ColorPtr[1] = OrigPtr[2];
447 ColorPtr[2] = OrigPtr[3];
448 ColorPtr[3] = 0xff;
449 }
450 }
451 break;
452 }
453 }
454
455
IsLandscapeFlatColored()456 inline bool IsLandscapeFlatColored()
457 {
458 OGL_ConfigureData& ConfigureData = Get_OGL_ConfigureData();
459 return TEST_FLAG(ConfigureData.Flags,OGL_Flag_FlatLand);
460 }
461
462
463 // Modify color-table index if necessary;
464 // makes it the infravision or silhouette one if necessary
ModifyCLUT(short TransferMode,short CLUT)465 short ModifyCLUT(short TransferMode, short CLUT)
466 {
467 short CTable;
468
469 // Tinted mode is only used for invisibility, and infravision will make objects visible
470 if (TransferMode == _static_transfer) CTable = SILHOUETTE_BITMAP_CLUTSPECIFIC + CLUT;
471 else if (TransferMode == _tinted_transfer) CTable = SILHOUETTE_BITMAP_CLUTSPECIFIC + CLUT;
472 else if (InfravisionActive) CTable = INFRAVISION_BITMAP_CLUTSPECIFIC + CLUT;
473 else CTable = CLUT;
474
475 return CTable;
476 }
477
478 /*
479 Routine for using some texture; it will load the texture if necessary.
480 It parses a shape descriptor and checks on whether the collection's texture type
481 is one of those given.
482 It will check for more than one intended texture type,
483 a convenience for multiple texture types sharing the same handling.
484
485 It uses the transfer mode and the transfer data to work out
486 what transfer modes to use (invisibility is a special case of tinted)
487 */
Setup()488 bool TextureManager::Setup()
489 {
490
491 // Parse the shape descriptor and check on whether the texture type
492 // is the texture's intended type
493 short CollColor = GET_DESCRIPTOR_COLLECTION(ShapeDesc);
494 Collection = GET_COLLECTION(CollColor);
495 CTable = ModifyCLUT(TransferMode,GET_COLLECTION_CLUT(CollColor));
496 Frame = (LowLevelShape)? LowLevelShape : GET_DESCRIPTOR_SHAPE(ShapeDesc);
497 Bitmap = get_bitmap_index(Collection,Frame);
498 if (Bitmap == NONE) return false;
499
500 // Get the texture-state info: first, per-collection, then per-bitmap
501 CollBitmapTextureState *CBTSList = TextureStateSets[TextureType][Collection];
502 if (CBTSList == NULL) return false;
503 CollBitmapTextureState& CBTS = CBTSList[Bitmap];
504
505 // Get the control info for this texture type:
506 TxtrTypeInfoData& TxtrTypeInfo = TxtrTypeInfoList[TextureType];
507
508 // Get the rendering options for this texture:
509 TxtrOptsPtr = OGL_GetTextureOptions(Collection,CTable,Bitmap);
510
511 // Get the texture-state info: per-color-table -- be sure to preserve this for later
512 // Set the texture ID, and load the texture if necessary
513 // If "Use()" is true, then load, otherwise, assume the texture is loaded and skip
514 TxtrStatePtr = &CBTS.CTStates[CTable];
515 TextureState &CTState = *TxtrStatePtr;
516 if (!CTState.IsUsed)
517 {
518 // Initial sprite scale/offset
519 U_Scale = V_Scale = 1;
520 U_Offset = V_Offset = 0;
521
522 // Try to load a substitute texture, and if that fails,
523 // get the geometry from the shapes bitmap.
524 bool substitute = LoadSubstituteTexture();
525 if (!substitute)
526 if (!SetupTextureGeometry()) return false;
527
528 // Store sprite scale/offset
529 CTState.U_Scale = U_Scale;
530 CTState.V_Scale = V_Scale;
531 CTState.U_Offset = U_Offset;
532 CTState.V_Offset = V_Offset;
533
534 // This finding of color tables sets the glow state
535 if (!substitute)
536 FindColorTables();
537 else if (GlowImage.get() && GlowImage.get()->IsPresent()) {
538 // Override if textures had been substituted;
539 // if the normal texture had been substituted, it will be assumed to be
540 // non-glowing unless the glow texture has also been substituted.
541
542 IsGlowing = true;
543 } else {
544 IsGlowing = false;
545 }
546
547 CTState.IsGlowing = IsGlowing;
548
549 if (substitute && OffsetImage.get() && OffsetImage.get()->IsPresent()) {
550 CTState.IsBumped = true;
551 } else {
552 CTState.IsBumped = false;
553 }
554
555 // Load the fake landscape if selected
556 if (TextureType == OGL_Txtr_Landscape)
557 {
558 if (IsLandscapeFlatColored())
559 {
560 NormalImage.edit(new ImageDescriptor(TxtrWidth, TxtrHeight, GetFakeLandscape()));
561 }
562 }
563
564 // If not, then load the expected textures
565 if (!NormalImage.get() || !NormalImage.get()->IsPresent())
566 NormalImage.edit(new ImageDescriptor(TxtrWidth, TxtrHeight, GetOGLTexture(NormalColorTable)));
567 if (IsGlowing && (!GlowImage.get() || !GlowImage.get()->IsPresent()))
568 GlowImage.edit(new ImageDescriptor(TxtrWidth, TxtrHeight, GetOGLTexture(GlowColorTable)));
569
570 // Display size: may be shrunk
571 int MaxWidth = MAX(TxtrWidth >> TxtrTypeInfo.Resolution, 1);
572 int MaxHeight = MAX(TxtrHeight >> TxtrTypeInfo.Resolution, 1);
573
574 // Fit the image into the maximum size allowed by the OpenGL implementation in use
575 GLint MaxTextureSize;
576 glGetIntegerv(GL_MAX_TEXTURE_SIZE,&MaxTextureSize);
577 while (MaxWidth > MaxTextureSize || MaxHeight > MaxTextureSize)
578 {
579 LoadedWidth >>= 1;
580 LoadedHeight >>= 1;
581 }
582
583 while (NormalImage.get()->GetWidth() > MaxWidth || NormalImage.get()->GetHeight() > MaxHeight)
584 {
585 if (!NormalImage.edit()->Minify()) break;
586 if (GlowImage.get() && GlowImage.get()->IsPresent()) {
587 if (!GlowImage.edit()->Minify()) break;
588 }
589 if (OffsetImage.get() && OffsetImage.get()->IsPresent()) {
590 if (!OffsetImage.edit()->Minify()) break;
591 }
592 }
593
594 // Kludge for making top and bottom look flat
595 /*
596 if (TextureType == OGL_Txtr_Landscape)
597 {
598 MakeAverage(LoadedWidth,NormalBuffer);
599 MakeAverage(LoadedWidth,NormalBuffer+LoadedWidth*(LoadedHeight-1));
600 }
601 */
602 }
603 else
604 {
605 // Get sprite scale/offset
606 U_Scale = CTState.U_Scale;
607 V_Scale = CTState.V_Scale;
608 U_Offset = CTState.U_Offset;
609 V_Offset = CTState.V_Offset;
610
611 // Get glow state
612 IsGlowing = CTState.IsGlowing;
613
614 // Populate NormalImage, GlowImage, OffsetImage
615 NormalImage.set(&TxtrOptsPtr->NormalImg);
616 GlowImage.set(&TxtrOptsPtr->GlowImg);
617 OffsetImage.set(&TxtrOptsPtr->OffsetImg);
618 }
619
620 // Done!!!
621 return true;
622 }
623
624
WhetherTextureFix()625 inline bool WhetherTextureFix()
626 {
627 OGL_ConfigureData& ConfigureData = Get_OGL_ConfigureData();
628 return TEST_FLAG(ConfigureData.Flags,OGL_Flag_TextureFix);
629 }
630
631
632 // Conversion of color data types
633
MakeEightBit(GLfloat Chan)634 inline int MakeEightBit(GLfloat Chan)
635 {
636 return int(PIN(int(255*Chan+0.5),0,255));
637 }
638
MakeIntColor(GLfloat * FloatColor)639 uint32 MakeIntColor(GLfloat *FloatColor)
640 {
641 uint32 IntColor;
642 uint8 *ColorPtr = (uint8 *)(&IntColor);
643 for (int k=0; k<4; k++)
644 ColorPtr[k] = MakeEightBit(FloatColor[k]);
645 return IntColor;
646 }
647
MakeFloatColor(uint32 IntColor,GLfloat * FloatColor)648 void MakeFloatColor(uint32 IntColor, GLfloat *FloatColor)
649 {
650 uint8 *ColorPtr = (uint8 *)(&IntColor);
651 for (int k=0; k<4; k++)
652 FloatColor[k] = float(ColorPtr[k])/float(255);
653 }
654
LoadSubstituteTexture()655 bool TextureManager::LoadSubstituteTexture()
656 {
657 // don't load replacements for bitmaps that have been patched
658 if (Texture->flags & _PATCHED_BIT) return false;
659
660 // Is there a texture to be substituted?
661 ImageDescriptor& NormalImg = TxtrOptsPtr->NormalImg;
662 if (!NormalImg.IsPresent()) return false;
663
664 // Idiot-proofing
665 if (NormalBuffer)
666 {
667 delete []NormalBuffer;
668 NormalBuffer = NULL;
669 }
670 if (GlowBuffer)
671 {
672 delete []GlowBuffer;
673 GlowBuffer = NULL;
674 }
675
676 NormalImage.set(&TxtrOptsPtr->NormalImg);
677 GlowImage.set(&TxtrOptsPtr->GlowImg);
678 OffsetImage.set(&TxtrOptsPtr->OffsetImg);
679
680 int Width = NormalImg.GetWidth();
681 int Height = NormalImg.GetHeight();
682
683 switch(TextureType)
684 {
685 case OGL_Txtr_Wall:
686 // For tiling to be possible, the width and height must be powers of 2;
687 // also, be sure to transpose the texture
688 TxtrHeight = Height;
689 TxtrWidth = Width;
690 if (!npotTextures)
691 {
692 if (TxtrWidth != NextPowerOfTwo(TxtrWidth)) return false;
693 if (TxtrHeight != NextPowerOfTwo(TxtrHeight)) return false;
694 }
695 TxtrOptsPtr->Substitution = true;
696 break;
697
698 case OGL_Txtr_Landscape:
699 // For tiling to be possible, the width must be a power of 2;
700 // the height need not be such a power.
701 TxtrWidth = Width;
702 TxtrHeight = (Landscape_AspRatExp >= 0) ?
703 (TxtrWidth >> Landscape_AspRatExp) :
704 (TxtrWidth << (-Landscape_AspRatExp));
705 if (!npotTextures && TxtrWidth != NextPowerOfTwo(TxtrWidth)) return false;
706
707 // the renderer doesn't use these,
708 // so I'll use them to get the texture matrix set up right
709 U_Scale = (float) TxtrHeight / NormalImg.GetHeight();
710 U_Offset = -1.0 + ((TxtrHeight - NormalImg.GetHeight()) / 2.0 / TxtrHeight) + (1.0 - NormalImg.GetUScale()) / 2.0;
711
712 TxtrOptsPtr->Substitution = true;
713
714 GlowImage.set((ImageDescriptor *) NULL);
715 break;
716
717 case OGL_Txtr_Inhabitant:
718 case OGL_Txtr_WeaponsInHand:
719 // Much of the code here has been copied from elsewhere.
720 // Set these for convenience; sprites are transposed, as walls are.
721 TxtrHeight = Height;
722 TxtrWidth = Width;
723
724 if (!npotTextures)
725 {
726 // ImageLoader now stores these as powers of two sized
727 if (TxtrWidth != NextPowerOfTwo(TxtrWidth)) return false;
728 if (TxtrHeight != NextPowerOfTwo(TxtrHeight)) return false;
729 }
730
731 // We can calculate the scales and offsets here
732 V_Scale = NormalImg.GetVScale();
733 V_Offset = 0;
734 U_Scale = NormalImg.GetUScale();
735 U_Offset = 0;
736
737 TxtrOptsPtr->Substitution = true;
738 break;
739 }
740
741 // Use the Tomb Raider opacity hack if selected
742 SetPixelOpacities(*TxtrOptsPtr, NormalImage);
743
744 // Modify if infravision is active
745 if (IsInfravisionTable(CTable))
746 {
747 FindInfravisionVersion(Collection, NormalImage);
748
749 // Infravision textures don't glow
750 GlowImage.set((ImageDescriptor *) NULL);
751
752 // FIXME: bump maps don't load properly under infravision
753 OffsetImage.set((ImageDescriptor *) NULL);
754 }
755 else if (IsSilhouetteTable(CTable))
756 {
757 FindSilhouetteVersion(NormalImage);
758 GlowImage.set((ImageDescriptor *) NULL);
759 }
760 return true;
761 }
762
SetupTextureGeometry()763 bool TextureManager::SetupTextureGeometry()
764 {
765 // How many rows (scanlines) and columns
766 if (Texture->flags&_COLUMN_ORDER_BIT)
767 {
768 BaseTxtrWidth = Texture->height;
769 BaseTxtrHeight = Texture->width;
770 }
771 else
772 {
773 BaseTxtrWidth = Texture->width;
774 BaseTxtrHeight = Texture->height;
775 }
776
777 short RowBytes = Texture->bytes_per_row;
778 if (RowBytes != NONE)
779 if (BaseTxtrWidth != RowBytes) return false;
780
781 // The default
782 WidthOffset = HeightOffset = 0;
783
784 switch(TextureType)
785 {
786 case OGL_Txtr_Wall:
787 // For tiling to be possible, the width and height must be powers of 2
788 // Match M1 engine, and truncate larger textures to 128px square
789 TxtrWidth = std::min(static_cast<int>(BaseTxtrWidth), 128);
790 TxtrHeight = std::min(static_cast<int>(BaseTxtrHeight), 128);
791 if (!npotTextures)
792 {
793 if (TxtrWidth != NextPowerOfTwo(TxtrWidth)) return false;
794 if (TxtrHeight != NextPowerOfTwo(TxtrHeight)) return false;
795 }
796 break;
797
798 case OGL_Txtr_Landscape:
799 if (IsLandscapeFlatColored())
800 {
801 TxtrWidth = 128;
802 TxtrHeight = 128;
803 }
804 else
805 {
806 // Width is horizontal direction here
807 TxtrWidth = BaseTxtrWidth;
808 if (!npotTextures && TxtrWidth != NextPowerOfTwo(TxtrWidth))
809 return false;
810
811 if (npotTextures)
812 {
813 // Use the landscape height here
814 TxtrHeight = (Landscape_AspRatExp >= 0) ?
815 (TxtrWidth >> Landscape_AspRatExp) :
816 (TxtrWidth << (-Landscape_AspRatExp));
817 U_Scale = (double) TxtrHeight / BaseTxtrHeight;
818 U_Offset = -(TxtrHeight - BaseTxtrHeight) / 2.0 / TxtrHeight;
819 TxtrHeight = BaseTxtrHeight;
820 }
821 else
822 {
823 // Use the landscape height here
824 TxtrHeight = (Landscape_AspRatExp >= 0) ?
825 (TxtrWidth >> Landscape_AspRatExp) :
826 (TxtrWidth << (-Landscape_AspRatExp));
827
828 // Offsets
829 WidthOffset = (TxtrWidth - BaseTxtrWidth) >> 1;
830 HeightOffset = (TxtrHeight - BaseTxtrHeight) >> 1;
831 }
832 }
833
834 break;
835
836 case OGL_Txtr_Inhabitant:
837 case OGL_Txtr_WeaponsInHand:
838 {
839 if (npotTextures)
840 {
841 TxtrWidth = BaseTxtrWidth;
842 TxtrHeight = BaseTxtrHeight;
843 }
844 else
845 {
846 // The 2 here is so that there will be an empty border around a sprite,
847 // so that the texture can be conveniently mipmapped.
848 TxtrWidth = NextPowerOfTwo(BaseTxtrWidth+2);
849 TxtrHeight = NextPowerOfTwo(BaseTxtrHeight+2);
850
851 // This kludge no longer necessary
852 // Restored due to some people still having AppleGL 1.1.2
853 if (WhetherTextureFix())
854 {
855 TxtrWidth = MAX(TxtrWidth,128);
856 TxtrHeight = MAX(TxtrHeight,128);
857 }
858
859 // Offsets
860 WidthOffset = (TxtrWidth - BaseTxtrWidth) >> 1;
861 HeightOffset = (TxtrHeight - BaseTxtrHeight) >> 1;
862
863 // We can calculate the scales and offsets here
864 double TWidRecip = 1/double(TxtrWidth);
865 double THtRecip = 1/double(TxtrHeight);
866 U_Scale = TWidRecip*double(BaseTxtrWidth);
867 U_Offset = TWidRecip*WidthOffset;
868 V_Scale = THtRecip*double(BaseTxtrHeight);
869 V_Offset = THtRecip*HeightOffset;
870 }
871 }
872 break;
873 }
874
875 // Success!
876 return true;
877 }
878
879
FindColorTables()880 void TextureManager::FindColorTables()
881 {
882 // Default
883 IsGlowing = false;
884
885 // The silhouette case is easy
886 if (IsSilhouetteTable(CTable))
887 {
888 NormalColorTable[0] = 0;
889 for (int k=1; k<MAXIMUM_SHADING_TABLE_INDEXES; k++)
890 NormalColorTable[k] = 0xffffffff;
891 return;
892 }
893
894 // Interface collection? Then use the CLUT directly
895 if (Collection == 0) {
896 int num_colors;
897 struct rgb_color_value *q = get_collection_colors(0, 0, num_colors);
898 uint8 *p = (uint8 *)NormalColorTable;
899 for (int k=0; k<num_colors; k++) {
900 int idx = q[k].value;
901 p[idx * 4 + 0] = q[k].red >> 8;
902 p[idx * 4 + 1] = q[k].green >> 8;
903 p[idx * 4 + 2] = q[k].blue >> 8;
904 p[idx * 4 + 3] = 0xff;
905 }
906 SetPixelOpacitiesRGBA(*TxtrOptsPtr, MAXIMUM_SHADING_TABLE_INDEXES, NormalColorTable);
907 NormalColorTable[0] = 0;
908 return;
909 }
910
911 // Number of source bytes, for reading off of the shading table
912 // IR change: dithering
913 short NumSrcBytes = bit_depth / 8;
914
915 // Shadeless polygons use the first, instead of the last, shading table
916 byte *OrigColorTable = (byte *)ShadingTables;
917 byte *OrigGlowColorTable = OrigColorTable;
918 if (!IsShadeless) OrigColorTable +=
919 NumSrcBytes*(number_of_shading_tables - 1)*MAXIMUM_SHADING_TABLE_INDEXES;
920
921 // Find the normal color table,
922 // and set its opacities as if there was no glow table.
923 FindOGLColorTable(NumSrcBytes,OrigColorTable,NormalColorTable);
924 SetPixelOpacitiesRGBA(*TxtrOptsPtr,MAXIMUM_SHADING_TABLE_INDEXES,NormalColorTable);
925
926 // Find the glow-map color table;
927 // only inhabitants are glowmapped.
928 // Also, it seems that only infravision textures are shadeless.
929 if (!IsShadeless && (TextureType != OGL_Txtr_Landscape))
930 {
931 // Find the glow table from the lowest-illumination color table
932 FindOGLColorTable(NumSrcBytes,OrigGlowColorTable,GlowColorTable);
933
934 // Search for self-luminous colors; ignore the first one as the transparent one
935 for (int k=1; k<MAXIMUM_SHADING_TABLE_INDEXES; k++)
936 {
937 // Check for illumination-independent colors
938 uint8 *NormalEntry = (uint8 *)(NormalColorTable + k);
939 uint8 *GlowEntry = (uint8 *)(GlowColorTable + k);
940
941 bool EntryIsGlowing = false;
942 for (int q=0; q<3; q++)
943 if (GlowEntry[q] >= 0x0f) EntryIsGlowing = true;
944
945 // Make the glow color the original color, to get continuity
946 for (int q=0; q<3; q++)
947 GlowEntry[q] = NormalEntry[q];
948
949 if (EntryIsGlowing && NormalEntry[3])
950 {
951 IsGlowing = true;
952 // Make half-opaque, to get more like the software rendering
953 float Opacity = NormalEntry[3]/float(0xff);
954 NormalEntry[3] = MakeEightBit(Opacity/(2-Opacity));
955 GlowEntry[3] = MakeEightBit(Opacity/2);
956 }
957 else
958 {
959 // Make transparent, to get appropriate continuity
960 GlowEntry[3] = 0;
961 }
962 }
963 }
964
965 // The first color is always the transparent color,
966 // except if it is a landscape color
967 if (TextureType != OGL_Txtr_Landscape)
968 {NormalColorTable[0] = 0; GlowColorTable[0] = 0;}
969
970 // PremultiplyColorTables();
971 }
972
PremultiplyColorTables()973 void TextureManager::PremultiplyColorTables()
974 {
975 uint32 alphaMask = PlatformIsLittleEndian() ? 0xff000000 : 0x000000ff;
976
977 uint32 *tables[2];
978 tables[0] = NormalColorTable;
979 if (!IsShadeless && (TextureType != OGL_Txtr_Landscape))
980 tables[1] = GlowColorTable;
981 else
982 tables[1] = 0;
983
984 for (int table = 0; table < 2; table++)
985 {
986 if (!tables[table]) continue;
987 for (int k = 0; k < MAXIMUM_SHADING_TABLE_INDEXES; k++)
988 {
989 if ((tables[table][k] & alphaMask) == alphaMask)
990 continue;
991 if ((tables[table][k] & alphaMask) == 0) {
992 tables[table][k] = 0;
993 continue;
994 }
995
996 short r, g, b, a;
997 uint8 *PxlPtr = (uint8 *) &tables[table][k];
998
999 r = PxlPtr[0];
1000 g = PxlPtr[1];
1001 b = PxlPtr[2];
1002 a = PxlPtr[3];
1003
1004 r = (a * r + 127) / 255;
1005 g = (a * g + 127) / 255;
1006 b = (a * b + 127) / 255;
1007
1008 PxlPtr[0] = (uint8) r;
1009 PxlPtr[1] = (uint8) g;
1010 PxlPtr[2] = (uint8) b;
1011 }
1012 }
1013 }
1014
GetOGLTexture(uint32 * ColorTable)1015 uint32 *TextureManager::GetOGLTexture(uint32 *ColorTable)
1016 {
1017 // Allocate pixel buffer
1018 int NumPixels = int(TxtrWidth)*int(TxtrHeight);
1019 uint32 *Buffer = new uint32[NumPixels];
1020
1021 // Calculate the rows to move to the OpenGL buffer.
1022 short OGLHeightOffset, OGLHeightFinish, OrigHeightDiff;
1023 if (HeightOffset >= 0)
1024 {
1025 OGLHeightOffset = HeightOffset;
1026 OGLHeightFinish = std::min(static_cast<int>(TxtrHeight), HeightOffset + BaseTxtrHeight);
1027 OrigHeightDiff = -HeightOffset;
1028 }
1029 else
1030 {
1031 OGLHeightOffset = 0;
1032 OGLHeightFinish = TxtrHeight;
1033 OrigHeightDiff = -HeightOffset;
1034 }
1035
1036 // Calculate the pixels within each row to move.
1037 // If we have a constant row size, we can calculate the
1038 // offsets once for every row.
1039 short OrigWidthOffset, OGLWidthOffset, OGLWidthFinish;
1040 if (Texture->bytes_per_row != NONE)
1041 {
1042 if (WidthOffset >= 0)
1043 {
1044 OrigWidthOffset = 0;
1045 OGLWidthOffset = WidthOffset;
1046 OGLWidthFinish = std::min(static_cast<int>(TxtrWidth), WidthOffset + BaseTxtrWidth);
1047 }
1048 else
1049 {
1050 OrigWidthOffset = -WidthOffset;
1051 OGLWidthOffset = 0;
1052 OGLWidthFinish = TxtrWidth;
1053 }
1054 }
1055 else
1056 {
1057 // set later for each row
1058 OrigWidthOffset = 0;
1059 OGLWidthOffset = 0;
1060 OGLWidthFinish = 0;
1061 }
1062
1063 uint32 rgb_mask = PlatformIsLittleEndian() ? 0x00ffffff : 0xffffff00;
1064
1065 for (short h = OGLHeightOffset; h < OGLHeightFinish; h++)
1066 {
1067 byte *OrigStrip = Texture->row_addresses[h + OrigHeightDiff];
1068 uint32 *OGLStrip = &Buffer[TxtrWidth * h];
1069
1070 if (Texture->bytes_per_row == NONE)
1071 {
1072 // Determine the offsets for this row
1073 // This is the Marathon 2 sprite-interpretation scheme;
1074 // assumes big-endian data
1075
1076 // First destination location
1077 uint16 First = uint16(*(OrigStrip++)) << 8;
1078 First |= uint16(*(OrigStrip++));
1079 // Last destination location (last pixel is just before it)
1080 uint16 Last = uint16(*(OrigStrip++)) << 8;
1081 Last |= uint16(*(OrigStrip++));
1082
1083 if (WidthOffset + First >= 0)
1084 {
1085 OrigWidthOffset = 0;
1086 OGLWidthOffset = WidthOffset + First;
1087 }
1088 else
1089 {
1090 OrigWidthOffset = -(WidthOffset + First);
1091 OGLWidthOffset = 0;
1092 }
1093 OGLWidthFinish = std::min(static_cast<int>(TxtrWidth), WidthOffset + Last);
1094 }
1095 OrigStrip += OrigWidthOffset;
1096
1097 // smear first pixel to left edge
1098 for (short w = 0; w < OGLWidthOffset; w++)
1099 *(OGLStrip++) = ColorTable[*OrigStrip] & rgb_mask;
1100
1101 for (short w = OGLWidthOffset; w < OGLWidthFinish; w++)
1102 *(OGLStrip++) = ColorTable[*(OrigStrip++)];
1103
1104 // smear last pixel to right edge
1105 for (short w = OGLWidthFinish; w < TxtrWidth; w++)
1106 *(OGLStrip++) = ColorTable[*(OrigStrip - 1)] & rgb_mask;
1107 }
1108
1109 // smear first pixel row to top edge
1110 for (short h = 0; h < OGLHeightOffset; h++)
1111 {
1112 uint32 *SrcStrip;
1113 if (TextureType == OGL_Txtr_Landscape)
1114 {
1115 SrcStrip = &Buffer[TxtrWidth * (2 * OGLHeightOffset - h) - 1];
1116 }
1117 else
1118 {
1119 SrcStrip = &Buffer[TxtrWidth * OGLHeightOffset];
1120 }
1121 uint32 *OGLStrip = &Buffer[TxtrWidth * h];
1122
1123 for (short w = 0; w < TxtrWidth; w++)
1124 *(OGLStrip++) = *(SrcStrip++) & rgb_mask;
1125 }
1126 // smear last pixel row to bottom edge
1127 for (short h = OGLHeightFinish; h < TxtrHeight; h++)
1128 {
1129 uint32 *SrcStrip;
1130 if (TextureType == OGL_Txtr_Landscape)
1131 {
1132 SrcStrip = &Buffer[TxtrWidth * (2 * OGLHeightFinish - h - 1)];
1133 }
1134 else
1135 {
1136 SrcStrip = &Buffer[TxtrWidth * (OGLHeightFinish - 1)];
1137 }
1138 uint32 *OGLStrip = &Buffer[TxtrWidth * h];
1139
1140 for (short w = 0; w < TxtrWidth; w++)
1141 *(OGLStrip++) = *(SrcStrip++) & rgb_mask;
1142 }
1143
1144 return Buffer;
1145 }
1146
1147
GetFakeLandscape()1148 uint32 *TextureManager::GetFakeLandscape()
1149 {
1150 // Allocate and set to black and transparent
1151 int NumPixels = int(TxtrWidth)*int(TxtrHeight);
1152 uint32 *Buffer = new uint32[NumPixels];
1153 objlist_clear(Buffer,NumPixels);
1154
1155 // Set up land and sky colors;
1156 // be sure to idiot-proof out-of-range ones
1157 OGL_ConfigureData& ConfigureData = Get_OGL_ConfigureData();
1158 int LscpIndx = static_world->song_index;
1159 if (!LandscapesLoaded || (LscpIndx < 0 && LscpIndx >= 4))
1160 {
1161 memset(Buffer,0,NumPixels*sizeof(uint32));
1162 return Buffer;
1163 }
1164
1165 RGBColor OrigLandColor = ConfigureData.LscpColors[LscpIndx][0];
1166 RGBColor OrigSkyColor = ConfigureData.LscpColors[LscpIndx][1];
1167
1168 // Set up floating-point ones, complete with alpha channel
1169 GLfloat LandColor[4], SkyColor[4];
1170 MakeFloatColor(OrigLandColor,LandColor);
1171 LandColor[3] = 1;
1172 MakeFloatColor(OrigSkyColor,SkyColor);
1173 SkyColor[3] = 1;
1174
1175 // Modify if infravision is active
1176 if (IsInfravisionTable(CTable))
1177 {
1178 FindInfravisionVersionRGBA(Collection,LandColor);
1179 FindInfravisionVersionRGBA(Collection,SkyColor);
1180 }
1181
1182 uint32 TxtrLandColor = MakeIntColor(LandColor);
1183 uint32 TxtrSkyColor = MakeIntColor(SkyColor);
1184
1185 // Textures' vertical dimension is upward;
1186 // put in the land after the sky
1187 uint32 *BufPtr = Buffer;
1188 for (int h=0; h<TxtrHeight/2; h++)
1189 for (int w=0; w<TxtrWidth; w++)
1190 *(BufPtr++) = TxtrLandColor;
1191 for (int h=0; h<TxtrHeight/2; h++)
1192 for (int w=0; w<TxtrWidth; w++)
1193 *(BufPtr++) = TxtrSkyColor;
1194
1195 return Buffer;
1196 }
1197
1198
Shrink(uint32 * Buffer)1199 uint32 *TextureManager::Shrink(uint32 *Buffer)
1200 {
1201 int NumPixels = int(LoadedWidth)*int(LoadedHeight);
1202 GLuint *NewBuffer = new GLuint[NumPixels];
1203 gluScaleImage(GL_RGBA, TxtrWidth, TxtrHeight, GL_UNSIGNED_BYTE, Buffer,
1204 LoadedWidth, LoadedHeight, GL_UNSIGNED_BYTE, NewBuffer);
1205
1206 return (uint32 *)NewBuffer;
1207 }
1208
1209
1210 // This places a texture into the OpenGL software and gives it the right
1211 // mapping attributes
PlaceTexture(const ImageDescriptor * Image,bool normal_map)1212 void TextureManager::PlaceTexture(const ImageDescriptor *Image, bool normal_map)
1213 {
1214
1215 bool mipmapsLoaded = false;
1216
1217 TxtrTypeInfoData& TxtrTypeInfo = TxtrTypeInfoList[TextureType];
1218
1219 GLenum internalFormat = TxtrTypeInfo.ColorFormat;
1220 // some optimizations here:
1221 if (TextureType == 1) // landscape
1222 {
1223 if (internalFormat == GL_RGBA8)
1224 internalFormat = GL_RGB8;
1225 else if (internalFormat == GL_RGBA4)
1226 internalFormat = GL_RGB5;
1227 }
1228 else if (!IsBlended() && internalFormat == GL_RGBA4)
1229 {
1230 internalFormat = GL_RGB5_A1;
1231 }
1232
1233 bool load_as_sRGB = (Wanting_sRGB && !normal_map &&
1234 Collection != _collection_interface &&
1235 Collection != _collection_weapons_in_hand);
1236
1237 if(load_as_sRGB) {
1238 switch(internalFormat) {
1239 case GL_RGB:
1240 case GL_R3_G3_B2:
1241 case GL_RGB4:
1242 case GL_RGB5:
1243 case GL_RGB8:
1244 case GL_RGB10:
1245 case GL_RGB12:
1246 case GL_RGB16:
1247 internalFormat = GL_SRGB;
1248 break;
1249 case GL_RGBA:
1250 case GL_RGBA2:
1251 case GL_RGBA4:
1252 case GL_RGB5_A1:
1253 case GL_RGBA8:
1254 case GL_RGB10_A2:
1255 case GL_RGBA12:
1256 case GL_RGBA16:
1257 internalFormat = GL_SRGB_ALPHA;
1258 break;
1259 #if defined(GL_ARB_texture_compression) && defined(GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
1260 /* These might not do anything... */
1261 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1262 internalFormat = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
1263 break;
1264 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1265 internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
1266 break;
1267 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
1268 internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
1269 break;
1270 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
1271 internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
1272 break;
1273 #endif
1274 }
1275 }
1276
1277 if (Image->GetFormat() == ImageDescriptor::RGBA8) {
1278 switch (TxtrTypeInfo.FarFilter)
1279 {
1280 case GL_NEAREST:
1281 case GL_LINEAR:
1282 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, Image->GetWidth(), Image->GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, Image->GetBuffer());
1283 break;
1284 case GL_NEAREST_MIPMAP_NEAREST:
1285 case GL_LINEAR_MIPMAP_NEAREST:
1286 case GL_NEAREST_MIPMAP_LINEAR:
1287 case GL_LINEAR_MIPMAP_LINEAR:
1288 if (Image->GetMipMapCount() > 1) {
1289 #ifdef GL_SGIS_generate_mipmap
1290 if (useSGISMipmaps) {
1291 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
1292 }
1293 #endif
1294 int i = 0;
1295 for (i = 0; i < Image->GetMipMapCount(); i++) {
1296 glTexImage2D(GL_TEXTURE_2D, i, internalFormat, max(1, Image->GetWidth() >> i), max(1, Image->GetHeight() >> i), 0, GL_RGBA, GL_UNSIGNED_BYTE, Image->GetMipMapPtr(i));
1297 }
1298 mipmapsLoaded = true;
1299 } else {
1300 #ifdef GL_SGIS_generate_mipmap
1301 if (useSGISMipmaps) {
1302 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
1303 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, Image->GetWidth(), Image->GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, Image->GetBuffer());
1304 } else
1305 #endif
1306 {
1307 gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat, Image->GetWidth(), Image->GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, Image->GetBuffer());
1308 }
1309 mipmapsLoaded = true;
1310 }
1311 break;
1312 default:
1313 assert(false);
1314 }
1315 } else if (Image->GetFormat() == ImageDescriptor::DXTC1 ||
1316 Image->GetFormat() == ImageDescriptor::DXTC3 ||
1317 Image->GetFormat() == ImageDescriptor::DXTC5)
1318 {
1319 #if defined(GL_ARB_texture_compression) && defined(GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
1320 if (Image->GetFormat() == ImageDescriptor::DXTC1)
1321 internalFormat = (load_as_sRGB) ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
1322 else if (Image->GetFormat() == ImageDescriptor::DXTC3)
1323 internalFormat = (load_as_sRGB) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
1324 else if (Image->GetFormat() == ImageDescriptor::DXTC5)
1325 internalFormat = (load_as_sRGB) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1326
1327 switch(TxtrTypeInfo.FarFilter)
1328 {
1329 case GL_NEAREST:
1330 case GL_LINEAR:
1331 glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, internalFormat, Image->GetWidth(), Image->GetHeight(), 0, Image->GetMipMapSize(0), Image->GetBuffer());
1332 break;
1333 case GL_NEAREST_MIPMAP_NEAREST:
1334 case GL_LINEAR_MIPMAP_NEAREST:
1335 case GL_NEAREST_MIPMAP_LINEAR:
1336 case GL_LINEAR_MIPMAP_LINEAR:
1337 if (Image->GetMipMapCount() > 1) {
1338 #ifdef GL_SGIS_generate_mipmap
1339 if (useSGISMipmaps) {
1340 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
1341 }
1342 #endif
1343 int i = 0;
1344 for (i = 0; i < Image->GetMipMapCount(); i++) {
1345 glCompressedTexImage2DARB(GL_TEXTURE_2D, i, internalFormat, max(1, Image->GetWidth() >> i), max(1, Image->GetHeight() >> i), 0, Image->GetMipMapSize(i), Image->GetMipMapPtr(i));
1346 }
1347 mipmapsLoaded = true;
1348 } else {
1349 #if defined GL_SGIS_generate_mipmap
1350 if (useSGISMipmaps) {
1351 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
1352 mipmapsLoaded = true;
1353 }
1354 #endif
1355 glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, internalFormat, Image->GetWidth(), Image->GetHeight(), 0, Image->GetMipMapSize(0), Image->GetBuffer());
1356 }
1357 break;
1358
1359 default:
1360 // Shouldn't happen
1361 assert(false);
1362 }
1363 #else
1364 assert(false);
1365 #endif
1366 }
1367
1368 // Set texture-mapping features
1369 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1370 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TxtrTypeInfo.NearFilter);
1371 if ((TxtrTypeInfo.FarFilter == GL_NEAREST_MIPMAP_NEAREST || TxtrTypeInfo.FarFilter == GL_LINEAR_MIPMAP_NEAREST || TxtrTypeInfo.FarFilter == GL_NEAREST_MIPMAP_LINEAR || TxtrTypeInfo.FarFilter == GL_LINEAR_MIPMAP_LINEAR) && !mipmapsLoaded)
1372 {
1373 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1374 } else {
1375 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TxtrTypeInfo.FarFilter);
1376 }
1377
1378 switch(TextureType)
1379 {
1380 case OGL_Txtr_Wall:
1381 // Walls are tiled in both direction
1382 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1383 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1384 #if defined(GL_TEXTURE_MAX_ANISOTROPY_EXT)
1385 // enable anisotropic filtering
1386 {
1387 float anisoLevel = Get_OGL_ConfigureData().AnisotropyLevel;
1388 if (anisoLevel > 0.0) {
1389 GLfloat max_aniso;
1390 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_aniso);
1391 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0F + ((anisoLevel-1.0F)/15.0F)*(max_aniso-1.0F));
1392 }
1393 }
1394 #endif
1395 break;
1396
1397 case OGL_Txtr_Landscape:
1398 // Landscapes repeat horizontally, have vertical limits or repeats vertically
1399 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1400 if (LandscapeVertRepeat)
1401 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1402 else
1403 {
1404 #if defined(GL_ARB_texture_mirrored_repeat)
1405 if (useMirroredRepeat)
1406 {
1407 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT_ARB);
1408 }
1409 else
1410 #endif
1411 {
1412 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1413 }
1414 }
1415 break;
1416
1417 case OGL_Txtr_Inhabitant:
1418 case OGL_Txtr_WeaponsInHand:
1419 // Sprites have both horizontal and vertical limits
1420 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1421 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1422 break;
1423 }
1424 }
1425
1426 // What to render:
1427
1428 // Always call this one and call it first; safe to allocate texture ID's in it
RenderNormal()1429 void TextureManager::RenderNormal()
1430 {
1431
1432
1433 TxtrStatePtr->Allocate(TextureType);
1434
1435 if (TxtrStatePtr->UseNormal())
1436 {
1437 assert(NormalBuffer || (NormalImage.get() && NormalImage.get()->IsPresent()));
1438 if (NormalImage.get() && NormalImage.get()->IsPresent()) {
1439 PlaceTexture(NormalImage.get());
1440 }
1441 }
1442
1443 gGLTxStats.binds++;
1444 int time = 0;
1445 gGLTxStats.totalBind += time;
1446 if (gGLTxStats.minBind > time) gGLTxStats.minBind = time;
1447 if (gGLTxStats.maxBind < time) gGLTxStats.maxBind = time;
1448 if (time>2) gGLTxStats.longNormalSetups++;
1449 }
1450
1451 // Call this one after RenderNormal()
RenderGlowing()1452 void TextureManager::RenderGlowing()
1453 {
1454
1455
1456 if (TxtrStatePtr->UseGlowing())
1457 {
1458 assert(GlowBuffer || (GlowImage.get() && GlowImage.get()->IsPresent()));
1459 if (GlowImage.get() && GlowImage.get()->IsPresent()) {
1460 PlaceTexture(GlowImage.get());
1461 }
1462 }
1463
1464 gGLTxStats.binds++;
1465 int time = 0;
1466 gGLTxStats.totalBind += time;
1467 if (gGLTxStats.minBind > time) gGLTxStats.minBind = time;
1468 if (gGLTxStats.maxBind < time) gGLTxStats.maxBind = time;
1469 if (time>2) gGLTxStats.longGlowSetups++;
1470 }
1471
RenderBump()1472 void TextureManager::RenderBump()
1473 {
1474 if (TxtrStatePtr->IsBumped) {
1475 if (TxtrStatePtr->UseBump() && OffsetImage.get() && OffsetImage.get()->IsPresent())
1476 PlaceTexture(OffsetImage.get(), true);
1477 } else {
1478 FlatBumpTexture();
1479 }
1480
1481 gGLTxStats.binds++;
1482 int time = 0;
1483 gGLTxStats.totalBind += time;
1484 if (gGLTxStats.minBind > time) gGLTxStats.minBind = time;
1485 if (gGLTxStats.maxBind < time) gGLTxStats.maxBind = time;
1486 if (time>2) gGLTxStats.longBumpSetups++;
1487 }
1488
SetupTextureMatrix()1489 void TextureManager::SetupTextureMatrix()
1490 {
1491 // set up the texture matrix
1492 switch(TextureType)
1493 {
1494 case OGL_Txtr_Wall:
1495 case OGL_Txtr_WeaponsInHand:
1496 case OGL_Txtr_Inhabitant:
1497 glMatrixMode(GL_TEXTURE);
1498 glLoadIdentity();
1499 if (TxtrOptsPtr->Substitution) {
1500 // these come in right side up, but the renderer
1501 // expects them to be upside down and sideways
1502 glRotatef(90.0, 0.0, 0.0, 1.0);
1503 glScalef(1.0, -1.0, 1.0);
1504 }
1505 glMatrixMode(GL_MODELVIEW);
1506 break;
1507 case OGL_Txtr_Landscape:
1508 glMatrixMode(GL_TEXTURE);
1509 glLoadIdentity();
1510 if (TxtrOptsPtr->Substitution) {
1511 // these come in right side up, and un-centered
1512 // the renderer expects them upside down, and centered
1513 glScalef(1.0, -U_Scale, 1.0);
1514 glTranslatef(0.0, U_Offset, 0.0);
1515 } else {
1516 glScalef(1.0, U_Scale, 1.0);
1517 glTranslatef(0.0, U_Offset, 0.0);
1518 }
1519 glMatrixMode(GL_MODELVIEW);
1520 break;
1521 }
1522 }
1523
RestoreTextureMatrix()1524 void TextureManager::RestoreTextureMatrix()
1525 {
1526 switch(TextureType)
1527 {
1528 case OGL_Txtr_Wall:
1529 case OGL_Txtr_WeaponsInHand:
1530 case OGL_Txtr_Inhabitant:
1531 case OGL_Txtr_Landscape:
1532 glMatrixMode(GL_TEXTURE);
1533 glLoadIdentity();
1534 glMatrixMode(GL_MODELVIEW);
1535 }
1536 }
1537
1538
1539
1540 // Init
TextureManager()1541 TextureManager::TextureManager()
1542 {
1543 NormalBuffer = 0;
1544 GlowBuffer = 0;
1545
1546 ShadingTables = NULL;
1547 TransferMode = 0;
1548 TransferData = 0;
1549 IsShadeless = false;
1550 TextureType = 0;
1551 LandscapeVertRepeat = false;
1552
1553 TxtrStatePtr = 0;
1554 TxtrOptsPtr = 0;
1555
1556 FastPath = 0;
1557
1558 LowLevelShape = 0;
1559
1560 // Marathon default
1561 Landscape_AspRatExp = 1;
1562 }
1563
1564 // Cleanup
~TextureManager()1565 TextureManager::~TextureManager()
1566 {
1567 if (NormalBuffer != 0) delete []NormalBuffer;
1568 if (GlowBuffer != 0) delete []GlowBuffer;
1569 }
1570
OGL_ResetTextures()1571 void OGL_ResetTextures()
1572 {
1573 // Fix for crashing bug when OpenGL is inactive
1574 if (!OGL_IsActive()) return;
1575
1576 // Reset the textures:
1577 for (int it=0; it<OGL_NUMBER_OF_TEXTURE_TYPES; it++)
1578 for (int ic=0; ic<MAXIMUM_COLLECTIONS; ic++)
1579 {
1580 bool CollectionPresent = is_collection_present(ic);
1581 short NumberOfBitmaps =
1582 CollectionPresent ? get_number_of_collection_bitmaps(ic) : 0;
1583
1584 CollBitmapTextureState *CBTSSet = TextureStateSets[it][ic];
1585 for (int ib=0; ib<NumberOfBitmaps; ib++)
1586 {
1587 TextureState *TSSet = CBTSSet[ib].CTStates;
1588 for (int ist=0; ist<NUMBER_OF_OPENGL_BITMAP_SETS; ist++)
1589 TSSet[ist].Reset();
1590 }
1591 }
1592
1593 // Reset the surface textures for all the models:
1594 OGL_ResetModelSkins(OGL_IsActive());
1595
1596 // Reset the font textures
1597 FontSpecifier::OGL_ResetFonts(false);
1598
1599 // Reset blitters
1600 OGL_Blitter::StopTextures();
1601
1602 glDeleteTextures(1, &flatBumpTextureID);
1603 flatBumpTextureID = 0;
1604 }
1605
1606
LoadModelSkin(ImageDescriptor & SkinImage,short Collection,short CLUT)1607 void LoadModelSkin(ImageDescriptor& SkinImage, short Collection, short CLUT)
1608 {
1609 // A lot of this is copies of TextureManager member code
1610
1611 ImageDescriptorManager Image;
1612 Image.set(&SkinImage);
1613
1614 int TxtrWidth = Image.get()->GetWidth();
1615 int TxtrHeight = Image.get()->GetHeight();
1616
1617 bool IsInfravision = IsInfravisionTable(CLUT);
1618 bool IsSilhouette = IsSilhouetteTable(CLUT);
1619
1620 if (IsInfravision)
1621 FindInfravisionVersion(Collection, Image);
1622 else if (IsSilhouette)
1623 FindSilhouetteVersion(Image);
1624
1625 TxtrTypeInfoData& TxtrTypeInfo = ModelSkinInfo;
1626
1627 // Display size: may be shrunk
1628 int LoadedWidth = MAX(TxtrWidth >> TxtrTypeInfo.Resolution, 1);
1629 int LoadedHeight = MAX(TxtrHeight >> TxtrTypeInfo.Resolution, 1);
1630
1631 // Fit the image into the maximum size allowed by the OpenGL implementation in use
1632 GLint MaxTextureSize;
1633 glGetIntegerv(GL_MAX_TEXTURE_SIZE,&MaxTextureSize);
1634 while (LoadedWidth > MaxTextureSize || LoadedHeight > MaxTextureSize)
1635 {
1636 LoadedWidth >>= 1;
1637 LoadedHeight >>= 1;
1638 }
1639
1640 while (Image.get()->GetWidth() > LoadedWidth || Image.get()->GetHeight() > LoadedHeight)
1641 {
1642 if (!Image.edit()->Minify()) break;
1643 }
1644
1645 bool mipmapsLoaded = false;
1646
1647 // Load the texture
1648 GLenum internalFormat = TxtrTypeInfo.ColorFormat;
1649 if (Image.get()->GetFormat() == ImageDescriptor::RGBA8)
1650 {
1651 switch(TxtrTypeInfo.FarFilter)
1652 {
1653 case GL_NEAREST:
1654 case GL_LINEAR:
1655 glTexImage2D(GL_TEXTURE_2D, 0, TxtrTypeInfo.ColorFormat, LoadedWidth, LoadedHeight,
1656 0, GL_RGBA, GL_UNSIGNED_BYTE, Image.get()->GetBuffer());
1657 break;
1658 case GL_NEAREST_MIPMAP_NEAREST:
1659 case GL_LINEAR_MIPMAP_NEAREST:
1660 case GL_NEAREST_MIPMAP_LINEAR:
1661 case GL_LINEAR_MIPMAP_LINEAR:
1662 if (Image.get()->GetMipMapCount() > 1)
1663 {
1664 #ifdef GL_SGIS_generate_mipmap
1665 if (useSGISMipmaps) {
1666 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
1667 }
1668 #endif
1669 int i = 0;
1670 for (i = 0; i < Image.get()->GetMipMapCount(); i++)
1671 {
1672 glTexImage2D(GL_TEXTURE_2D, i, internalFormat, max(1, Image.get()->GetWidth() >> i), max(1, Image.get()->GetHeight() >> i), 0, GL_RGBA, GL_UNSIGNED_BYTE, Image.get()->GetMipMapPtr(i));
1673 }
1674 mipmapsLoaded = true;
1675 }
1676 else
1677 {
1678 #ifdef GL_SGIS_generate_mipmap
1679 if (useSGISMipmaps)
1680 {
1681 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
1682 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, Image.get()->GetWidth(), Image.get()->GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, Image.get()->GetBuffer());
1683 }
1684 else
1685 #endif
1686 {
1687 gluBuild2DMipmaps(GL_TEXTURE_2D, TxtrTypeInfo.ColorFormat, LoadedWidth, LoadedHeight,
1688 GL_RGBA, GL_UNSIGNED_BYTE, Image.get()->GetBuffer());
1689 }
1690 mipmapsLoaded = true;
1691 }
1692 break;
1693
1694 default:
1695 // Shouldn't happen
1696 assert(false);
1697 }
1698 }
1699 else if (Image.get()->GetFormat() == ImageDescriptor::DXTC1 ||
1700 Image.get()->GetFormat() == ImageDescriptor::DXTC3 ||
1701 Image.get()->GetFormat() == ImageDescriptor::DXTC5)
1702 {
1703 #if defined (GL_ARB_texture_compression) && defined(GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
1704 if (Image.get()->GetFormat() == ImageDescriptor::DXTC1)
1705 internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
1706 else if (Image.get()->GetFormat() == ImageDescriptor::DXTC3)
1707 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
1708 else if (Image.get()->GetFormat() == ImageDescriptor::DXTC5)
1709 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1710
1711 switch (TxtrTypeInfo.FarFilter)
1712 {
1713 case GL_NEAREST:
1714 case GL_LINEAR:
1715 glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, internalFormat, Image.get()->GetWidth(), Image.get()->GetHeight(), 0, Image.get()->GetMipMapSize(0), Image.get()->GetBuffer());
1716 break;
1717 case GL_NEAREST_MIPMAP_NEAREST:
1718 case GL_LINEAR_MIPMAP_NEAREST:
1719 case GL_NEAREST_MIPMAP_LINEAR:
1720 case GL_LINEAR_MIPMAP_LINEAR:
1721 if (Image.get()->GetMipMapCount() > 1)
1722 {
1723 #ifdef GL_SGIS_generate_mipmap
1724 if (useSGISMipmaps)
1725 {
1726 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
1727 }
1728 #endif
1729 int i = 0;
1730 for (i = 0; i < Image.get()->GetMipMapCount(); i++)
1731 {
1732 glCompressedTexImage2DARB(GL_TEXTURE_2D, i, internalFormat, max(1, Image.get()->GetWidth() >> i), max(1, Image.get()->GetHeight() >> i), 0, Image.get()->GetMipMapSize(i), Image.get()->GetMipMapPtr(i));
1733 }
1734 mipmapsLoaded = true;
1735 }
1736 else
1737 {
1738 #ifdef GL_SGIS_generate_mipmap
1739 if (useSGISMipmaps)
1740 {
1741 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
1742 mipmapsLoaded = true;
1743 }
1744 #endif
1745 glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, internalFormat, Image.get()->GetWidth(), Image.get()->GetHeight(), 0, Image.get()->GetMipMapSize(0), Image.get()->GetBuffer());
1746 }
1747 break;
1748
1749 default:
1750 // Shouldn't happen
1751 assert(false);
1752 }
1753 #else
1754 assert(false);
1755 #endif
1756 }
1757
1758 // Set texture-mapping features
1759 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1760 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TxtrTypeInfo.NearFilter);
1761 if ((TxtrTypeInfo.FarFilter == GL_NEAREST_MIPMAP_NEAREST || TxtrTypeInfo.FarFilter == GL_LINEAR_MIPMAP_NEAREST || TxtrTypeInfo.FarFilter == GL_NEAREST_MIPMAP_LINEAR || TxtrTypeInfo.FarFilter == GL_LINEAR_MIPMAP_LINEAR) && !mipmapsLoaded)
1762 {
1763 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1764 }
1765 else
1766 {
1767 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TxtrTypeInfo.FarFilter);
1768 }
1769
1770
1771 // Like sprites, model textures have both horizontal and vertical limits
1772 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1773 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1774
1775 }
1776
1777
1778 // Infravision (I'm blue, are you?)
IsInfravisionActive()1779 bool& IsInfravisionActive() {return InfravisionActive;}
1780
1781
1782 // Sets the infravision tinting color for a shapes collection, and whether to use such tinting;
1783 // the color values are from 0 to 1.
SetInfravisionTint(short Collection,bool IsTinted,float Red,float Green,float Blue)1784 bool SetInfravisionTint(short Collection, bool IsTinted, float Red, float Green, float Blue)
1785 {
1786 assert(Collection >= 0 && Collection < NUMBER_OF_COLLECTIONS);
1787 InfravisionData& IVData = IVDataList[Collection];
1788
1789 IVData.Red = Red;
1790 IVData.Green = Green;
1791 IVData.Blue = Blue;
1792 IVData.IsTinted = IsTinted;
1793
1794 return true;
1795 }
1796
1797
1798 // Finds the infravision version of a color for some collection set;
1799 // it makes no change if infravision is inactive.
FindInfravisionVersionRGBA(short Collection,GLfloat * Color)1800 void FindInfravisionVersionRGBA(short Collection, GLfloat *Color)
1801 {
1802 if (!InfravisionActive) return;
1803
1804 InfravisionData& IVData = IVDataList[Collection];
1805 if (!IVData.IsTinted) return;
1806
1807 GLfloat AvgColor = (Color[0] + Color[1] + Color[2])/3;
1808 Color[0] = IVData.Red*AvgColor;
1809 Color[1] = IVData.Green*AvgColor;
1810 Color[2] = IVData.Blue*AvgColor;
1811 }
1812
1813
1814 // Mass-production version of above; suitable for textures
FindInfravisionVersionRGBA(short Collection,int NumPixels,uint32 * Pixels)1815 void FindInfravisionVersionRGBA(short Collection, int NumPixels, uint32 *Pixels)
1816 {
1817 if (!InfravisionActive) return;
1818
1819 InfravisionData& IVData = IVDataList[Collection];
1820 if (!IVData.IsTinted) return;
1821
1822 // OK to use marching-pointer optimization here;
1823 // the float-to-int and int-to-float conversions have been simplified,
1824 // because the infravision-value-finding does not care if the values
1825 // had been multipled by 255 (int <-> float color-value multiplier/divider)
1826 for (int k=0; k<NumPixels; k++, Pixels++)
1827 {
1828 uint8 *PxlPtr = (uint8 *)Pixels;
1829 GLfloat AvgColor = GLfloat(int(PxlPtr[0]) + int(PxlPtr[1]) + int(PxlPtr[2]))/3;
1830 PxlPtr[0] = PIN(int(IVData.Red*AvgColor + 0.5),0,255);
1831 PxlPtr[1] = PIN(int(IVData.Green*AvgColor + 0.5),0,255);
1832 PxlPtr[2] = PIN(int(IVData.Blue*AvgColor + 0.5),0,255);
1833 }
1834 }
1835
FindInfravisionVersionDXTCColor(SDL_Color tint,uint16 color)1836 static inline uint16 FindInfravisionVersionDXTCColor(SDL_Color tint, uint16 color)
1837 {
1838 uint16 grayscale = (((color & 0xf800) >> 11) + ((color & 0x7e0) >> 6) + (color & 0x1f)) / 3;
1839
1840 uint16 r = (grayscale * tint.r) / 256;
1841 uint16 g = (grayscale * tint.g) / 256;
1842 uint16 b = (grayscale * tint.b) / 256;
1843
1844 return (r << 11) | (g << 6) | (g > 15 ? 0x20 : 0) | b;
1845 }
1846
FindInfravisionVersionDXTC1(InfravisionData & IVData,int NumBytes,unsigned char * buffer)1847 void FindInfravisionVersionDXTC1(InfravisionData& IVData, int NumBytes, unsigned char *buffer)
1848 {
1849 assert(NumBytes % 8 == 0);
1850
1851 uint16 *pixels = (uint16 *) buffer;
1852
1853 SDL_Color tint;
1854 tint.r = PIN(int(IVData.Red * 256), 0, 255);
1855 tint.g = PIN(int(IVData.Green * 256), 0, 255);
1856 tint.b = PIN(int(IVData.Blue* 256), 0, 255);
1857 tint.a = 0xff;
1858
1859 // the first two uint16s in each block are our colors
1860 for (int i = 0; i < NumBytes / 4; i++) {
1861
1862 uint16 c1 = SDL_SwapLE16(pixels[i * 4]);
1863 uint16 c2 = SDL_SwapLE16(pixels[i * 4 + 1]);
1864 uint16 new_c1 = FindInfravisionVersionDXTCColor(tint, c1);
1865 uint16 new_c2 = FindInfravisionVersionDXTCColor(tint, c2);
1866
1867 // DXTC1 uses c1 > c2 to determine whether to make certain
1868 // pixels transparent
1869 // if c1 and c2 happen to come out of the infravision algorithm
1870 // the same, we have to chamge one so that pixels don't become
1871 // transparent that were opaque, or vice versa
1872 if (new_c1 == new_c2) {
1873 if (c1 > c2)
1874 if (new_c2) new_c2 -= 1;
1875 else new_c1 += 1;
1876 else
1877 if (new_c1) new_c1 -= 1;
1878 else new_c2 += 1;
1879 }
1880 // likewise, in the unlikely state that infravision ends up
1881 // making one bigger than the other, swap them back
1882 else if ((new_c1 > new_c2) != (c1 > c2)) {
1883 SWAP(new_c1, new_c2);
1884 }
1885 pixels[i * 4] = SDL_SwapLE16(new_c1);
1886 pixels[i * 4 + 1] = SDL_SwapLE16(new_c2);
1887 }
1888 }
1889
FindInfavisionVersionDXTC35(InfravisionData & IVData,int NumBytes,unsigned char * buffer)1890 void FindInfavisionVersionDXTC35(InfravisionData &IVData, int NumBytes, unsigned char *buffer)
1891 {
1892 assert(NumBytes % 16 == 0);
1893
1894 uint16 *pixels = (uint16 *) buffer;
1895
1896 SDL_Color tint;
1897 tint.r = PIN(int(IVData.Red * 256), 0, 255);
1898 tint.g = PIN(int(IVData.Green * 256), 0, 255);
1899 tint.b = PIN(int(IVData.Blue* 256), 0, 255);
1900 tint.a = 0xff;
1901
1902 for (int i = 0; i < NumBytes / 8; i++) {
1903 uint16 *c1 = &pixels[i * 8 + 4];
1904 uint16 *c2 = &pixels[i * 8 + 5];
1905
1906 uint16 new_c1 = FindInfravisionVersionDXTCColor(tint, SDL_SwapLE16(*c1));
1907 uint16 new_c2 = FindInfravisionVersionDXTCColor(tint, SDL_SwapLE16(*c2));
1908
1909 *c1 = SDL_SwapLE16(new_c1);
1910 *c2 = SDL_SwapLE16(new_c2);
1911 }
1912 }
1913
FindInfravisionVersion(short Collection,ImageDescriptorManager & imageManager)1914 void FindInfravisionVersion(short Collection, ImageDescriptorManager &imageManager)
1915 {
1916 if (!InfravisionActive) return;
1917 InfravisionData& IVData = IVDataList[Collection];
1918 if (!IVData.IsTinted) return;
1919
1920 if (!imageManager.get() || !imageManager.get()->IsPresent())
1921 return;
1922
1923 if (imageManager.get()->GetFormat() == ImageDescriptor::RGBA8) {
1924 FindInfravisionVersionRGBA(Collection, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer());
1925 } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC1) {
1926 FindInfravisionVersionDXTC1(IVData, imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer());
1927 } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC3 || imageManager.get()->GetFormat() == ImageDescriptor::DXTC5) {
1928 FindInfavisionVersionDXTC35(IVData, imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer());
1929 }
1930 }
1931
FindSilhouetteVersionDXTC1(int NumBytes,unsigned char * buffer)1932 void FindSilhouetteVersionDXTC1(int NumBytes, unsigned char *buffer)
1933 {
1934 uint16 *pixels = (uint16 *) buffer;
1935
1936 for (int i = 0; i < NumBytes / 4; i++)
1937 {
1938 if (SDL_SwapLE16(pixels[i * 4]) > SDL_SwapLE16(pixels[i * 4 + 1]))
1939 {
1940 pixels[i * 4 + 1] = PlatformIsLittleEndian() ? 0xffdf : 0xdfff;
1941 }
1942 else
1943 {
1944 pixels[i * 4 + 1] = 0xffff;
1945 }
1946 pixels[i * 4] = 0xffff;
1947 }
1948 }
1949
FindSilhouetteVersionDXTC35(int NumBytes,unsigned char * buffer)1950 void FindSilhouetteVersionDXTC35(int NumBytes, unsigned char *buffer)
1951 {
1952 uint16 *pixels = (uint16 *) buffer;
1953
1954 for (int i = 0; i < NumBytes / 8; i++)
1955 {
1956 pixels[i * 8 + 4] = 0xffff;
1957 pixels[i * 8 + 5] = PlatformIsLittleEndian() ? 0xffdf : 0xdfff;
1958 }
1959 }
1960
FindSilhouetteVersionRGBA(int NumPixels,uint32 * Pixels)1961 void FindSilhouetteVersionRGBA(int NumPixels, uint32 *Pixels)
1962 {
1963 for (int i = 0; i < NumPixels; i++)
1964 {
1965 Pixels[i] |= PlatformIsLittleEndian() ? 0x00ffffff : 0xffffff00;
1966 }
1967 }
1968
FindSilhouetteVersion(ImageDescriptorManager & imageManager)1969 void FindSilhouetteVersion(ImageDescriptorManager &imageManager)
1970 {
1971 if (imageManager.get()->GetFormat() == ImageDescriptor::RGBA8)
1972 {
1973 FindSilhouetteVersionRGBA(imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer());
1974 }
1975 else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC1)
1976 {
1977 FindSilhouetteVersionDXTC1(imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer());
1978 }
1979 else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC3 || imageManager.get()->GetFormat() == ImageDescriptor::DXTC5)
1980 {
1981 FindSilhouetteVersionDXTC35(imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer());
1982 }
1983 imageManager.edit()->PremultipliedAlpha = false;
1984 }
1985
SetPixelOpacitiesDXTC3Row(int scale,int shift,uint16 alpha)1986 static inline uint16 SetPixelOpacitiesDXTC3Row(int scale, int shift, uint16 alpha)
1987 {
1988 uint16 a1 = (alpha >> 12) & 0xf;
1989 uint16 a2 = (alpha >> 8) & 0xf;
1990 uint16 a3 = (alpha >> 4) & 0xf;
1991 uint16 a4 = alpha & 0xf;
1992
1993 a1 = PIN((a1 * scale) / 16 + shift, 0, 15);
1994 a2 = PIN((a2 * scale) / 16 + shift, 0, 15);
1995 a3 = PIN((a3 * scale) / 16 + shift, 0, 15);
1996 a4 = PIN((a4 * scale) / 16 + shift, 0, 15);
1997
1998 return ((a1 << 12) | (a2 << 8) | (a3 << 4) | a4);
1999 }
2000
SetPixelOpacitiesDXTC3(OGL_TextureOptions & Options,int NumBytes,unsigned char * buffer)2001 void SetPixelOpacitiesDXTC3(OGL_TextureOptions& Options, int NumBytes, unsigned char *buffer)
2002 {
2003 assert(NumBytes % 16 == 0);
2004
2005 uint16 *rows = (uint16 *) buffer;
2006
2007 int scale = PIN(int(Options.OpacityScale * 16), 0, 16);
2008 int shift = PIN(int(Options.OpacityShift * 16), -16, 16);
2009
2010 for (int i = 0; i < NumBytes / 8; i++) {
2011 uint16 *a1 = &rows[i * 8];
2012 uint16 *a2 = &rows[i * 8 + 1];
2013 uint16 *a3 = &rows[i * 8 + 2];
2014 uint16 *a4 = &rows[i * 8 + 3];
2015 *a1 = SetPixelOpacitiesDXTC3Row(scale, shift, *a1);
2016 *a2 = SetPixelOpacitiesDXTC3Row(scale, shift, *a2);
2017 *a3 = SetPixelOpacitiesDXTC3Row(scale, shift, *a3);
2018 *a4 = SetPixelOpacitiesDXTC3Row(scale, shift, *a4);
2019 }
2020
2021 }
2022
SetPixelOpacitiesDXTC5Pair(int scale,int shift,uint16 alpha)2023 static inline uint16 SetPixelOpacitiesDXTC5Pair(int scale, int shift, uint16 alpha)
2024 {
2025 uint16 a1 = alpha >> 8;
2026 uint16 a2 = alpha & 0xff;
2027
2028 uint16 new_a1 = PIN((a1 * scale) / 256 + shift, 0, 255);
2029 uint16 new_a2 = PIN((a2 * scale) / 256 + shift, 0, 255);
2030
2031 if (new_a1 == new_a2 && a1 != a2)
2032 if (a1 > a2)
2033 if (new_a2) new_a2--;
2034 else new_a1++;
2035 else
2036 if (new_a1) new_a1--;
2037 else new_a2++;
2038 else if ((new_a1 > new_a2) != (a1 > a2))
2039 SWAP(new_a1, new_a2);
2040
2041 return (new_a1 << 8 | new_a2);
2042 }
2043
SetPixelOpacitiesDXTC5(OGL_TextureOptions & Options,int NumBytes,unsigned char * buffer)2044 void SetPixelOpacitiesDXTC5(OGL_TextureOptions& Options, int NumBytes, unsigned char *buffer)
2045 {
2046 assert (NumBytes % 16 == 0);
2047
2048 uint16 *pixels = (uint16 *) buffer;
2049
2050 int scale = PIN(int(Options.OpacityScale * 256), 0, 256);
2051 int shift = PIN(int(Options.OpacityShift * 256), -256, 256);
2052
2053 for (int i = 0; i < NumBytes / 8; i++) {
2054 pixels[i * 8] = SDL_SwapLE16(SetPixelOpacitiesDXTC5Pair(scale, shift, SDL_SwapLE16(pixels[i * 8])));
2055 }
2056 }
2057
2058
SetPixelOpacities(OGL_TextureOptions & Options,ImageDescriptorManager & imageManager)2059 void SetPixelOpacities(OGL_TextureOptions& Options, ImageDescriptorManager &imageManager)
2060 {
2061 if (Options.OpacityType != OGL_OpacType_Avg && Options.OpacityType != OGL_OpacType_Max && Options.OpacityScale == 1.0 && Options.OpacityShift == 0.0)
2062 return;
2063
2064 if (imageManager.get()->GetFormat() == ImageDescriptor::RGBA8) {
2065
2066 SetPixelOpacitiesRGBA(Options, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer());
2067
2068 } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC3) {
2069
2070 // to do opac_type we have to decompress the texture
2071 if (Options.OpacityType == OGL_OpacType_Avg || Options.OpacityType == OGL_OpacType_Max) {
2072 if (imageManager.edit()->MakeRGBA()) {
2073 SetPixelOpacitiesRGBA(Options, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer());
2074 } else if (Options.OpacityScale == 1.0 && Options.OpacityShift == 0.0) {
2075 return;
2076 } else {
2077 SetPixelOpacitiesDXTC3(Options, imageManager.edit()->GetBufferSize() / 4, (unsigned char *) imageManager.edit()->GetBuffer());
2078 }
2079 } else {
2080 // if it's just scale/shift, we can do without decompressing
2081 SetPixelOpacitiesDXTC3(Options, imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer());
2082 }
2083
2084 } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC5) {
2085 if (Options.OpacityType == OGL_OpacType_Avg || Options.OpacityType == OGL_OpacType_Max) {
2086 if (imageManager.edit()->MakeRGBA()) {
2087 SetPixelOpacitiesRGBA(Options, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer());
2088 } else if (Options.OpacityScale == 1.0 && Options.OpacityShift == 0.0) {
2089 return;
2090 } else {
2091 SetPixelOpacitiesDXTC5(Options, imageManager.edit()->GetBufferSize() / 4, (unsigned char *) imageManager.edit()->GetBuffer());
2092 }
2093 } else {
2094 SetPixelOpacitiesDXTC5(Options, imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer());
2095 }
2096 } else {
2097 // we have to decompress DXTC1 to do anything
2098 if (imageManager.edit()->MakeRGBA()) {
2099 SetPixelOpacitiesRGBA(Options, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer());
2100 }
2101 }
2102
2103 }
2104
2105
2106 // Does this for a set of several pixel values or color-table values;
2107 // the pixels are assumed to be in OpenGL-friendly byte-by-byte RGBA format.
SetPixelOpacitiesRGBA(OGL_TextureOptions & Options,int NumPixels,uint32 * Pixels)2108 void SetPixelOpacitiesRGBA(OGL_TextureOptions& Options, int NumPixels, uint32 *Pixels)
2109 {
2110 for (int k=0; k<NumPixels; k++)
2111 {
2112 uint8 *PxlPtr = (uint8 *)(Pixels + k);
2113
2114 // This won't be scaled to (0,1), but will be left at (0,255) here
2115 float Opacity;
2116 switch(Options.OpacityType)
2117 {
2118 // Two versions of the Tomb Raider texture-opacity hack
2119 case OGL_OpacType_Avg:
2120 {
2121 uint32 Red = uint32(PxlPtr[0]);
2122 uint32 Green = uint32(PxlPtr[1]);
2123 uint32 Blue = uint32(PxlPtr[2]);
2124 Opacity = (Red + Green + Blue)/3.0F;
2125 }
2126 break;
2127
2128 case OGL_OpacType_Max:
2129 {
2130 uint32 Red = uint32(PxlPtr[0]);
2131 uint32 Green = uint32(PxlPtr[1]);
2132 uint32 Blue = uint32(PxlPtr[2]);
2133 Opacity = (float)MAX(MAX(Red,Green),Blue);
2134 }
2135 break;
2136
2137 // Use pre-existing alpha value; useful if the opacity was loaded from a mask image
2138 default:
2139 Opacity = PxlPtr[3];
2140 break;
2141 }
2142
2143 // Scale, shift, and put back the edited opacity;
2144 // round off and pin to the appropriate range.
2145 // The shift has to be scaled to the color-channel range (1 -> 255).
2146 PxlPtr[3] = PIN(int32(Options.OpacityScale*Opacity + 255*Options.OpacityShift + 0.5),0,255);
2147 }
2148 }
2149
2150 /*
2151 // Stuff for doing 16->32 pixel-format conversion, 1555 ARGB to 8888 RGBA
2152 GLuint *ConversionTable_16to32 = NULL;
2153
2154 void MakeConversion_16to32(int BitDepth)
2155 {
2156 // This is for allocating a 16->32 conversion table only when necessary
2157 if (BitDepth == 16 && (!ConversionTable_16to32))
2158 {
2159 // Allocate it
2160 int TableSize = (1 << 15);
2161 ConversionTable_16to32 = new GLuint[TableSize];
2162
2163 // Fill it
2164 for (word InVal = 0; InVal < TableSize; InVal++)
2165 ConversionTable_16to32[InVal] = Convert_16to32(InVal);
2166 }
2167 else if (ConversionTable_16to32)
2168 {
2169 // Get rid of it
2170 delete []ConversionTable_16to32;
2171 ConversionTable_16to32 = NULL;
2172 }
2173 }
2174 */
2175
2176 #endif // def HAVE_OPENGL
2177