Oolite
Loading...
Searching...
No Matches
OOShaderMaterial.m
Go to the documentation of this file.
1/*
2
3OOShaderMaterial.m
4
5
6Copyright (C) 2007-2013 Jens Ayton
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in all
16copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
25
26*/
27
28
29#import "OOShaderMaterial.h"
30
31#if OO_SHADERS
32
33#import "ResourceManager.h"
34#import "OOShaderUniform.h"
37#import "OOShaderProgram.h"
38#import "OOTexture.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
48NSString * const kOOVertexShaderSourceKey = @"_oo_vertex_shader_source";
49NSString * const kOOVertexShaderNameKey = @"vertex_shader";
50NSString * const kOOFragmentShaderSourceKey = @"_oo_fragment_shader_source";
51NSString * const kOOFragmentShaderNameKey = @"fragment_shader";
52NSString * const kOOTexturesKey = @"textures";
53NSString * const kOOTextureObjectsKey = @"_oo_texture_objects";
54NSString * const kOOUniformsKey = @"uniforms";
55NSString * const kOOIsSynthesizedMaterialConfigurationKey = @"_oo_is_synthesized_config";
56NSString * const kOOIsSynthesizedMaterialMacrosKey = @"_oo_synthesized_material_macros";
57
58
59static BOOL GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult);
60static 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;
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 NSString *name = nil;
500 id definition = nil;
501 id value = nil;
502 NSString *binding = nil;
503 NSString *type = nil;
504 GLfloat floatValue;
505 BOOL gotValue;
506 OOUniformConvertOptions convertOptions;
507 BOOL quatAsMatrix = YES;
508 GLfloat scale = 1.0;
509 uint32_t randomSeed;
510 RANROTSeed savedSeed;
511 NSArray *keys = nil;
512
513 if ([target respondsToSelector:@selector(randomSeedForShaders)])
514 {
515 randomSeed = [(id)target randomSeedForShaders];
516 }
517 else
518 {
519 randomSeed = (uint32_t)(uintptr_t)self;
520 }
521 savedSeed = RANROTGetFullSeed();
522 ranrot_srand(randomSeed);
523
524 keys = [[uniformDefs allKeys] sortedArrayUsingSelector:@selector(compare:)];
525 foreach (name, keys)
526 {
527 gotValue = NO;
528 definition = [uniformDefs objectForKey:name];
529
530 type = nil;
531 value = nil;
532 binding = nil;
533
534 if ([definition isKindOfClass:[NSDictionary class]])
535 {
536 value = [(NSDictionary *)definition objectForKey:@"value"];
537 binding = [(NSDictionary *)definition oo_stringForKey:@"binding"];
538 type = [(NSDictionary *)definition oo_stringForKey:@"type"];
539 scale = [(NSDictionary *)definition oo_floatForKey:@"scale" defaultValue:1.0];
540 if (type == nil)
541 {
542 if (value == nil && binding != nil) type = @"binding";
543 else type = @"float";
544 }
545 }
546 else if ([definition isKindOfClass:[NSNumber class]])
547 {
548 value = definition;
549 type = @"float";
550 }
551 else if ([definition isKindOfClass:[NSString class]])
552 {
553 if (OOIsNumberLiteral(definition, NO))
554 {
555 value = definition;
556 type = @"float";
557 }
558 else
559 {
560 binding = definition;
561 type = @"binding";
562 }
563 }
564 else if ([definition isKindOfClass:[NSArray class]])
565 {
566 binding = definition;
567 type = @"vector";
568 }
569
570 // Transform random values to concrete values
571 if ([type isEqualToString:@"randomFloat"])
572 {
573 type = @"float";
574 value = [NSNumber numberWithFloat:randf() * scale];
575 }
576 else if ([type isEqualToString:@"randomUnitVector"])
577 {
578 type = @"vector";
579 value = OOPropertyListFromVector(vector_multiply_scalar(OORandomUnitVector(), scale));
580 }
581 else if ([type isEqualToString:@"randomVectorSpatial"])
582 {
583 type = @"vector";
585 }
586 else if ([type isEqualToString:@"randomVectorRadial"])
587 {
588 type = @"vector";
590 }
591 else if ([type isEqualToString:@"randomQuaternion"])
592 {
593 type = @"quaternion";
594 value = OOPropertyListFromQuaternion(OORandomQuaternion());
595 }
596
597 if ([type isEqualToString:@"float"] || [type isEqualToString:@"real"])
598 {
599 gotValue = YES;
600 if ([value respondsToSelector:@selector(floatValue)]) floatValue = [value floatValue];
601 else if ([value respondsToSelector:@selector(doubleValue)]) floatValue = [value doubleValue];
602 else if ([value respondsToSelector:@selector(intValue)]) floatValue = [value intValue];
603 else gotValue = NO;
604
605 if (gotValue)
606 {
607 [self setUniform:name floatValue:floatValue];
608 }
609 }
610 else if ([type isEqualToString:@"int"] || [type isEqualToString:@"integer"] || [type isEqualToString:@"texture"])
611 {
612 /* "texture" is allowed as a synonym for "int" because shader
613 uniforms are mapped to texture units by specifying an integer
614 index.
615 uniforms = { diffuseMap = { type = texture; value = 0; }; };
616 means "bind uniform diffuseMap to texture unit 0" (which will
617 have the first texture in the textures array).
618 */
619 if ([value respondsToSelector:@selector(intValue)])
620 {
621 [self setUniform:name intValue:[value intValue]];
622 gotValue = YES;
623 }
624 }
625 else if ([type isEqualToString:@"vector"])
626 {
627 [self setUniform:name vectorObjectValue:value];
628 gotValue = YES;
629 }
630 else if ([type isEqualToString:@"quaternion"])
631 {
632 if ([definition isKindOfClass:[NSDictionary class]])
633 {
634 quatAsMatrix = [definition oo_boolForKey:@"asMatrix" defaultValue:quatAsMatrix];
635 }
636 [self setUniform:name
637 quaternionValue:OOQuaternionFromObject(value, kIdentityQuaternion)
638 asMatrix:quatAsMatrix];
639 gotValue = YES;
640 }
641 else if (target != nil && [type isEqualToString:@"binding"])
642 {
643 if ([definition isKindOfClass:[NSDictionary class]])
644 {
645 convertOptions = 0;
646 if ([definition oo_boolForKey:@"clamped" defaultValue:NO]) convertOptions |= kOOUniformConvertClamp;
647 if ([definition oo_boolForKey:@"normalized" defaultValue:[definition oo_boolForKey:@"normalised" defaultValue:NO]])
648 {
649 convertOptions |= kOOUniformConvertNormalize;
650 }
651 if ([definition oo_boolForKey:@"asMatrix" defaultValue:YES]) convertOptions |= kOOUniformConvertToMatrix;
652 if (![definition oo_boolForKey:@"bindToSubentity" defaultValue:NO]) convertOptions |= kOOUniformBindToSuperTarget;
653 }
654 else
655 {
656 convertOptions = kOOUniformConvertDefaults;
657 }
658
659 [self bindSafeUniform:name toObject:target propertyNamed:binding convertOptions:convertOptions];
660 gotValue = YES;
661 }
662
663 if (!gotValue)
664 {
665 OOLog(@"shader.uniform.badDescription", @"----- Warning: could not bind uniform \"%@\" for target %@ -- could not interpret definition:\n%@", name, target, definition);
666 }
667 }
668
669 RANROTSetFullSeed(savedSeed);
670}
671
672
673- (BOOL)doApply
674{
675 OOShaderUniform *uniform = nil;
676 uint32_t i;
677
679
680 [super doApply];
681 [shaderProgram apply];
682
683 for (i = 0; i != texCount; ++i)
684 {
685 OOGL(glActiveTextureARB(GL_TEXTURE0_ARB + i));
686 [textures[i] apply];
687 }
688 if (texCount > 1) OOGL(glActiveTextureARB(GL_TEXTURE0_ARB));
689
690 @try
691 {
692 foreach (uniform, [uniforms allValues])
693 {
694 [uniform apply];
695 }
696 }
697 @catch (id exception) {}
698
699 return YES;
700}
701
702
703- (void)ensureFinishedLoading
704{
705 uint32_t i;
706
707 if (textures != NULL)
708 {
709 for (i = 0; i != texCount; ++i)
710 {
711 [textures[i] ensureFinishedLoading];
712 }
713 }
714}
715
716
717- (BOOL) isFinishedLoading
718{
719 uint32_t i;
720
721 if (textures != NULL)
722 {
723 for (i = 0; i != texCount; ++i)
724 {
725 if (![textures[i] isFinishedLoading]) return NO;
726 }
727 }
728
729 return YES;
730}
731
732
733- (void)unapplyWithNext:(OOMaterial *)next
734{
735 uint32_t i, count;
736
737 if (![next isKindOfClass:[OOShaderMaterial class]]) // Avoid redundant state change
738 {
740 [OOShaderProgram applyNone];
741
742 /* BUG: unapplyWithNext: was failing to clear texture state. If a
743 shader material was followed by a basic material (with no texture),
744 the shader's #0 texture would be used.
745 It is necessary to clear at least one texture for the case where a
746 shader material with textures is followed by a shader material
747 without textures, then a basic material.
748 -- Ahruman 2007-08-13
749 */
750 count = texCount ? texCount : 1;
751 for (i = 0; i != count; ++i)
752 {
753 OOGL(glActiveTextureARB(GL_TEXTURE0_ARB + i));
755 }
756 if (count != 1) OOGL(glActiveTextureARB(GL_TEXTURE0_ARB));
757 }
758}
759
760
761- (void)setBindingTarget:(id<OOWeakReferenceSupport>)target
762{
763 [[uniforms allValues] makeObjectsPerformSelector:@selector(setBindingTarget:) withObject:target];
764 [bindingTarget release];
765 bindingTarget = [target weakRetain];
766}
767
768
769- (BOOL) permitSpecular
770{
771 return YES;
772}
773
774
775#ifndef NDEBUG
776- (NSSet *) allTextures
777{
778 return [NSSet setWithObjects:textures count:texCount];
779}
780#endif
781
782@end
783
784
785@implementation OOShaderMaterial (OOPrivate)
786
787- (NSArray *) loadTexturesFromArray:(NSArray *)textureSpecs unitCount:(GLuint)max
788{
789 GLuint i, count = (GLuint)MIN([textureSpecs count], (NSUInteger)max);
790 NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
791
792 for (i = 0; i < count; i++)
793 {
794 id textureSpec = [textureSpecs objectAtIndex:i];
795 OOTexture *texture = [OOTexture textureWithConfiguration:textureSpec];
796 if (texture == nil) texture = [OOTexture nullTexture];
797 [result addObject:texture];
798 }
799
800 return result;
801}
802
803
804- (void) addTexturesFromArray:(NSArray *)textureObjects unitCount:(GLuint)max
805{
806 // Allocate space for texture object name array
807 texCount = (uint32_t)MIN([textureObjects count], (NSUInteger)max);
808 if (texCount == 0) return;
809
810 textures = malloc(texCount * sizeof *textures);
811 if (textures == NULL)
812 {
813 texCount = 0;
814 return;
815 }
816
817 // Set up texture object names and appropriate uniforms
818 unsigned i;
819 for (i = 0; i != texCount; ++i)
820 {
821 textures[i] = [textureObjects objectAtIndex:i];
822 [textures[i] retain];
823 }
824}
825
826@end
827
828
829static NSString *MacrosToString(NSDictionary *macros)
830{
831 NSMutableString *result = nil;
832 id key = nil, value = nil;
833
834 if (macros == nil) return nil;
835
836 result = [NSMutableString string];
837 foreachkey (key, macros)
838 {
839 if (![key isKindOfClass:[NSString class]]) continue;
840 value = [macros objectForKey:key];
841
842 [result appendFormat:@"#define %@ %@\n", key, value];
843 }
844
845 if ([result length] == 0) return nil;
846 [result appendString:@"\n\n"];
847 return result;
848}
849
850#endif
851
852
853/* Attempt to load fragment or vertex shader source from a file.
854 Returns YES if source was loaded or no shader was specified, and NO if an
855 external shader was specified but could not be found.
856*/
857static BOOL GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult)
858{
859 NSString *result = nil;
860 NSArray *extensions = nil;
861 NSString *extension = nil;
862 NSString *nameWithExtension = nil;
863
864 if (fileName == nil) return YES; // It's OK for one or the other of the shaders to be undefined.
865
866 result = [ResourceManager stringFromFilesNamed:fileName inFolder:@"Shaders"];
867 if (result == nil)
868 {
869 extensions = [NSArray arrayWithObjects:shaderType, [shaderType substringToIndex:4], nil]; // vertex and vert, or fragment and frag
870
871 // Futureproofing -- in future, we may wish to support automatic selection between supported shader languages.
872 if (![fileName pathHasExtensionInArray:extensions])
873 {
874 foreach (extension, extensions)
875 {
876 nameWithExtension = [fileName stringByAppendingPathExtension:extension];
877 result = [ResourceManager stringFromFilesNamed:nameWithExtension
878 inFolder:@"Shaders"];
879 if (result != nil) break;
880 }
881 }
882 if (result == nil)
883 {
884 OOLog(kOOLogFileNotFound, @"GLSL ERROR: failed to find %@ program %@.", shaderType, fileName);
885 return NO;
886 }
887 }
888
889 if (outResult != NULL) *outResult = result;
890 return YES;
891}
NSUInteger gDebugFlags
Definition main.m:7
#define foreachkey(VAR, DICT)
Definition OOCocoa.h:353
NSDictionary * OOPropertyListFromVector(Vector value)
Vector OOVectorFromObject(id object, Vector defaultValue)
NSDictionary * OOPropertyListFromQuaternion(Quaternion value)
float OOFloatFromObject(id object, float defaultValue)
@ DEBUG_NO_SHADER_FALLBACK
BOOL OOIsNumberLiteral(NSString *string, BOOL allowSpaces)
#define OOLogWARN(class, format,...)
Definition OOLogging.h:113
#define OOLogERR(class, format,...)
Definition OOLogging.h:112
void OOLogOutdent(void)
Definition OOLogging.m:376
#define OOLog(class, format,...)
Definition OOLogging.h:88
NSString *const kOOLogFileNotFound
Definition OOLogging.m:652
void OOLogIndent(void)
Definition OOLogging.m:366
#define OO_ENTER_OPENGL()
#define MIN(A, B)
Definition OOMaths.h:111
#define OOGL(statement)
Definition OOOpenGL.h:251
unsigned count
return nil
static BOOL GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult)
const Vector kZeroVector
Definition OOVector.m:28
Vector OORandomUnitVector(void)
Definition OOVector.m:83
Vector OOVectorRandomRadial(OOScalar maxLength)
Definition OOVector.m:115
Vector OOVectorRandomSpatial(OOScalar maxLength)
Definition OOVector.m:99
BOOL OOUniformBindingPermitted(NSString *propertyName, id bindingTarget)
OOOpenGLExtensionManager * sharedManager()
void applyNone()
Definition OOTexture.m:275
id textureWithConfiguration:(id configuration)
Definition OOTexture.m:192
id nullTexture()
Definition OOTexture.m:211
NSString * stringFromFilesNamed:inFolder:(NSString *fileName,[inFolder] NSString *folderName)
RANROTSeed RANROTGetFullSeed(void)
void ranrot_srand(uint32_t seed)
void RANROTSetFullSeed(RANROTSeed seed)