Line data Source code
1 0 : /*
2 :
3 : OOShaderMaterial.m
4 :
5 :
6 : Copyright (C) 2007-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 :
29 : #import "OOShaderMaterial.h"
30 :
31 : #if OO_SHADERS
32 :
33 : #import "ResourceManager.h"
34 : #import "OOShaderUniform.h"
35 : #import "OOFunctionAttributes.h"
36 : #import "OOCollectionExtractors.h"
37 : #import "OOShaderProgram.h"
38 : #import "OOTexture.h"
39 : #import "OOOpenGLExtensionManager.h"
40 : #import "OOMacroOpenGL.h"
41 : #import "Universe.h"
42 : #import "OOIsNumberLiteral.h"
43 : #import "OOLogging.h"
44 : #import "OODebugFlags.h"
45 : #import "OOStringParsing.h"
46 :
47 :
48 : NSString * const kOOVertexShaderSourceKey = @"_oo_vertex_shader_source";
49 : NSString * const kOOVertexShaderNameKey = @"vertex_shader";
50 : NSString * const kOOFragmentShaderSourceKey = @"_oo_fragment_shader_source";
51 : NSString * const kOOFragmentShaderNameKey = @"fragment_shader";
52 : NSString * const kOOTexturesKey = @"textures";
53 : NSString * const kOOTextureObjectsKey = @"_oo_texture_objects";
54 : NSString * const kOOUniformsKey = @"uniforms";
55 : NSString * const kOOIsSynthesizedMaterialConfigurationKey = @"_oo_is_synthesized_config";
56 : NSString * const kOOIsSynthesizedMaterialMacrosKey = @"_oo_synthesized_material_macros";
57 :
58 :
59 : static BOOL GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult);
60 : static NSString *MacrosToString(NSDictionary *macros);
61 :
62 :
63 : @interface OOShaderMaterial (OOPrivate)
64 :
65 : // Convert a "textures" array to an "_oo_texture_objects" array.
66 : - (NSArray *) loadTexturesFromArray:(NSArray *)textureSpecs unitCount:(GLuint)max;
67 :
68 : // Load up an array of texture objects.
69 : - (void) addTexturesFromArray:(NSArray *)textureObjects unitCount:(GLuint)max;
70 :
71 : @end
72 :
73 :
74 : @implementation OOShaderMaterial
75 :
76 : + (BOOL)configurationDictionarySpecifiesShaderMaterial:(NSDictionary *)configuration
77 : {
78 : if (configuration == nil) return NO;
79 :
80 : if ([configuration oo_stringForKey:kOOVertexShaderSourceKey] != nil) return YES;
81 : if ([configuration oo_stringForKey:kOOFragmentShaderSourceKey] != nil) return YES;
82 : if ([configuration oo_stringForKey:kOOVertexShaderNameKey] != nil) return YES;
83 : if ([configuration oo_stringForKey:kOOVertexShaderNameKey] != nil) return YES;
84 :
85 : return NO;
86 : }
87 :
88 :
89 : + (instancetype) shaderMaterialWithName:(NSString *)name
90 : configuration:(NSDictionary *)configuration
91 : macros:(NSDictionary *)macros
92 : bindingTarget:(id<OOWeakReferenceSupport>)target
93 : {
94 : return [[[self alloc] initWithName:name configuration:configuration macros:macros bindingTarget:target] autorelease];
95 : }
96 :
97 :
98 : - (id) initWithName:(NSString *)name
99 : configuration:(NSDictionary *)configuration
100 : macros:(NSDictionary *)macros
101 : bindingTarget:(id<OOWeakReferenceSupport>)target
102 : {
103 : BOOL OK = YES;
104 : NSString *macroString = nil;
105 : NSString *vertexShader = nil;
106 : NSString *fragmentShader = nil;
107 : GLint textureUnits = [[OOOpenGLExtensionManager sharedManager] textureImageUnitCount];
108 : NSMutableDictionary *modifiedMacros = nil;
109 : NSString *vsName = @"<synthesized>";
110 : NSString *fsName = @"<synthesized>";
111 : NSString *vsCacheKey = nil;
112 : NSString *fsCacheKey = nil;
113 :
114 : if (configuration == nil) OK = NO;
115 :
116 : self = [super initWithName:name configuration:configuration];
117 : if (self == nil) OK = NO;
118 :
119 : if (OK)
120 : {
121 : modifiedMacros = macros ? [macros mutableCopy] : [[NSMutableDictionary alloc] init];
122 : [modifiedMacros autorelease];
123 :
124 : [modifiedMacros setObject:[NSNumber numberWithUnsignedInt:textureUnits]
125 : forKey:@"OO_TEXTURE_UNIT_COUNT"];
126 :
127 : // used to test for simplified shaders - OO_REDUCED_COMPLEXITY - here
128 : macroString = MacrosToString(modifiedMacros);
129 : }
130 :
131 : if (OK)
132 : {
133 : vertexShader = [configuration oo_stringForKey:kOOVertexShaderSourceKey];
134 : if (vertexShader == nil)
135 : {
136 : vsName = [configuration oo_stringForKey:kOOVertexShaderNameKey];
137 : vsCacheKey = vsName;
138 : if (vsName != nil)
139 : {
140 : if (!GetShaderSource(vsName, @"vertex", macroString, &vertexShader)) OK = NO;
141 : }
142 : }
143 : else
144 : {
145 : vsCacheKey = vertexShader;
146 : }
147 : }
148 :
149 : if (OK)
150 : {
151 : fragmentShader = [configuration oo_stringForKey:kOOFragmentShaderSourceKey];
152 : if (fragmentShader == nil)
153 : {
154 : fsName = [configuration oo_stringForKey:kOOFragmentShaderNameKey];
155 : fsCacheKey = fsName;
156 : if (fsName != nil)
157 : {
158 : if (!GetShaderSource(fsName, @"fragment", macroString, &fragmentShader)) OK = NO;
159 : }
160 : }
161 : else
162 : {
163 : fsCacheKey = fragmentShader;
164 : }
165 : }
166 :
167 : if (OK)
168 : {
169 : if (vertexShader != nil || fragmentShader != nil)
170 : {
171 : static NSDictionary *attributeBindings = nil;
172 : if (attributeBindings == nil)
173 : {
174 : attributeBindings = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kTangentAttributeIndex]
175 : forKey:@"tangent"];
176 : [attributeBindings retain];
177 : }
178 :
179 : NSString *cacheKey = [NSString stringWithFormat:@"$VERTEX:\n%@\n\n$FRAGMENT:\n%@\n\n$MACROS:\n%@\n", vsCacheKey, fsCacheKey, macroString];
180 :
181 : OOLogIndent();
182 : shaderProgram = [OOShaderProgram shaderProgramWithVertexShader:vertexShader
183 : fragmentShader:fragmentShader
184 : vertexShaderName:vsName
185 : fragmentShaderName:fsName
186 : prefix:macroString
187 : attributeBindings:attributeBindings
188 : cacheKey:cacheKey];
189 : OOLogOutdent();
190 :
191 : // no reduced complexity mode now
192 : #if 0
193 : if (shaderProgram == nil)
194 : {
195 :
196 : BOOL canFallBack = ![modifiedMacros oo_boolForKey:@"OO_REDUCED_COMPLEXITY"];
197 : #ifndef NDEBUG
198 : if (gDebugFlags & DEBUG_NO_SHADER_FALLBACK) canFallBack = NO;
199 : #endif
200 : if (canFallBack)
201 : {
202 : OOLogWARN(@"shader.load.fullModeFailed", @"Could not build shader %@/%@ in full complexity mode, trying simple mode.", vsName, fsName);
203 :
204 : [modifiedMacros setObject:[NSNumber numberWithInt:1] forKey:@"OO_REDUCED_COMPLEXITY"];
205 : macroString = MacrosToString(modifiedMacros);
206 : cacheKey = [cacheKey stringByAppendingString:@"\n$SIMPLIFIED FALLBACK\n"];
207 :
208 : OOLogIndent();
209 : shaderProgram = [OOShaderProgram shaderProgramWithVertexShader:vertexShader
210 : fragmentShader:fragmentShader
211 : vertexShaderName:vsName
212 : fragmentShaderName:fsName
213 : prefix:macroString
214 : attributeBindings:attributeBindings
215 : cacheKey:cacheKey];
216 : OOLogOutdent();
217 :
218 : if (shaderProgram != nil)
219 : {
220 : OOLog(@"shader.load.fallbackSuccess", @"Simple mode fallback successful.");
221 : }
222 : }
223 : }
224 : #endif
225 :
226 : if (shaderProgram == nil)
227 : {
228 : OOLogERR(@"shader.load.failed", @"Could not build shader %@/%@.", vsName, fsName);
229 : }
230 : }
231 : else
232 : {
233 : OOLog(@"shader.load.noShader", @"***** Error: no vertex or fragment shader specified in shader dictionary:\n%@", configuration);
234 : }
235 :
236 : OK = (shaderProgram != nil);
237 : if (OK) [shaderProgram retain];
238 : }
239 :
240 : if (OK)
241 : {
242 : // Load uniforms and textures, which are a flavour of uniform for our purpose.
243 : NSDictionary *uniformDefs = [configuration oo_dictionaryForKey:kOOUniformsKey];
244 :
245 : NSArray *textureArray = [configuration oo_arrayForKey:kOOTextureObjectsKey];
246 : if (textureArray == nil)
247 : {
248 : NSArray *textureSpecs = [configuration oo_arrayForKey:kOOTexturesKey];
249 : if (textureSpecs != nil)
250 : {
251 : textureArray = [self loadTexturesFromArray:textureSpecs unitCount:textureUnits];
252 : }
253 : }
254 :
255 : uniforms = [[NSMutableDictionary alloc] initWithCapacity:[uniformDefs count] + [textureArray count]];
256 : [self addUniformsFromDictionary:uniformDefs withBindingTarget:target];
257 : [self addTexturesFromArray:textureArray unitCount:textureUnits];
258 : }
259 :
260 : if (OK)
261 : {
262 : // write gloss and gamma correction preference to the uniforms dictionary
263 :
264 : if (![uniforms objectForKey:@"uGloss"])
265 : {
266 : float gloss = OOClamp_0_1_f([configuration oo_floatForKey:@"gloss" defaultValue:0.5f]);
267 : [self setUniform:@"uGloss" floatValue:gloss];
268 : }
269 :
270 : if (![uniforms objectForKey:@"uGammaCorrect"])
271 : {
272 : BOOL gammaCorrect = [configuration oo_boolForKey:@"gamma_correct"
273 : defaultValue:![[NSUserDefaults standardUserDefaults] boolForKey:@"no-gamma-correct"]];
274 : [self setUniform:@"uGammaCorrect" floatValue:(float)gammaCorrect];
275 : }
276 : }
277 :
278 : if (!OK)
279 : {
280 : [self release];
281 : self = nil;
282 : }
283 : return self;
284 : }
285 :
286 :
287 : - (void)dealloc
288 : {
289 : uint32_t i;
290 :
291 : [self willDealloc];
292 :
293 : [shaderProgram release];
294 : [uniforms release];
295 :
296 : if (textures != NULL)
297 : {
298 : for (i = 0; i != texCount; ++i)
299 : {
300 : [textures[i] release];
301 : }
302 : free(textures);
303 : }
304 :
305 : [bindingTarget release];
306 :
307 : [super dealloc];
308 : }
309 :
310 :
311 : - (BOOL)bindUniform:(NSString *)uniformName
312 : toObject:(id<OOWeakReferenceSupport>)source
313 : property:(SEL)selector
314 : convertOptions:(OOUniformConvertOptions)options
315 : {
316 : OOShaderUniform *uniform = nil;
317 :
318 : if (uniformName == nil) return NO;
319 :
320 : uniform = [[OOShaderUniform alloc] initWithName:uniformName
321 : shaderProgram:shaderProgram
322 : boundToObject:source
323 : property:selector
324 : convertOptions:options];
325 : if (uniform != nil)
326 : {
327 : OOLog(@"shader.uniform.set", @"Set up uniform %@", uniform);
328 : [uniforms setObject:uniform forKey:uniformName];
329 : [uniform release];
330 : return YES;
331 : }
332 : else
333 : {
334 : OOLog(@"shader.uniform.unSet", @"Did not set uniform \"%@\"", uniformName);
335 : [uniforms removeObjectForKey:uniformName];
336 : return NO;
337 : }
338 : }
339 :
340 :
341 : - (BOOL)bindSafeUniform:(NSString *)uniformName
342 : toObject:(id<OOWeakReferenceSupport>)target
343 : propertyNamed:(NSString *)property
344 : convertOptions:(OOUniformConvertOptions)options
345 : {
346 : SEL selector = NULL;
347 :
348 : selector = NSSelectorFromString(property);
349 :
350 : if (selector != NULL && OOUniformBindingPermitted(property, target))
351 : {
352 : return [self bindUniform:uniformName
353 : toObject:target
354 : property:selector
355 : convertOptions:options];
356 : }
357 : else
358 : {
359 : OOLog(@"shader.uniform.unpermittedMethod", @"Did not bind uniform \"%@\" to property -[%@ %@] - unpermitted method.", uniformName, [target class], property);
360 : }
361 :
362 : return NO;
363 : }
364 :
365 :
366 : - (void)setUniform:(NSString *)uniformName intValue:(int)value
367 : {
368 : OOShaderUniform *uniform = nil;
369 :
370 : if (uniformName == nil) return;
371 :
372 : uniform = [[OOShaderUniform alloc] initWithName:uniformName
373 : shaderProgram:shaderProgram
374 : intValue:value];
375 : if (uniform != nil)
376 : {
377 : OOLog(@"shader.uniform.set", @"Set up uniform %@", uniform);
378 : [uniforms setObject:uniform forKey:uniformName];
379 : [uniform release];
380 : }
381 : else
382 : {
383 : OOLog(@"shader.uniform.unSet", @"Did not set uniform \"%@\"", uniformName);
384 : [uniforms removeObjectForKey:uniformName];
385 : }
386 : }
387 :
388 :
389 : - (void)setUniform:(NSString *)uniformName floatValue:(float)value
390 : {
391 : OOShaderUniform *uniform = nil;
392 :
393 : if (uniformName == nil) return;
394 :
395 : uniform = [[OOShaderUniform alloc] initWithName:uniformName
396 : shaderProgram:shaderProgram
397 : floatValue:value];
398 : if (uniform != nil)
399 : {
400 : OOLog(@"shader.uniform.set", @"Set up uniform %@", uniform);
401 : [uniforms setObject:uniform forKey:uniformName];
402 : [uniform release];
403 : }
404 : else
405 : {
406 : OOLog(@"shader.uniform.unSet", @"Did not set uniform \"%@\"", uniformName);
407 : [uniforms removeObjectForKey:uniformName];
408 : }
409 : }
410 :
411 :
412 : - (void)setUniform:(NSString *)uniformName vectorValue:(GLfloat[4])value
413 : {
414 : OOShaderUniform *uniform = nil;
415 :
416 : if (uniformName == nil) return;
417 :
418 : uniform = [[OOShaderUniform alloc] initWithName:uniformName
419 : shaderProgram:shaderProgram
420 : vectorValue:value];
421 : if (uniform != nil)
422 : {
423 : OOLog(@"shader.uniform.set", @"Set up uniform %@", uniform);
424 : [uniforms setObject:uniform forKey:uniformName];
425 : [uniform release];
426 : }
427 : else
428 : {
429 : OOLog(@"shader.uniform.unSet", @"Did not set uniform \"%@\"", uniformName);
430 : [uniforms removeObjectForKey:uniformName];
431 : }
432 : }
433 :
434 :
435 : - (void)setUniform:(NSString *)uniformName vectorObjectValue:(id)value
436 : {
437 : if (uniformName == nil) return;
438 :
439 : GLfloat vecArray[4];
440 : if ([value isKindOfClass:[NSArray class]] && [value count] == 4)
441 : {
442 : for (unsigned i = 0; i < 4; i++)
443 : {
444 : vecArray[i] = OOFloatFromObject([value objectAtIndex:i], 0.0f);
445 : }
446 : }
447 : else
448 : {
449 : Vector vec = OOVectorFromObject(value, kZeroVector);
450 : vecArray[0] = vec.x;
451 : vecArray[1] = vec.y;
452 : vecArray[2] = vec.z;
453 : vecArray[3] = 1.0;
454 : }
455 :
456 : OOShaderUniform *uniform = [[OOShaderUniform alloc] initWithName:uniformName
457 : shaderProgram:shaderProgram
458 : vectorValue:vecArray];
459 : if (uniform != nil)
460 : {
461 : OOLog(@"shader.uniform.set", @"Set up uniform %@", uniform);
462 : [uniforms setObject:uniform forKey:uniformName];
463 : [uniform release];
464 : }
465 : else
466 : {
467 : OOLog(@"shader.uniform.unSet", @"Did not set uniform \"%@\"", uniformName);
468 : [uniforms removeObjectForKey:uniformName];
469 : }
470 : }
471 :
472 :
473 : - (void)setUniform:(NSString *)uniformName quaternionValue:(Quaternion)value asMatrix:(BOOL)asMatrix
474 : {
475 : OOShaderUniform *uniform = nil;
476 :
477 : if (uniformName == nil) return;
478 :
479 : uniform = [[OOShaderUniform alloc] initWithName:uniformName
480 : shaderProgram:shaderProgram
481 : quaternionValue:value
482 : asMatrix:asMatrix];
483 : if (uniform != nil)
484 : {
485 : OOLog(@"shader.uniform.set", @"Set up uniform %@", uniform);
486 : [uniforms setObject:uniform forKey:uniformName];
487 : [uniform release];
488 : }
489 : else
490 : {
491 : OOLog(@"shader.uniform.unSet", @"Did not set uniform \"%@\"", uniformName);
492 : [uniforms removeObjectForKey:uniformName];
493 : }
494 : }
495 :
496 :
497 : -(void)addUniformsFromDictionary:(NSDictionary *)uniformDefs withBindingTarget:(id<OOWeakReferenceSupport>)target
498 : {
499 : NSEnumerator *uniformEnum = nil;
500 : NSString *name = nil;
501 : id definition = nil;
502 : id value = nil;
503 : NSString *binding = nil;
504 : NSString *type = nil;
505 : GLfloat floatValue;
506 : BOOL gotValue;
507 : OOUniformConvertOptions convertOptions;
508 : BOOL quatAsMatrix = YES;
509 : GLfloat scale = 1.0;
510 : uint32_t randomSeed;
511 : RANROTSeed savedSeed;
512 : NSArray *keys = nil;
513 :
514 : if ([target respondsToSelector:@selector(randomSeedForShaders)])
515 : {
516 : randomSeed = [(id)target randomSeedForShaders];
517 : }
518 : else
519 : {
520 : randomSeed = (uint32_t)(uintptr_t)self;
521 : }
522 : savedSeed = RANROTGetFullSeed();
523 : ranrot_srand(randomSeed);
524 :
525 : keys = [[uniformDefs allKeys] sortedArrayUsingSelector:@selector(compare:)];
526 : for (uniformEnum = [keys objectEnumerator]; (name = [uniformEnum nextObject]); )
527 : {
528 : gotValue = NO;
529 : definition = [uniformDefs objectForKey:name];
530 :
531 : type = nil;
532 : value = nil;
533 : binding = nil;
534 :
535 : if ([definition isKindOfClass:[NSDictionary class]])
536 : {
537 : value = [(NSDictionary *)definition objectForKey:@"value"];
538 : binding = [(NSDictionary *)definition oo_stringForKey:@"binding"];
539 : type = [(NSDictionary *)definition oo_stringForKey:@"type"];
540 : scale = [(NSDictionary *)definition oo_floatForKey:@"scale" defaultValue:1.0];
541 : if (type == nil)
542 : {
543 : if (value == nil && binding != nil) type = @"binding";
544 : else type = @"float";
545 : }
546 : }
547 : else if ([definition isKindOfClass:[NSNumber class]])
548 : {
549 : value = definition;
550 : type = @"float";
551 : }
552 : else if ([definition isKindOfClass:[NSString class]])
553 : {
554 : if (OOIsNumberLiteral(definition, NO))
555 : {
556 : value = definition;
557 : type = @"float";
558 : }
559 : else
560 : {
561 : binding = definition;
562 : type = @"binding";
563 : }
564 : }
565 : else if ([definition isKindOfClass:[NSArray class]])
566 : {
567 : binding = definition;
568 : type = @"vector";
569 : }
570 :
571 : // Transform random values to concrete values
572 : if ([type isEqualToString:@"randomFloat"])
573 : {
574 : type = @"float";
575 : value = [NSNumber numberWithFloat:randf() * scale];
576 : }
577 : else if ([type isEqualToString:@"randomUnitVector"])
578 : {
579 : type = @"vector";
580 : value = OOPropertyListFromVector(vector_multiply_scalar(OORandomUnitVector(), scale));
581 : }
582 : else if ([type isEqualToString:@"randomVectorSpatial"])
583 : {
584 : type = @"vector";
585 : value = OOPropertyListFromVector(OOVectorRandomSpatial(scale));
586 : }
587 : else if ([type isEqualToString:@"randomVectorRadial"])
588 : {
589 : type = @"vector";
590 : value = OOPropertyListFromVector(OOVectorRandomRadial(scale));
591 : }
592 : else if ([type isEqualToString:@"randomQuaternion"])
593 : {
594 : type = @"quaternion";
595 : value = OOPropertyListFromQuaternion(OORandomQuaternion());
596 : }
597 :
598 : if ([type isEqualToString:@"float"] || [type isEqualToString:@"real"])
599 : {
600 : gotValue = YES;
601 : if ([value respondsToSelector:@selector(floatValue)]) floatValue = [value floatValue];
602 : else if ([value respondsToSelector:@selector(doubleValue)]) floatValue = [value doubleValue];
603 : else if ([value respondsToSelector:@selector(intValue)]) floatValue = [value intValue];
604 : else gotValue = NO;
605 :
606 : if (gotValue)
607 : {
608 : [self setUniform:name floatValue:floatValue];
609 : }
610 : }
611 : else if ([type isEqualToString:@"int"] || [type isEqualToString:@"integer"] || [type isEqualToString:@"texture"])
612 : {
613 : /* "texture" is allowed as a synonym for "int" because shader
614 : uniforms are mapped to texture units by specifying an integer
615 : index.
616 : uniforms = { diffuseMap = { type = texture; value = 0; }; };
617 : means "bind uniform diffuseMap to texture unit 0" (which will
618 : have the first texture in the textures array).
619 : */
620 : if ([value respondsToSelector:@selector(intValue)])
621 : {
622 : [self setUniform:name intValue:[value intValue]];
623 : gotValue = YES;
624 : }
625 : }
626 : else if ([type isEqualToString:@"vector"])
627 : {
628 : [self setUniform:name vectorObjectValue:value];
629 : gotValue = YES;
630 : }
631 : else if ([type isEqualToString:@"quaternion"])
632 : {
633 : if ([definition isKindOfClass:[NSDictionary class]])
634 : {
635 : quatAsMatrix = [definition oo_boolForKey:@"asMatrix" defaultValue:quatAsMatrix];
636 : }
637 : [self setUniform:name
638 : quaternionValue:OOQuaternionFromObject(value, kIdentityQuaternion)
639 : asMatrix:quatAsMatrix];
640 : gotValue = YES;
641 : }
642 : else if (target != nil && [type isEqualToString:@"binding"])
643 : {
644 : if ([definition isKindOfClass:[NSDictionary class]])
645 : {
646 : convertOptions = 0;
647 : if ([definition oo_boolForKey:@"clamped" defaultValue:NO]) convertOptions |= kOOUniformConvertClamp;
648 : if ([definition oo_boolForKey:@"normalized" defaultValue:[definition oo_boolForKey:@"normalised" defaultValue:NO]])
649 : {
650 : convertOptions |= kOOUniformConvertNormalize;
651 : }
652 : if ([definition oo_boolForKey:@"asMatrix" defaultValue:YES]) convertOptions |= kOOUniformConvertToMatrix;
653 : if (![definition oo_boolForKey:@"bindToSubentity" defaultValue:NO]) convertOptions |= kOOUniformBindToSuperTarget;
654 : }
655 : else
656 : {
657 : convertOptions = kOOUniformConvertDefaults;
658 : }
659 :
660 : [self bindSafeUniform:name toObject:target propertyNamed:binding convertOptions:convertOptions];
661 : gotValue = YES;
662 : }
663 :
664 : if (!gotValue)
665 : {
666 : OOLog(@"shader.uniform.badDescription", @"----- Warning: could not bind uniform \"%@\" for target %@ -- could not interpret definition:\n%@", name, target, definition);
667 : }
668 : }
669 :
670 : RANROTSetFullSeed(savedSeed);
671 : }
672 :
673 :
674 : - (BOOL)doApply
675 : {
676 : NSEnumerator *uniformEnum = nil;
677 : OOShaderUniform *uniform = nil;
678 : uint32_t i;
679 :
680 : OO_ENTER_OPENGL();
681 :
682 : [super doApply];
683 : [shaderProgram apply];
684 :
685 : for (i = 0; i != texCount; ++i)
686 : {
687 : OOGL(glActiveTextureARB(GL_TEXTURE0_ARB + i));
688 : [textures[i] apply];
689 : }
690 : if (texCount > 1) OOGL(glActiveTextureARB(GL_TEXTURE0_ARB));
691 :
692 : @try
693 : {
694 : for (uniformEnum = [uniforms objectEnumerator]; (uniform = [uniformEnum nextObject]); )
695 : {
696 : [uniform apply];
697 : }
698 : }
699 : @catch (id exception) {}
700 :
701 : return YES;
702 : }
703 :
704 :
705 : - (void)ensureFinishedLoading
706 : {
707 : uint32_t i;
708 :
709 : if (textures != NULL)
710 : {
711 : for (i = 0; i != texCount; ++i)
712 : {
713 : [textures[i] ensureFinishedLoading];
714 : }
715 : }
716 : }
717 :
718 :
719 : - (BOOL) isFinishedLoading
720 : {
721 : uint32_t i;
722 :
723 : if (textures != NULL)
724 : {
725 : for (i = 0; i != texCount; ++i)
726 : {
727 : if (![textures[i] isFinishedLoading]) return NO;
728 : }
729 : }
730 :
731 : return YES;
732 : }
733 :
734 :
735 : - (void)unapplyWithNext:(OOMaterial *)next
736 : {
737 : uint32_t i, count;
738 :
739 : if (![next isKindOfClass:[OOShaderMaterial class]]) // Avoid redundant state change
740 : {
741 : OO_ENTER_OPENGL();
742 : [OOShaderProgram applyNone];
743 :
744 : /* BUG: unapplyWithNext: was failing to clear texture state. If a
745 : shader material was followed by a basic material (with no texture),
746 : the shader's #0 texture would be used.
747 : It is necessary to clear at least one texture for the case where a
748 : shader material with textures is followed by a shader material
749 : without textures, then a basic material.
750 : -- Ahruman 2007-08-13
751 : */
752 : count = texCount ? texCount : 1;
753 : for (i = 0; i != count; ++i)
754 : {
755 : OOGL(glActiveTextureARB(GL_TEXTURE0_ARB + i));
756 : [OOTexture applyNone];
757 : }
758 : if (count != 1) OOGL(glActiveTextureARB(GL_TEXTURE0_ARB));
759 : }
760 : }
761 :
762 :
763 : - (void)setBindingTarget:(id<OOWeakReferenceSupport>)target
764 : {
765 : [[uniforms allValues] makeObjectsPerformSelector:@selector(setBindingTarget:) withObject:target];
766 : [bindingTarget release];
767 : bindingTarget = [target weakRetain];
768 : }
769 :
770 :
771 : - (BOOL) permitSpecular
772 : {
773 : return YES;
774 : }
775 :
776 :
777 : #ifndef NDEBUG
778 : - (NSSet *) allTextures
779 : {
780 : return [NSSet setWithObjects:textures count:texCount];
781 : }
782 : #endif
783 :
784 : @end
785 :
786 :
787 : @implementation OOShaderMaterial (OOPrivate)
788 :
789 : - (NSArray *) loadTexturesFromArray:(NSArray *)textureSpecs unitCount:(GLuint)max
790 : {
791 : GLuint i, count = (GLuint)MIN([textureSpecs count], (NSUInteger)max);
792 : NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
793 :
794 : for (i = 0; i < count; i++)
795 : {
796 : id textureSpec = [textureSpecs objectAtIndex:i];
797 : OOTexture *texture = [OOTexture textureWithConfiguration:textureSpec];
798 : if (texture == nil) texture = [OOTexture nullTexture];
799 : [result addObject:texture];
800 : }
801 :
802 : return result;
803 : }
804 :
805 :
806 : - (void) addTexturesFromArray:(NSArray *)textureObjects unitCount:(GLuint)max
807 : {
808 : // Allocate space for texture object name array
809 : texCount = (uint32_t)MIN([textureObjects count], (NSUInteger)max);
810 : if (texCount == 0) return;
811 :
812 : textures = malloc(texCount * sizeof *textures);
813 : if (textures == NULL)
814 : {
815 : texCount = 0;
816 : return;
817 : }
818 :
819 : // Set up texture object names and appropriate uniforms
820 : unsigned i;
821 : for (i = 0; i != texCount; ++i)
822 : {
823 : textures[i] = [textureObjects objectAtIndex:i];
824 : [textures[i] retain];
825 : }
826 : }
827 :
828 : @end
829 :
830 :
831 : static NSString *MacrosToString(NSDictionary *macros)
832 : {
833 : NSMutableString *result = nil;
834 : NSEnumerator *macroEnum = nil;
835 : id key = nil, value = nil;
836 :
837 : if (macros == nil) return nil;
838 :
839 : result = [NSMutableString string];
840 : for (macroEnum = [macros keyEnumerator]; (key = [macroEnum nextObject]); )
841 : {
842 : if (![key isKindOfClass:[NSString class]]) continue;
843 : value = [macros objectForKey:key];
844 :
845 : [result appendFormat:@"#define %@ %@\n", key, value];
846 : }
847 :
848 : if ([result length] == 0) return nil;
849 : [result appendString:@"\n\n"];
850 : return result;
851 : }
852 :
853 : #endif
854 :
855 :
856 : /* Attempt to load fragment or vertex shader source from a file.
857 : Returns YES if source was loaded or no shader was specified, and NO if an
858 : external shader was specified but could not be found.
859 : */
860 0 : static BOOL GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult)
861 : {
862 : NSString *result = nil;
863 : NSArray *extensions = nil;
864 : NSEnumerator *extEnum = nil;
865 : NSString *extension = nil;
866 : NSString *nameWithExtension = nil;
867 :
868 : if (fileName == nil) return YES; // It's OK for one or the other of the shaders to be undefined.
869 :
870 : result = [ResourceManager stringFromFilesNamed:fileName inFolder:@"Shaders"];
871 : if (result == nil)
872 : {
873 : extensions = [NSArray arrayWithObjects:shaderType, [shaderType substringToIndex:4], nil]; // vertex and vert, or fragment and frag
874 :
875 : // Futureproofing -- in future, we may wish to support automatic selection between supported shader languages.
876 : if (![fileName pathHasExtensionInArray:extensions])
877 : {
878 : for (extEnum = [extensions objectEnumerator]; (extension = [extEnum nextObject]); )
879 : {
880 : nameWithExtension = [fileName stringByAppendingPathExtension:extension];
881 : result = [ResourceManager stringFromFilesNamed:nameWithExtension
882 : inFolder:@"Shaders"];
883 : if (result != nil) break;
884 : }
885 : }
886 : if (result == nil)
887 : {
888 : OOLog(kOOLogFileNotFound, @"GLSL ERROR: failed to find %@ program %@.", shaderType, fileName);
889 : return NO;
890 : }
891 : }
892 :
893 : if (outResult != NULL) *outResult = result;
894 : return YES;
895 : }
|