Oolite 1.91.0.7604-240417-a536cbe
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 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";
586 }
587 else if ([type isEqualToString:@"randomVectorRadial"])
588 {
589 type = @"vector";
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
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 {
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));
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
831static 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*/
860static 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}
NSUInteger gDebugFlags
Definition main.m:7
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)