Line data Source code
1 0 : /*
2 :
3 : OOCombinedEmissionMapGenerator.m
4 :
5 :
6 : Copyright (C) 2010-2013 Jens Ayton
7 :
8 : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : of this software and associated documentation files (the "Software"), to deal
10 : in the Software without restriction, including without limitation the rights
11 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : copies of the Software, and to permit persons to whom the Software is
13 : furnished to do so, subject to the following conditions:
14 :
15 : The above copyright notice and this permission notice shall be included in all
16 : copies or substantial portions of the Software.
17 :
18 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : SOFTWARE.
25 :
26 : */
27 :
28 : #import "OOCombinedEmissionMapGenerator.h"
29 :
30 : #import "OOColor.h"
31 : #import "OOPixMapChannelOperations.h"
32 : #import "OOTextureScaling.h"
33 : #import "OOTextureInternal.h"
34 : #import "OOMaterialSpecifier.h"
35 : #import "OOCollectionExtractors.h"
36 :
37 :
38 0 : #define DUMP_COMBINER 0
39 :
40 :
41 : static OOColor *ModulateColor(OOColor *a, OOColor *b);
42 : static void ScaleToMatch(OOPixMap *pmA, OOPixMap *pmB);
43 :
44 :
45 : @interface OOCombinedEmissionMapGenerator (Private)
46 :
47 0 : - (id) initWithEmissionMapSpec:(NSDictionary *)emissionMapSpec
48 : emissionColor:(OOColor *)emissionColor
49 : diffuseMap:(OOTexture *)diffuseMap
50 : diffuseColor:(OOColor *)diffuseColor
51 : illuminationMapSpec:(NSDictionary *)illuminationMapSpec
52 : illuminationColor:(OOColor *)illuminationColor
53 : isCombinedMap:(BOOL)isCombinedMap
54 : optionsSpecifier:(NSDictionary *)spec;
55 :
56 0 : - (NSString *)constructCacheKey;
57 :
58 : @end
59 :
60 :
61 : @implementation OOCombinedEmissionMapGenerator
62 :
63 : - (id) initWithEmissionMapSpec:(NSDictionary *)emissionMapSpec
64 : emissionColor:(OOColor *)emissionColor
65 : diffuseMap:(OOTexture *)diffuseMap
66 : diffuseColor:(OOColor *)diffuseColor
67 : illuminationMapSpec:(NSDictionary *)illuminationMapSpec
68 : illuminationColor:(OOColor *)illuminationColor
69 : optionsSpecifier:(NSDictionary *)spec
70 : {
71 : return [self initWithEmissionMapSpec:emissionMapSpec
72 : emissionColor:emissionColor
73 : diffuseMap:diffuseMap
74 : diffuseColor:diffuseColor
75 : illuminationMapSpec:illuminationMapSpec
76 : illuminationColor:illuminationColor
77 : isCombinedMap:NO
78 : optionsSpecifier:spec];
79 : }
80 :
81 :
82 : - (id) initWithEmissionAndIlluminationMapSpec:(NSDictionary *)emissionAndIlluminationMapSpec
83 : diffuseMap:(OOTexture *)diffuseMap
84 : diffuseColor:(OOColor *)diffuseColor
85 : emissionColor:(OOColor *)emissionColor
86 : illuminationColor:(OOColor *)illuminationColor
87 : optionsSpecifier:(NSDictionary *)spec
88 : {
89 : return [self initWithEmissionMapSpec:emissionAndIlluminationMapSpec
90 : emissionColor:emissionColor
91 : diffuseMap:diffuseMap
92 : diffuseColor:diffuseColor
93 : illuminationMapSpec:nil
94 : illuminationColor:illuminationColor
95 : isCombinedMap:YES
96 : optionsSpecifier:spec];
97 : }
98 :
99 :
100 0 : - (id) initWithEmissionMapSpec:(NSDictionary *)emissionMapSpec
101 : emissionColor:(OOColor *)emissionColor
102 : diffuseMap:(OOTexture *)diffuseMap
103 : diffuseColor:(OOColor *)diffuseColor
104 : illuminationMapSpec:(NSDictionary *)illuminationMapSpec
105 : illuminationColor:(OOColor *)illuminationColor
106 : isCombinedMap:(BOOL)isCombinedMap
107 : optionsSpecifier:(NSDictionary *)spec
108 : {
109 : if (emissionMapSpec == nil && illuminationMapSpec == nil)
110 : {
111 : [self release];
112 : return nil;
113 : }
114 :
115 : NSParameterAssert(illuminationMapSpec == nil || !isCombinedMap);
116 :
117 : uint32_t options;
118 : GLfloat anisotropy;
119 : GLfloat lodBias;
120 : OOInterpretTextureSpecifier(spec, NULL, &options, &anisotropy, &lodBias, YES);
121 : options = OOApplyTextureOptionDefaults(options);
122 :
123 : if ((self = [super initWithPath:@"<generated emission map>" options:options]))
124 : {
125 : /* Illumination contribution is:
126 : illuminationMap * illuminationColor * diffuseMap * diffuseColor
127 : Since illuminationColor and diffuseColor aren't used otherwise,
128 : we may as well combine them up front.
129 : */
130 : illuminationColor = ModulateColor(diffuseColor, illuminationColor);
131 :
132 : if ([emissionColor isWhite]) emissionColor = nil;
133 : if ([illuminationColor isWhite]) illuminationColor = nil;
134 : if (!isCombinedMap && illuminationMapSpec == nil) diffuseMap = nil; // Diffuse map is only used with illumination
135 :
136 : // Insert extraShrink flag here instead of using extraOptions later because we need it in cache key too.
137 : NSMutableDictionary *emissionSpec = [emissionMapSpec mutableCopy];
138 : [emissionSpec oo_setBool:YES forKey:kOOTextureSpecifierExtraShrinkKey];
139 : _emissionSpec = emissionSpec;
140 :
141 : NSMutableDictionary *illuminationSpec = [illuminationMapSpec mutableCopy];
142 : [illuminationSpec oo_setBool:YES forKey:kOOTextureSpecifierExtraShrinkKey];
143 : _illuminationSpec = illuminationSpec;
144 :
145 : _diffuseMap = [diffuseMap retain];
146 :
147 : _emissionColor = [emissionColor retain];
148 : _illuminationColor = [illuminationColor retain];
149 : _isCombinedMap = isCombinedMap;
150 :
151 : _textureOptions = options;
152 : _anisotropy = anisotropy;
153 : _lodBias = lodBias;
154 :
155 : _cacheKey = [self constructCacheKey];
156 :
157 : if ([OOTexture existingTextureForKey:_cacheKey] == nil)
158 : {
159 : /* Extract pixmap from diffuse map. This must be done in the main
160 : thread even if scheduling is fixed, because it might involve
161 : reading back pixels from OpenGL.
162 : */
163 : if (diffuseMap != nil)
164 : {
165 : _diffusePx = [diffuseMap copyPixMapRepresentation];
166 : #ifndef NDEBUG
167 : _diffuseDesc = [[diffuseMap shortDescription] copy];
168 : #endif
169 : }
170 :
171 : /* Extract emission and illumination pixmaps from loaders. Ideally,
172 : this would be done asynchronously, but that requires dependency
173 : management in OOAsyncWorkManager.
174 : */
175 : OOTextureDataFormat format;
176 : if (_emissionSpec != nil)
177 : {
178 : OOTextureLoader *emissionMapLoader = [OOTextureLoader loaderWithTextureSpecifier:_emissionSpec
179 : extraOptions:0
180 : folder:@"Textures"];
181 : [emissionMapLoader getResult:&_emissionPx format:&format originalWidth:NULL originalHeight:NULL];
182 : #ifndef NDEBUG
183 : _emissionDesc = [[emissionMapLoader shortDescription] copy];
184 : #endif
185 : }
186 : if (_illuminationSpec != nil)
187 : {
188 : OOTextureLoader *illuminationMapLoader = [OOTextureLoader loaderWithTextureSpecifier:_illuminationSpec
189 : extraOptions:0
190 : folder:@"Textures"];
191 : [illuminationMapLoader getResult:&_illuminationPx format:&format originalWidth:NULL originalHeight:NULL];
192 : #ifndef NDEBUG
193 : _illuminationDesc = [[illuminationMapLoader shortDescription] copy];
194 : #endif
195 : }
196 : }
197 : }
198 :
199 : return self;
200 : }
201 :
202 :
203 0 : - (NSString *)constructCacheKey
204 : {
205 : NSMutableString *cacheKey = [NSMutableString string];
206 :
207 : if (_isCombinedMap) [cacheKey appendString:@"combined emission and illumination map;"];
208 : else if (_emissionSpec == nil) [cacheKey appendString:@"illumination map;"];
209 : else if (_illuminationSpec == nil) [cacheKey appendString:@"emission map;"];
210 : else [cacheKey appendString:@"merged emission and illumination map;"];
211 :
212 : NSString *emissionDesc = nil;
213 : if (_emissionSpec != nil)
214 : {
215 : emissionDesc = OOTextureCacheKeyForSpecifier(_emissionSpec);
216 : [cacheKey appendFormat:@"emission:{%@}", emissionDesc];
217 : if (_emissionColor != nil) [cacheKey appendFormat:@"*%@", [_emissionColor rgbaDescription]];
218 : [cacheKey appendString:@";"];
219 : }
220 :
221 : NSString *illuminationDesc = nil;
222 : if (_isCombinedMap)
223 : {
224 : illuminationDesc = [NSString stringWithFormat:@"%@:a", emissionDesc];
225 : }
226 : else if (_illuminationSpec != nil)
227 : {
228 : illuminationDesc = OOTextureCacheKeyForSpecifier(_illuminationSpec);
229 : }
230 :
231 : if (illuminationDesc != nil)
232 : {
233 : [cacheKey appendFormat:@"illumination:{%@}*{%@}", illuminationDesc, [_diffuseMap cacheKey]];
234 : if (_illuminationColor != nil) [cacheKey appendFormat:@"*%@", [_illuminationColor rgbaDescription]];
235 : [cacheKey appendString:@";"];
236 : }
237 :
238 : return cacheKey;
239 : }
240 :
241 :
242 0 : - (void) dealloc
243 : {
244 : DESTROY(_emissionSpec);
245 : DESTROY(_illuminationSpec);
246 : DESTROY(_diffuseMap);
247 :
248 : OOFreePixMap(&_emissionPx);
249 : OOFreePixMap(&_illuminationPx);
250 : OOFreePixMap(&_diffusePx);
251 : DESTROY(_emissionColor);
252 : DESTROY(_illuminationColor);
253 :
254 : #ifndef NDEBUG
255 : DESTROY(_diffuseDesc);
256 : DESTROY(_emissionDesc);
257 : DESTROY(_illuminationDesc);
258 : #endif
259 :
260 : [super dealloc];
261 : }
262 :
263 :
264 : #ifndef NDEBUG
265 0 : - (NSString *) descriptionComponents
266 : {
267 : NSMutableString *result = [NSMutableString string];
268 : BOOL haveIllumination = NO;
269 :
270 : if (_emissionDesc != nil)
271 : {
272 : [result appendFormat:@"emission map: %@", _emissionDesc];
273 : if (_isCombinedMap)
274 : {
275 : [result appendString:@".rgb"];
276 : }
277 : if (_emissionColor != nil)
278 : {
279 : [result appendFormat:@" * %@", [_emissionColor rgbaDescription]];
280 : }
281 :
282 : if (_isCombinedMap)
283 : {
284 : [result appendFormat:@", illumination map: %@.a", _emissionDesc];
285 : haveIllumination = YES;
286 : }
287 : }
288 :
289 : if (_illuminationDesc != nil)
290 : {
291 : if (_emissionDesc != nil) [result appendString:@", "];
292 : [result appendFormat:@"illumination map: %@", _illuminationDesc];
293 : haveIllumination = YES;
294 : }
295 :
296 : if (haveIllumination)
297 : {
298 : if (_diffuseDesc != nil)
299 : {
300 : [result appendFormat:@" * %@", _diffuseDesc];
301 : }
302 : if (_illuminationColor != nil)
303 : {
304 : [result appendFormat:@" * %@", [_illuminationColor rgbaDescription]];
305 : }
306 : }
307 :
308 : return result;
309 : }
310 : #endif
311 :
312 :
313 0 : - (uint32_t) textureOptions
314 : {
315 : return _textureOptions;
316 : }
317 :
318 :
319 0 : - (GLfloat) anisotropy
320 : {
321 : return _anisotropy;
322 : }
323 :
324 :
325 0 : - (GLfloat) lodBias
326 : {
327 : return _lodBias;
328 : }
329 :
330 :
331 0 : - (NSString *) cacheKey
332 : {
333 : return _cacheKey;
334 : }
335 :
336 :
337 0 : - (void) loadTexture
338 : {
339 : OOPixMap illuminationPx = kOONullPixMap;
340 : BOOL haveEmission = NO, haveIllumination = NO, haveDiffuse = NO;
341 :
342 : #if DUMP_COMBINER
343 : static unsigned sTexID = 0;
344 : unsigned texID = ++sTexID, dumpCount = 0;
345 :
346 : #define DUMP(pm, label) OODumpPixMap(pm, [NSString stringWithFormat:@"lightmap %u.%u - %@", texID, ++dumpCount, label]);
347 : #else
348 0 : #define DUMP(pm, label) do {} while (0)
349 : #endif
350 :
351 : haveEmission = !OOIsNullPixMap(_emissionPx);
352 : if (haveEmission) DUMP(_emissionPx, @"source emission map");
353 :
354 : // Extract illumination component if emission_and_illumination_map.
355 : if (haveEmission && _isCombinedMap && OOPixMapFormatHasAlpha(_emissionPx.format))
356 : {
357 : OOPixMapToRGBA(&_emissionPx);
358 : illuminationPx = OODuplicatePixMap(_emissionPx, 0);
359 : OOExtractPixMapChannel(&illuminationPx, 3, YES);
360 : haveIllumination = YES;
361 : DUMP(illuminationPx, @"extracted illumination map");
362 : }
363 :
364 : // Tint emission map if necessary.
365 : if (haveEmission && _emissionColor != nil)
366 : {
367 : OOPixMapModulateUniform(&_emissionPx, [_emissionColor redComponent], [_emissionColor greenComponent], [_emissionColor blueComponent], 1.0);
368 : DUMP(_emissionPx, @"modulated emission map");
369 : }
370 :
371 : if (!OOIsNullPixMap(_illuminationPx))
372 : {
373 : NSAssert(!_isCombinedMap, @"OOCombinedEmissionMapGenerator configured with both illumination map and combined emission/illumination map.");
374 :
375 : illuminationPx = _illuminationPx;
376 : _illuminationPx.pixels = NULL;
377 : haveIllumination = YES;
378 : DUMP(illuminationPx, @"source illumination map");
379 : }
380 :
381 : // Tint illumination map if necessary.
382 : if (haveIllumination && _illuminationColor != nil)
383 : {
384 : OOPixMapModulateUniform(&illuminationPx, [_illuminationColor redComponent], [_illuminationColor greenComponent], [_illuminationColor blueComponent], 1.0);
385 : DUMP(illuminationPx, @"modulated illumination map");
386 : }
387 :
388 : // Load diffuse map and combine with illumination map.
389 : haveDiffuse = !OOIsNullPixMap(_diffusePx);
390 : if (haveDiffuse) DUMP(_diffusePx, @"source diffuse map");
391 :
392 : if (haveIllumination && haveDiffuse)
393 : {
394 : // Modulate illumination with diffuse map.
395 : ScaleToMatch(&_diffusePx, &illuminationPx);
396 : OOPixMapToRGBA(&_diffusePx);
397 : OOPixMapModulatePixMap(&illuminationPx, _diffusePx);
398 : DUMP(illuminationPx, @"combined diffuse and illumination map");
399 : }
400 : OOFreePixMap(&_diffusePx);
401 :
402 : if (haveIllumination)
403 : {
404 : if (haveEmission)
405 : {
406 : OOPixMapToRGBA(&illuminationPx);
407 : OOPixMapAddPixMap(&_emissionPx, illuminationPx);
408 : OOFreePixMap(&illuminationPx);
409 : DUMP(_emissionPx, @"combined emission and illumination map");
410 : }
411 : else if (haveIllumination)
412 : {
413 : // No explicit emission map -> modulated illumination map is our only emission map.
414 : _emissionPx = illuminationPx;
415 : haveEmission = YES;
416 : illuminationPx.pixels = NULL;
417 : }
418 : haveIllumination = NO; // Either way, illumination is now baked into emission.
419 : }
420 :
421 : (void)haveEmission;
422 : (void)haveIllumination;
423 :
424 : // Done: emissionPx now contains combined emission map.
425 : OOCompactPixMap(&_emissionPx);
426 : if (OOIsValidPixMap(_emissionPx))
427 : {
428 : _data = _emissionPx.pixels;
429 : _width = _emissionPx.width;
430 : _height = _emissionPx.height;
431 : _rowBytes = _emissionPx.rowBytes;
432 : _format = _emissionPx.format;
433 :
434 : _emissionPx.pixels = NULL; // So it won't be freed by -dealloc.
435 : }
436 : if (_data == NULL)
437 : {
438 : OOLogERR(@"texture.combinedEmissionMap.error", @"Unknown error loading %@", self);
439 : }
440 : }
441 :
442 : @end
443 :
444 :
445 0 : static OOColor *ModulateColor(OOColor *a, OOColor *b)
446 : {
447 : if (a == nil) return b;
448 : if (b == nil) return a;
449 :
450 : OORGBAComponents ac, bc;
451 : ac = [a rgbaComponents];
452 : bc = [b rgbaComponents];
453 :
454 : ac.r *= bc.r;
455 : ac.g *= bc.g;
456 : ac.b *= bc.b;
457 : ac.a *= bc.a;
458 :
459 : return [OOColor colorWithRGBAComponents:ac];
460 : }
461 :
462 :
463 0 : static void ScaleToMatch(OOPixMap *pmA, OOPixMap *pmB)
464 : {
465 : NSCParameterAssert(pmA != NULL && pmB != NULL && OOIsValidPixMap(*pmA) && OOIsValidPixMap(*pmB));
466 :
467 : OOPixMapDimension minWidth = MIN(pmA->width, pmB->width);
468 : OOPixMapDimension minHeight = MIN(pmA->height, pmB->height);
469 :
470 : if (pmA->width != minWidth || pmA->height != minHeight)
471 : {
472 : *pmA = OOScalePixMap(*pmA, minWidth, minHeight, NO);
473 : }
474 : if (pmB->width != minWidth || pmB->height != minHeight)
475 : {
476 : *pmB = OOScalePixMap(*pmB, minWidth, minHeight, NO);
477 : }
478 : }
|