Oolite 1.91.0.7644-241112-7f5034b
Loading...
Searching...
No Matches
OOConcreteTexture.m
Go to the documentation of this file.
1/*
2
3 OOConcreteTexture.m
4
5 Copyright (C) 2007-2013 Jens Ayton and contributors
6
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24
25*/
26
27#import "OOTextureInternal.h"
28#import "OOConcreteTexture.h"
29
30#import "OOTextureLoader.h"
31
33#import "Universe.h"
34#import "ResourceManager.h"
36#import "OOMacroOpenGL.h"
37#import "OOCPUInfo.h"
38#import "OOPixMap.h"
39
40#ifndef NDEBUG
42#endif
43
44
45#if OOLITE_BIG_ENDIAN
46#define RGBA_IMAGE_TYPE GL_UNSIGNED_INT_8_8_8_8_REV
47#elif OOLITE_LITTLE_ENDIAN
48#define RGBA_IMAGE_TYPE GL_UNSIGNED_BYTE
49#else
50#error Neither OOLITE_BIG_ENDIAN nor OOLITE_LITTLE_ENDIAN is defined as nonzero!
51#endif
52
53
54@interface OOConcreteTexture (Private)
55
56- (void)setUpTexture;
57- (void)uploadTexture;
58- (void)uploadTextureDataWithMipMap:(BOOL)mipMap format:(OOTextureDataFormat)format;
59#if OO_TEXTURE_CUBE_MAP
60- (void) uploadTextureCubeMapDataWithMipMap:(BOOL)mipMap format:(OOTextureDataFormat)format;
61#endif
62
63- (GLenum) glTextureTarget;
64
65#if OOTEXTURE_RELOADABLE
66- (BOOL) isReloadable;
67#endif
68
69@end
70
71
72static BOOL DecodeFormat(OOTextureDataFormat format, uint32_t options, GLenum *outFormat, GLenum *outInternalFormat, GLenum *outType);
73
74
75@implementation OOConcreteTexture
76
77- (id) initWithLoader:(OOTextureLoader *)loader
78 key:(NSString *)key
79 options:(uint32_t)options
80 anisotropy:(GLfloat)anisotropy
81 lodBias:(GLfloat)lodBias
82{
83 if (loader == nil)
84 {
85 [self release];
86 return nil;
87 }
88
89 self = [super init];
90 if (EXPECT_NOT(self == nil)) return nil;
91
92 _loader = [loader retain];
93 _options = options;
94
95#if GL_EXT_texture_filter_anisotropic
96 _anisotropy = OOClamp_0_1_f(anisotropy) * gOOTextureInfo.anisotropyScale;
97#endif
98#if GL_EXT_texture_lod_bias
99 _lodBias = lodBias;
100#endif
101
102#ifndef NDEBUG
103 if ([loader isKindOfClass:[OOTextureGenerator class]])
104 {
105 _name = [[NSString alloc] initWithFormat:@"<%@>", [loader class]];
106 }
107#endif
108
109 _key = [key copy];
110
111 [self addToCaches];
112
113 return self;
114}
115
116
117- (id)initWithPath:(NSString *)path
118 key:(NSString *)key
119 options:(uint32_t)options
120 anisotropy:(float)anisotropy
121 lodBias:(GLfloat)lodBias
122{
123 OOTextureLoader *loader = [OOTextureLoader loaderWithPath:path options:options];
124 if (loader == nil)
125 {
126 [self release];
127 return nil;
128 }
129
130 if ((self = [self initWithLoader:loader key:key options:options anisotropy:anisotropy lodBias:lodBias]))
131 {
132#if OOTEXTURE_RELOADABLE
133 _path = [path retain];
134#endif
135 }
136
137 return self;
138}
139
140
141- (void)dealloc
142{
143#ifndef NDEBUG
144 OOLog(_trace ? @"texture.allocTrace.dealloc" : @"texture.dealloc", @"Deallocating and uncaching texture %p", self);
145#endif
146
147#if OOTEXTURE_RELOADABLE
148 DESTROY(_path);
149#endif
150
151 if (_loaded)
152 {
153 if (_textureName != 0)
154 {
156 OOGL(glDeleteTextures(1, &_textureName));
157 _textureName = 0;
158 }
159 free(_bytes);
160 _bytes = NULL;
161 }
162
163#ifndef OOTEXTURE_NO_CACHE
164 [self removeFromCaches];
165 [_key autorelease];
166 _key = nil;
167#endif
168
169 DESTROY(_loader);
170
171#ifndef NDEBUG
172 DESTROY(_name);
173#endif
174
175 [super dealloc];
176}
177
178
179- (NSString *) descriptionComponents
180{
181 NSString *stateDesc = nil;
182
183 if (_loaded)
184 {
185 if (_valid)
186 {
187 stateDesc = [NSString stringWithFormat:@"%u x %u", _width, _height];
188 }
189 else
190 {
191 stateDesc = @"LOAD ERROR";
192 }
193 }
194 else
195 {
196 stateDesc = @"loading";
197 }
198
199 return [NSString stringWithFormat:@"%@, %@", _key, stateDesc];
200}
201
202
203- (NSString *) shortDescriptionComponents
204{
205 return _key;
206}
207
208
209#ifndef NDEBUG
210- (NSString *) name
211{
212 if (_name != nil) return _name;
213
214#if OOTEXTURE_RELOADABLE
215 NSString *name = [_path lastPathComponent];
216#else
217 NSString *name = [[[[self cacheKey] componentsSeparatedByString:@":"] objectAtIndex:0] lastPathComponent];
218#endif
219
220 NSString *channelSuffix = nil;
221 switch (_options & kOOTextureExtractChannelMask)
222 {
224 channelSuffix = @":r";
225 break;
226
228 channelSuffix = @":g";
229 break;
230
232 channelSuffix = @":b";
233 break;
234
236 channelSuffix = @":a";
237 break;
238 }
239
240 if (channelSuffix != nil) name = [name stringByAppendingString:channelSuffix];
241
242 return name;
243}
244#endif
245
246
247- (void)apply
248{
250
251 if (EXPECT_NOT(!_loaded)) [self setUpTexture];
252 else if (EXPECT_NOT(!_uploaded)) [self uploadTexture];
253 else OOGL(glBindTexture([self glTextureTarget], _textureName));
254
255#if GL_EXT_texture_lod_bias
256 if (gOOTextureInfo.textureLODBiasAvailable) OOGL(glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, _lodBias));
257#endif
258}
259
260
261- (void)ensureFinishedLoading
262{
263 if (!_loaded) [self setUpTexture];
264}
265
266
267- (BOOL) isFinishedLoading
268{
269 return _loaded || [_loader isReady];
270}
271
272
273- (NSString *) cacheKey
274{
275 return _key;
276}
277
278
279- (NSSize)dimensions
280{
281 [self ensureFinishedLoading];
282
283 return NSMakeSize(_width, _height);
284}
285
286
287- (NSSize) originalDimensions
288{
289 [self ensureFinishedLoading];
290
291 return NSMakeSize(_originalWidth, _originalHeight);
292}
293
294
295- (BOOL) isMipMapped
296{
297 [self ensureFinishedLoading];
298
299 return _mipLevels != 0;
300}
301
302
303- (struct OOPixMap) copyPixMapRepresentation
304{
305 [self ensureFinishedLoading];
306
308
309 if (_bytes != NULL)
310 {
311 // If possible, just copy our existing buffer.
312 px = OOMakePixMap(_bytes, _width, _height, _format, 0, 0);
313 px = OODuplicatePixMap(px, 0);
314 }
315#if OOTEXTURE_RELOADABLE
316 else
317 {
318 // Otherwise, read it back from OpenGL.
320
321 GLenum format, internalFormat, type;
322 if (!DecodeFormat(_format, _options, &format, &internalFormat, &type))
323 {
324 return kOONullPixMap;
325 }
326
327 if (![self isCubeMap])
328 {
329
330 px = OOAllocatePixMap(_width, _height, _format, 0, 0);
331 if (!OOIsValidPixMap(px)) return kOONullPixMap;
332
333 glGetTexImage(GL_TEXTURE_2D, 0, format, type, px.pixels);
334 }
335#if OO_TEXTURE_CUBE_MAP
336 else
337 {
338 px = OOAllocatePixMap(_width, _width * 6, _format, 0, 0);
339 if (!OOIsValidPixMap(px)) return kOONullPixMap;
340 uint8_t *pixels = px.pixels;
341
342 unsigned i;
343 for (i = 0; i < 6; i++)
344 {
345 glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, type, pixels);
346 pixels += OOPixMapBytesPerPixelForFormat(_format) * _width * _width;
347 }
348 }
349#endif
350 }
351#endif
352
353 return px;
354}
355
356
357- (BOOL) isRectangleTexture
358{
359#if GL_EXT_texture_rectangle
360 return _isRectTexture;
361#else
362 return NO;
363#endif
364}
365
366
367- (BOOL) isCubeMap
368{
369#if OO_TEXTURE_CUBE_MAP
370 return _isCubeMap;
371#else
372 return NO;
373#endif
374}
375
376
377- (NSSize)texCoordsScale
378{
379#if GL_EXT_texture_rectangle
380 if (_loaded)
381 {
382 if (!_isRectTexture)
383 {
384 return NSMakeSize(1.0f, 1.0f);
385 }
386 else
387 {
388 return NSMakeSize(_width, _height);
389 }
390 }
391 else
392 {
393 // Not loaded
394 if (!(_options & kOOTextureAllowRectTexture))
395 {
396 return NSMakeSize(1.0f, 1.0f);
397 }
398 else
399 {
400 // Finishing may clear the rectangle texture flag (if the texture turns out to be POT)
401 [self ensureFinishedLoading];
402 return [self texCoordsScale];
403 }
404 }
405#else
406 return NSMakeSize(1.0f, 1.0f);
407#endif
408}
409
410
411- (GLint)glTextureName
412{
413 [self ensureFinishedLoading];
414
415 return _textureName;
416}
417
418@end
419
420
421@implementation OOConcreteTexture (Private)
422
423- (void)setUpTexture
424{
425 OOPixMap pm;
426
427 // This will block until loading is completed, if necessary.
428 if ([_loader getResult:&pm format:&_format originalWidth:&_originalWidth originalHeight:&_originalHeight])
429 {
430 _bytes = pm.pixels;
431 _width = pm.width;
432 _height = pm.height;
433
434#if OO_TEXTURE_CUBE_MAP
435 if (_options & kOOTextureAllowCubeMap && _height == _width * 6 && gOOTextureInfo.cubeMapAvailable)
436 {
437 _isCubeMap = YES;
438 }
439#endif
440
441#if !defined(NDEBUG) && OOTEXTURE_RELOADABLE
442 if (_trace)
443 {
444 static unsigned dumpID = 0;
445 NSString *name = [NSString stringWithFormat:@"tex dump %u \"%@\"", ++dumpID,[self name]];
446 OOLog(@"texture.trace.dump", @"Dumped traced texture %@ to \'%@.png\'", self, name);
447 OODumpPixMap(pm, name);
448 }
449#endif
450
451 [self uploadTexture];
452 }
453 else
454 {
455 _textureName = 0;
456 _valid = NO;
457 _uploaded = YES;
458 }
459
460 _loaded = YES;
461
462 DESTROY(_loader);
463}
464
465
466- (void) uploadTexture
467{
468 GLint filter;
469 BOOL mipMap = NO;
470
472
473 if (!_uploaded)
474 {
475 GLenum texTarget = [self glTextureTarget];
476
477 OOGL(glGenTextures(1, &_textureName));
478 OOGL(glBindTexture(texTarget, _textureName));
479
480 // Select wrap mode
481 GLint clampMode = gOOTextureInfo.clampToEdgeAvailable ? GL_CLAMP_TO_EDGE : GL_CLAMP;
482 GLint wrapS = (_options & kOOTextureRepeatS) ? GL_REPEAT : clampMode;
483 GLint wrapT = (_options & kOOTextureRepeatT) ? GL_REPEAT : clampMode;
484
485#if OO_TEXTURE_CUBE_MAP
486 if (texTarget == GL_TEXTURE_CUBE_MAP)
487 {
488 wrapS = wrapT = clampMode;
489 OOGL(glTexParameteri(texTarget, GL_TEXTURE_WRAP_R, clampMode));
490 }
491#endif
492
493 OOGL(glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, wrapS));
494 OOGL(glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, wrapT));
495
496 // Select min filter
497 filter = _options & kOOTextureMinFilterMask;
498 if (filter == kOOTextureMinFilterNearest) filter = GL_NEAREST;
499 else if (filter == kOOTextureMinFilterMipMap)
500 {
501 mipMap = YES;
502 filter = GL_LINEAR_MIPMAP_LINEAR;
503 }
504 else filter = GL_LINEAR;
505 OOGL(glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, filter));
506
507#if GL_EXT_texture_filter_anisotropic
508 if (gOOTextureInfo.anisotropyAvailable && mipMap && 1.0 < _anisotropy)
509 {
510 OOGL(glTexParameterf(texTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, _anisotropy));
511 }
512#endif
513
514 // Select mag filter
515 filter = _options & kOOTextureMagFilterMask;
516 if (filter == kOOTextureMagFilterNearest) filter = GL_NEAREST;
517 else filter = GL_LINEAR;
518 OOGL(glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, filter));
519
520 // if (gOOTextureInfo.clientStorageAvailable) EnableClientStorage();
521
522 if (texTarget == GL_TEXTURE_2D)
523 {
524 [self uploadTextureDataWithMipMap:mipMap format:_format];
525 OOLog(@"texture.upload", @"Uploaded texture %u (%ux%u pixels, %@)", _textureName, _width, _height, _key);
526 }
527#if OO_TEXTURE_CUBE_MAP
528 else if (texTarget == GL_TEXTURE_CUBE_MAP)
529 {
530 [self uploadTextureCubeMapDataWithMipMap:mipMap format:_format];
531 OOLog(@"texture.upload", @"Uploaded cube map texture %u (%ux%ux6 pixels, %@)", _textureName, _width, _width, _key);
532 }
533#endif
534 else
535 {
536 [NSException raise:NSInternalInconsistencyException format:@"Unhandled texture target 0x%X.", texTarget];
537 }
538
539 _valid = YES;
540 _uploaded = YES;
541
542#if OOTEXTURE_RELOADABLE
543 if ([self isReloadable])
544 {
545 free(_bytes);
546 _bytes = NULL;
547 }
548#endif
549 }
550}
551
552
553- (void)uploadTextureDataWithMipMap:(BOOL)mipMap format:(OOTextureDataFormat)format
554{
555 GLenum glFormat = 0, internalFormat = 0, type = 0;
556 unsigned w = _width,
557 h = _height,
558 level = 0;
559 char *bytes = _bytes;
560 uint8_t components = OOTextureComponentsForFormat(format);
561
563
564 if (!DecodeFormat(format, _options, &glFormat, &internalFormat, &type)) return;
565
566 while (0 < w && 0 < h)
567 {
568 OOGL(glTexImage2D(GL_TEXTURE_2D, level++, internalFormat, w, h, 0, glFormat, type, bytes));
569 if (!mipMap) return;
570 bytes += w * components * h;
571 w >>= 1;
572 h >>= 1;
573 }
574
575 // Note: we only reach here if (mipMap).
576 _mipLevels = level - 1;
577 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, _mipLevels));
578}
579
580
581#if OO_TEXTURE_CUBE_MAP
582- (void) uploadTextureCubeMapDataWithMipMap:(BOOL)mipMap format:(OOTextureDataFormat)format
583{
585
586 GLenum glFormat = 0, internalFormat = 0, type = 0;
587 if (!DecodeFormat(format, _options, &glFormat, &internalFormat, &type)) return;
588 uint8_t components = OOTextureComponentsForFormat(format);
589
590 // Calculate stride between cube map sides.
591 size_t sideSize = _width * _width * components;
592 if (mipMap)
593 {
594 sideSize = sideSize * 4 / 3;
595 sideSize = (sideSize + 15) & ~15;
596 }
597
598 unsigned side;
599 for (side = 0; side < 6; side++)
600 {
601 char *bytes = _bytes;
602 bytes += side * sideSize;
603
604 unsigned w = _width, level = 0;
605
606 while (0 < w)
607 {
608 OOGL(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, level++, internalFormat, w, w, 0, glFormat, type, bytes));
609 if (!mipMap) break;
610 bytes += w * w * components;
611 w >>= 1;
612 }
613 }
614}
615#endif
616
617
618- (GLenum) glTextureTarget
619{
620 GLenum texTarget = GL_TEXTURE_2D;
621#if OO_TEXTURE_CUBE_MAP
622 if (_isCubeMap)
623 {
624 texTarget = GL_TEXTURE_CUBE_MAP;
625 }
626#endif
627 return texTarget;
628}
629
630
631- (void) forceRebind
632{
633 if (_loaded && _uploaded && _valid)
634 {
636
637 _uploaded = NO;
638 OOGL(glDeleteTextures(1, &_textureName));
639 _textureName = 0;
640
641#if OOTEXTURE_RELOADABLE
642 if ([self isReloadable])
643 {
644 OOLog(@"texture.reload", @"Reloading texture %@", self);
645
646 free(_bytes);
647 _bytes = NULL;
648 _loaded = NO;
649 _uploaded = NO;
650 _valid = NO;
651
652 _loader = [[OOTextureLoader loaderWithPath:_path options:_options] retain];
653 }
654#endif
655 }
656}
657
658
659#if OOTEXTURE_RELOADABLE
660
661- (BOOL) isReloadable
662{
663 return _path != nil;
664}
665
666#endif
667
668@end
669
670
671static BOOL DecodeFormat(OOTextureDataFormat format, uint32_t options, GLenum *outFormat, GLenum *outInternalFormat, GLenum *outType)
672{
673 NSCParameterAssert(outFormat != NULL && outInternalFormat != NULL && outType != NULL);
674
675 switch (format)
676 {
678 *outFormat = GL_RGBA;
679 *outInternalFormat = options & kOOTextureSRGBA ? GL_SRGB_ALPHA : GL_RGBA;
680 *outType = RGBA_IMAGE_TYPE;
681 return YES;
682
684 if (options & kOOTextureAlphaMask)
685 {
686 *outFormat = GL_ALPHA;
687 *outInternalFormat = GL_ALPHA8;
688 }
689 else
690 {
691 *outFormat = GL_LUMINANCE;
692 *outInternalFormat = GL_LUMINANCE8;
693 }
694 *outType = GL_UNSIGNED_BYTE;
695 return YES;
696
698 *outFormat = GL_LUMINANCE_ALPHA;
699 *outInternalFormat = GL_LUMINANCE8_ALPHA8;
700 *outType = GL_UNSIGNED_BYTE;
701 return YES;
702
703 default:
704 OOLog(kOOLogParameterError, @"Unexpected texture format %u.", format);
705 return NO;
706 }
707}
#define DESTROY(x)
Definition OOCocoa.h:77
static BOOL DecodeFormat(OOTextureDataFormat format, uint32_t options, GLenum *outFormat, GLenum *outInternalFormat, GLenum *outType)
#define EXPECT_NOT(x)
#define OOLog(class, format,...)
Definition OOLogging.h:88
NSString *const kOOLogParameterError
Definition OOLogging.m:647
#define OO_ENTER_OPENGL()
#define OOGL(statement)
Definition OOOpenGL.h:251
void OODumpPixMap(OOPixMap pixMap, NSString *name)
Definition OOPixMap.m:145
OOPixMapFormat
Definition OOPixMap.h:39
unsigned short OOPixMapBytesPerPixelForFormat(OOPixMapFormat format) PURE_FUNC
Definition OOPixMap.m:202
const OOPixMap kOONullPixMap
Definition OOPixMap.m:31
OOPixMap OOAllocatePixMap(OOPixMapDimension width, OOPixMapDimension height, OOPixMapFormat format, size_t rowBytes, size_t bufferSize)
Definition OOPixMap.m:73
OOPixMap OOMakePixMap(void *pixels, OOPixMapDimension width, OOPixMapDimension height, OOPixMapFormat format, size_t rowBytes, size_t bufferSize)
Definition OOPixMap.m:53
BOOL OOIsValidPixMap(OOPixMap pixMap)
Definition OOPixMap.m:42
OOPixMap OODuplicatePixMap(OOPixMap srcPixMap, size_t desiredSize)
Definition OOPixMap.m:95
return nil
#define GL_TEXTURE_MAX_LEVEL
#define GL_CLAMP_TO_EDGE
uint8_t OOTextureComponentsForFormat(OOTextureDataFormat format)
Definition OOTexture.m:667
@ kOOTextureDataGrayscaleAlpha
Definition OOTexture.h:111
@ kOOTextureDataGrayscale
Definition OOTexture.h:110
@ kOOTextureDataRGBA
Definition OOTexture.h:109
@ kOOTextureRepeatS
Definition OOTexture.h:55
@ kOOTextureAllowRectTexture
Definition OOTexture.h:57
@ kOOTextureExtractChannelA
Definition OOTexture.h:70
@ kOOTextureExtractChannelB
Definition OOTexture.h:69
@ kOOTextureMagFilterMask
Definition OOTexture.h:73
@ kOOTextureExtractChannelMask
Definition OOTexture.h:65
@ kOOTextureAllowCubeMap
Definition OOTexture.h:61
@ kOOTextureMinFilterMask
Definition OOTexture.h:72
@ kOOTextureSRGBA
Definition OOTexture.h:63
@ kOOTextureRepeatT
Definition OOTexture.h:56
@ kOOTextureMinFilterMipMap
Definition OOTexture.h:48
@ kOOTextureAlphaMask
Definition OOTexture.h:60
@ kOOTextureMagFilterNearest
Definition OOTexture.h:50
@ kOOTextureExtractChannelG
Definition OOTexture.h:68
@ kOOTextureExtractChannelR
Definition OOTexture.h:67
@ kOOTextureMinFilterNearest
Definition OOTexture.h:46
OOTextureInfo gOOTextureInfo
Definition OOTexture.m:101
id loaderWithPath:options:(NSString *path,[options] uint32_t options)
oneway void release()
Definition OOTexture.m:614
OOPixMapDimension height
Definition OOPixMap.h:50
void * pixels
Definition OOPixMap.h:49
OOPixMapDimension width
Definition OOPixMap.h:50
unsigned anisotropyAvailable
unsigned clampToEdgeAvailable
unsigned cubeMapAvailable
unsigned textureLODBiasAvailable