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";
59static BOOL
GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult);
60static NSString *MacrosToString(NSDictionary *macros);
63@interface OOShaderMaterial (OOPrivate)
66- (NSArray *) loadTexturesFromArray:(NSArray *)textureSpecs unitCount:(GLuint)max;
69- (void) addTexturesFromArray:(NSArray *)textureObjects unitCount:(GLuint)max;
74@implementation OOShaderMaterial
76+ (BOOL)configurationDictionarySpecifiesShaderMaterial:(NSDictionary *)configuration
78 if (configuration ==
nil)
return NO;
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;
89+ (instancetype) shaderMaterialWithName:(NSString *)name
90 configuration:(NSDictionary *)configuration
91 macros:(NSDictionary *)macros
92 bindingTarget:(
id<OOWeakReferenceSupport>)target
94 return [[[
self alloc] initWithName:name configuration:configuration macros:macros bindingTarget:target] autorelease];
98- (id) initWithName:(NSString *)name
99 configuration:(NSDictionary *)configuration
100 macros:(NSDictionary *)macros
101 bindingTarget:(
id<OOWeakReferenceSupport>)target
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;
114 if (configuration ==
nil) OK = NO;
116 self = [
super initWithName:name configuration:configuration];
117 if (
self ==
nil) OK = NO;
121 modifiedMacros = macros ? [macros mutableCopy] : [[NSMutableDictionary alloc] init];
122 [modifiedMacros autorelease];
124 [modifiedMacros setObject:[NSNumber numberWithUnsignedInt:textureUnits]
125 forKey:@"OO_TEXTURE_UNIT_COUNT"];
128 macroString = MacrosToString(modifiedMacros);
133 vertexShader = [configuration oo_stringForKey:kOOVertexShaderSourceKey];
134 if (vertexShader ==
nil)
136 vsName = [configuration oo_stringForKey:kOOVertexShaderNameKey];
140 if (!
GetShaderSource(vsName,
@"vertex", macroString, &vertexShader)) OK = NO;
145 vsCacheKey = vertexShader;
151 fragmentShader = [configuration oo_stringForKey:kOOFragmentShaderSourceKey];
152 if (fragmentShader ==
nil)
154 fsName = [configuration oo_stringForKey:kOOFragmentShaderNameKey];
158 if (!
GetShaderSource(fsName,
@"fragment", macroString, &fragmentShader)) OK = NO;
163 fsCacheKey = fragmentShader;
169 if (vertexShader !=
nil || fragmentShader !=
nil)
171 static NSDictionary *attributeBindings =
nil;
172 if (attributeBindings ==
nil)
174 attributeBindings = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kTangentAttributeIndex]
176 [attributeBindings retain];
179 NSString *cacheKey = [NSString stringWithFormat:@"$w12:\n%@\n\n$w13:\n%@\n\n$w14:\n%@\n", vsCacheKey, fsCacheKey, macroString];
182 shaderProgram = [OOShaderProgram shaderProgramWithVertexShader:vertexShader
183 fragmentShader:fragmentShader
184 vertexShaderName:vsName
185 fragmentShaderName:fsName
187 attributeBindings:attributeBindings
193 if (shaderProgram ==
nil)
196 BOOL canFallBack = ![modifiedMacros oo_boolForKey:@"OO_REDUCED_COMPLEXITY"];
202 OOLogWARN(
@"shader.load.fullModeFailed",
@"Could not build shader %@/%@ in full complexity mode, trying simple mode.", vsName, fsName);
204 [modifiedMacros setObject:[NSNumber numberWithInt:1] forKey:@"OO_REDUCED_COMPLEXITY"];
205 macroString = MacrosToString(modifiedMacros);
206 cacheKey = [cacheKey stringByAppendingString:@"\n$w25 FALLBACK\n"];
209 shaderProgram = [OOShaderProgram shaderProgramWithVertexShader:vertexShader
210 fragmentShader:fragmentShader
211 vertexShaderName:vsName
212 fragmentShaderName:fsName
214 attributeBindings:attributeBindings
218 if (shaderProgram !=
nil)
220 OOLog(
@"shader.load.fallbackSuccess",
@"Simple mode fallback successful.");
226 if (shaderProgram ==
nil)
228 OOLogERR(
@"shader.load.failed",
@"Could not build shader %@/%@.", vsName, fsName);
233 OOLog(
@"shader.load.noShader",
@"***** Error: no vertex or fragment shader specified in shader dictionary:\n%@", configuration);
236 OK = (shaderProgram !=
nil);
237 if (OK) [shaderProgram retain];
243 NSDictionary *uniformDefs = [configuration oo_dictionaryForKey:kOOUniformsKey];
245 NSArray *textureArray = [configuration oo_arrayForKey:kOOTextureObjectsKey];
246 if (textureArray ==
nil)
248 NSArray *textureSpecs = [configuration oo_arrayForKey:kOOTexturesKey];
249 if (textureSpecs !=
nil)
251 textureArray = [
self loadTexturesFromArray:textureSpecs unitCount:textureUnits];
255 uniforms = [[NSMutableDictionary alloc] initWithCapacity:[uniformDefs count] + [textureArray count]];
256 [
self addUniformsFromDictionary:uniformDefs withBindingTarget:target];
257 [
self addTexturesFromArray:textureArray unitCount:textureUnits];
264 if (![uniforms objectForKey:
@"uGloss"])
266 float gloss = OOClamp_0_1_f([configuration oo_floatForKey:
@"gloss" defaultValue:0.5f]);
267 [
self setUniform:@"uGloss" floatValue:gloss];
270 if (![uniforms objectForKey:
@"uGammaCorrect"])
272 BOOL gammaCorrect = [configuration oo_boolForKey:@"gamma_correct"
273 defaultValue:![[NSUserDefaults standardUserDefaults] boolForKey:@"no-gamma-correct"]];
274 [
self setUniform:@"uGammaCorrect" floatValue:(float)gammaCorrect];
293 [shaderProgram release];
296 if (textures != NULL)
298 for (i = 0; i != texCount; ++i)
300 [textures[i] release];
305 [bindingTarget release];
311- (BOOL)bindUniform:(NSString *)uniformName
312 toObject:(
id<OOWeakReferenceSupport>)source
313 property:(
SEL)selector
314 convertOptions:(OOUniformConvertOptions)options
316 OOShaderUniform *uniform =
nil;
318 if (uniformName ==
nil)
return NO;
320 uniform = [[OOShaderUniform alloc] initWithName:uniformName
321 shaderProgram:shaderProgram
324 convertOptions:options];
327 OOLog(
@"shader.uniform.set",
@"Set up uniform %@", uniform);
328 [uniforms setObject:uniform forKey:uniformName];
334 OOLog(
@"shader.uniform.unSet",
@"Did not set uniform \"%@\
"", uniformName);
335 [uniforms removeObjectForKey:uniformName];
341- (BOOL)bindSafeUniform:(NSString *)uniformName
342 toObject:(
id<OOWeakReferenceSupport>)target
343 propertyNamed:(NSString *)property
344 convertOptions:(OOUniformConvertOptions)options
348 selector = NSSelectorFromString(property);
352 return [
self bindUniform:uniformName
355 convertOptions:options];
359 OOLog(
@"shader.uniform.unpermittedMethod",
@"Did not bind uniform \"%@\
" to property -[%@ %@] - unpermitted method.", uniformName, [target
class], property);
366- (void)setUniform:(NSString *)uniformName intValue:(
int)value
368 OOShaderUniform *uniform =
nil;
370 if (uniformName ==
nil)
return;
372 uniform = [[OOShaderUniform alloc] initWithName:uniformName
373 shaderProgram:shaderProgram
377 OOLog(
@"shader.uniform.set",
@"Set up uniform %@", uniform);
378 [uniforms setObject:uniform forKey:uniformName];
383 OOLog(
@"shader.uniform.unSet",
@"Did not set uniform \"%@\
"", uniformName);
384 [uniforms removeObjectForKey:uniformName];
389- (void)setUniform:(NSString *)uniformName floatValue:(
float)value
391 OOShaderUniform *uniform =
nil;
393 if (uniformName ==
nil)
return;
395 uniform = [[OOShaderUniform alloc] initWithName:uniformName
396 shaderProgram:shaderProgram
400 OOLog(
@"shader.uniform.set",
@"Set up uniform %@", uniform);
401 [uniforms setObject:uniform forKey:uniformName];
406 OOLog(
@"shader.uniform.unSet",
@"Did not set uniform \"%@\
"", uniformName);
407 [uniforms removeObjectForKey:uniformName];
412- (void)setUniform:(NSString *)uniformName vectorValue:(GLfloat[4])value
414 OOShaderUniform *uniform =
nil;
416 if (uniformName ==
nil)
return;
418 uniform = [[OOShaderUniform alloc] initWithName:uniformName
419 shaderProgram:shaderProgram
423 OOLog(
@"shader.uniform.set",
@"Set up uniform %@", uniform);
424 [uniforms setObject:uniform forKey:uniformName];
429 OOLog(
@"shader.uniform.unSet",
@"Did not set uniform \"%@\
"", uniformName);
430 [uniforms removeObjectForKey:uniformName];
435- (void)setUniform:(NSString *)uniformName vectorObjectValue:(
id)value
437 if (uniformName ==
nil)
return;
440 if ([value isKindOfClass:[NSArray
class]] && [value
count] == 4)
442 for (
unsigned i = 0; i < 4; i++)
456 OOShaderUniform *uniform = [[OOShaderUniform alloc] initWithName:uniformName
457 shaderProgram:shaderProgram
458 vectorValue:vecArray];
461 OOLog(
@"shader.uniform.set",
@"Set up uniform %@", uniform);
462 [uniforms setObject:uniform forKey:uniformName];
467 OOLog(
@"shader.uniform.unSet",
@"Did not set uniform \"%@\
"", uniformName);
468 [uniforms removeObjectForKey:uniformName];
473- (void)setUniform:(NSString *)uniformName quaternionValue:(Quaternion)value asMatrix:(BOOL)asMatrix
475 OOShaderUniform *uniform =
nil;
477 if (uniformName ==
nil)
return;
479 uniform = [[OOShaderUniform alloc] initWithName:uniformName
480 shaderProgram:shaderProgram
481 quaternionValue:value
485 OOLog(
@"shader.uniform.set",
@"Set up uniform %@", uniform);
486 [uniforms setObject:uniform forKey:uniformName];
491 OOLog(
@"shader.uniform.unSet",
@"Did not set uniform \"%@\
"", uniformName);
492 [uniforms removeObjectForKey:uniformName];
497-(void)addUniformsFromDictionary:(NSDictionary *)uniformDefs withBindingTarget:(
id<OOWeakReferenceSupport>)target
499 NSEnumerator *uniformEnum =
nil;
500 NSString *name =
nil;
503 NSString *binding =
nil;
504 NSString *type =
nil;
507 OOUniformConvertOptions convertOptions;
508 BOOL quatAsMatrix = YES;
514 if ([target respondsToSelector:
@selector(randomSeedForShaders)])
516 randomSeed = [(id)target randomSeedForShaders];
520 randomSeed = (uint32_t)(uintptr_t)
self;
525 keys = [[uniformDefs allKeys] sortedArrayUsingSelector:@selector(compare:)];
526 for (uniformEnum = [keys objectEnumerator]; (name = [uniformEnum nextObject]); )
529 definition = [uniformDefs objectForKey:name];
535 if ([definition isKindOfClass:[NSDictionary
class]])
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];
543 if (value ==
nil && binding !=
nil) type =
@"binding";
544 else type =
@"float";
547 else if ([definition isKindOfClass:[NSNumber
class]])
552 else if ([definition isKindOfClass:[NSString
class]])
561 binding = definition;
565 else if ([definition isKindOfClass:[NSArray
class]])
567 binding = definition;
572 if ([type isEqualToString:
@"randomFloat"])
575 value = [NSNumber numberWithFloat:randf() * scale];
577 else if ([type isEqualToString:
@"randomUnitVector"])
582 else if ([type isEqualToString:
@"randomVectorSpatial"])
587 else if ([type isEqualToString:
@"randomVectorRadial"])
592 else if ([type isEqualToString:
@"randomQuaternion"])
594 type =
@"quaternion";
598 if ([type isEqualToString:
@"float"] || [type isEqualToString:
@"real"])
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];
608 [
self setUniform:name floatValue:floatValue];
611 else if ([type isEqualToString:
@"int"] || [type isEqualToString:
@"integer"] || [type isEqualToString:
@"texture"])
620 if ([value respondsToSelector:
@selector(intValue)])
622 [
self setUniform:name intValue:[value intValue]];
626 else if ([type isEqualToString:
@"vector"])
628 [
self setUniform:name vectorObjectValue:value];
631 else if ([type isEqualToString:
@"quaternion"])
633 if ([definition isKindOfClass:[NSDictionary
class]])
635 quatAsMatrix = [definition oo_boolForKey:@"asMatrix" defaultValue:quatAsMatrix];
637 [
self setUniform:name
638 quaternionValue:OOQuaternionFromObject(value, kIdentityQuaternion)
639 asMatrix:quatAsMatrix];
642 else if (target !=
nil && [type isEqualToString:
@"binding"])
644 if ([definition isKindOfClass:[NSDictionary
class]])
647 if ([definition oo_boolForKey:
@"clamped" defaultValue:NO]) convertOptions |= kOOUniformConvertClamp;
648 if ([definition oo_boolForKey:
@"normalized" defaultValue:[definition oo_boolForKey:
@"normalised" defaultValue:NO]])
650 convertOptions |= kOOUniformConvertNormalize;
652 if ([definition oo_boolForKey:
@"asMatrix" defaultValue:YES]) convertOptions |= kOOUniformConvertToMatrix;
653 if (![definition oo_boolForKey:
@"bindToSubentity" defaultValue:NO]) convertOptions |= kOOUniformBindToSuperTarget;
657 convertOptions = kOOUniformConvertDefaults;
660 [
self bindSafeUniform:name toObject:target propertyNamed:binding convertOptions:convertOptions];
666 OOLog(
@"shader.uniform.badDescription",
@"----- Warning: could not bind uniform \"%@\
" for target %@ -- could not interpret definition:\n%@", name, target, definition);
676 NSEnumerator *uniformEnum =
nil;
677 OOShaderUniform *uniform =
nil;
683 [shaderProgram apply];
685 for (i = 0; i != texCount; ++i)
687 OOGL(glActiveTextureARB(GL_TEXTURE0_ARB + i));
690 if (texCount > 1)
OOGL(glActiveTextureARB(GL_TEXTURE0_ARB));
694 for (uniformEnum = [uniforms objectEnumerator]; (uniform = [uniformEnum nextObject]); )
699 @catch (
id exception) {}
705- (void)ensureFinishedLoading
709 if (textures != NULL)
711 for (i = 0; i != texCount; ++i)
713 [textures[i] ensureFinishedLoading];
719- (BOOL) isFinishedLoading
723 if (textures != NULL)
725 for (i = 0; i != texCount; ++i)
727 if (![textures[i] isFinishedLoading])
return NO;
739 if (![next isKindOfClass:[OOShaderMaterial
class]])
742 [OOShaderProgram applyNone];
752 count = texCount ? texCount : 1;
753 for (i = 0; i !=
count; ++i)
755 OOGL(glActiveTextureARB(GL_TEXTURE0_ARB + i));
758 if (
count != 1)
OOGL(glActiveTextureARB(GL_TEXTURE0_ARB));
763- (void)setBindingTarget:(
id<OOWeakReferenceSupport>)target
765 [[uniforms allValues] makeObjectsPerformSelector:@selector(setBindingTarget:) withObject:target];
766 [bindingTarget release];
767 bindingTarget = [target weakRetain];
771- (BOOL) permitSpecular
778- (NSSet *) allTextures
780 return [NSSet setWithObjects:textures count:texCount];
787@implementation OOShaderMaterial (OOPrivate)
789- (NSArray *) loadTexturesFromArray:(NSArray *)textureSpecs unitCount:(GLuint)max
791 GLuint i,
count = (GLuint)
MIN([textureSpecs
count], (NSUInteger)max);
792 NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
794 for (i = 0; i <
count; i++)
796 id textureSpec = [textureSpecs objectAtIndex:i];
799 [result addObject:texture];
806- (void) addTexturesFromArray:(NSArray *)textureObjects unitCount:(GLuint)max
809 texCount = (uint32_t)
MIN([textureObjects
count], (NSUInteger)max);
810 if (texCount == 0)
return;
812 textures = malloc(texCount *
sizeof *textures);
813 if (textures == NULL)
821 for (i = 0; i != texCount; ++i)
823 textures[i] = [textureObjects objectAtIndex:i];
824 [textures[i] retain];
831static NSString *MacrosToString(NSDictionary *macros)
833 NSMutableString *result =
nil;
834 NSEnumerator *macroEnum =
nil;
835 id key =
nil, value =
nil;
837 if (macros ==
nil)
return nil;
839 result = [NSMutableString string];
840 for (macroEnum = [macros keyEnumerator]; (key = [macroEnum nextObject]); )
842 if (![key isKindOfClass:[NSString
class]])
continue;
843 value = [macros objectForKey:key];
845 [result appendFormat:@"#define %@ %@\n", key, value];
848 if ([result length] == 0)
return nil;
849 [result appendString:@"\n\n"];
860static BOOL
GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult)
862 NSString *result =
nil;
863 NSArray *extensions =
nil;
864 NSEnumerator *extEnum =
nil;
865 NSString *extension =
nil;
866 NSString *nameWithExtension =
nil;
868 if (fileName ==
nil)
return YES;
873 extensions = [NSArray arrayWithObjects:shaderType, [shaderType substringToIndex:4], nil];
876 if (![fileName pathHasExtensionInArray:extensions])
878 for (extEnum = [extensions objectEnumerator]; (extension = [extEnum nextObject]); )
880 nameWithExtension = [fileName stringByAppendingPathExtension:extension];
883 if (result !=
nil)
break;
893 if (outResult != NULL) *outResult = result;
@ DEBUG_NO_SHADER_FALLBACK
BOOL OOIsNumberLiteral(NSString *string, BOOL allowSpaces)
#define OOLogWARN(class, format,...)
#define OOLogERR(class, format,...)
#define OOLog(class, format,...)
NSString *const kOOLogFileNotFound
#define OO_ENTER_OPENGL()
static BOOL GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult)
Vector OORandomUnitVector(void)
Vector OOVectorRandomRadial(OOScalar maxLength)
Vector OOVectorRandomSpatial(OOScalar maxLength)
BOOL OOUniformBindingPermitted(NSString *propertyName, id bindingTarget)
OOOpenGLExtensionManager * sharedManager()
GLint textureImageUnitCount
id textureWithConfiguration:(id configuration)
NSString * stringFromFilesNamed:inFolder:(NSString *fileName,[inFolder] NSString *folderName)
RANROTSeed RANROTGetFullSeed(void)
void ranrot_srand(uint32_t seed)
void RANROTSetFullSeed(RANROTSeed seed)