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:@"$VERTEX:\n%@\n\n$FRAGMENT:\n%@\n\n$MACROS:\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$SIMPLIFIED 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 NSString *name =
nil;
502 NSString *binding =
nil;
503 NSString *type =
nil;
506 OOUniformConvertOptions convertOptions;
507 BOOL quatAsMatrix = YES;
513 if ([target respondsToSelector:@selector(randomSeedForShaders)])
515 randomSeed = [(id)target randomSeedForShaders];
519 randomSeed = (uint32_t)(uintptr_t)
self;
524 keys = [[uniformDefs allKeys] sortedArrayUsingSelector:@selector(compare:)];
528 definition = [uniformDefs objectForKey:name];
534 if ([definition isKindOfClass:[NSDictionary class]])
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];
542 if (value ==
nil && binding !=
nil) type =
@"binding";
543 else type =
@"float";
546 else if ([definition isKindOfClass:[NSNumber class]])
551 else if ([definition isKindOfClass:[NSString class]])
560 binding = definition;
564 else if ([definition isKindOfClass:[NSArray class]])
566 binding = definition;
571 if ([type isEqualToString:
@"randomFloat"])
574 value = [NSNumber numberWithFloat:randf() * scale];
576 else if ([type isEqualToString:
@"randomUnitVector"])
581 else if ([type isEqualToString:
@"randomVectorSpatial"])
586 else if ([type isEqualToString:
@"randomVectorRadial"])
591 else if ([type isEqualToString:
@"randomQuaternion"])
593 type =
@"quaternion";
597 if ([type isEqualToString:
@"float"] || [type isEqualToString:
@"real"])
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];
607 [
self setUniform:name floatValue:floatValue];
610 else if ([type isEqualToString:
@"int"] || [type isEqualToString:
@"integer"] || [type isEqualToString:
@"texture"])
619 if ([value respondsToSelector:@selector(intValue)])
621 [
self setUniform:name intValue:[value intValue]];
625 else if ([type isEqualToString:
@"vector"])
627 [
self setUniform:name vectorObjectValue:value];
630 else if ([type isEqualToString:
@"quaternion"])
632 if ([definition isKindOfClass:[NSDictionary class]])
634 quatAsMatrix = [definition oo_boolForKey:@"asMatrix" defaultValue:quatAsMatrix];
636 [
self setUniform:name
637 quaternionValue:OOQuaternionFromObject(value, kIdentityQuaternion)
638 asMatrix:quatAsMatrix];
641 else if (target !=
nil && [type isEqualToString:
@"binding"])
643 if ([definition isKindOfClass:[NSDictionary class]])
646 if ([definition oo_boolForKey:
@"clamped" defaultValue:NO]) convertOptions |= kOOUniformConvertClamp;
647 if ([definition oo_boolForKey:
@"normalized" defaultValue:[definition oo_boolForKey:
@"normalised" defaultValue:NO]])
649 convertOptions |= kOOUniformConvertNormalize;
651 if ([definition oo_boolForKey:
@"asMatrix" defaultValue:YES]) convertOptions |= kOOUniformConvertToMatrix;
652 if (![definition oo_boolForKey:
@"bindToSubentity" defaultValue:NO]) convertOptions |= kOOUniformBindToSuperTarget;
656 convertOptions = kOOUniformConvertDefaults;
659 [
self bindSafeUniform:name toObject:target propertyNamed:binding convertOptions:convertOptions];
665 OOLog(
@"shader.uniform.badDescription",
@"----- Warning: could not bind uniform \"%@\
" for target %@ -- could not interpret definition:\n%@", name, target, definition);
675 OOShaderUniform *uniform =
nil;
681 [shaderProgram apply];
683 for (i = 0; i != texCount; ++i)
685 OOGL(glActiveTextureARB(GL_TEXTURE0_ARB + i));
688 if (texCount > 1)
OOGL(glActiveTextureARB(GL_TEXTURE0_ARB));
692 foreach (uniform, [uniforms allValues])
697 @catch (
id exception) {}
703- (void)ensureFinishedLoading
707 if (textures != NULL)
709 for (i = 0; i != texCount; ++i)
711 [textures[i] ensureFinishedLoading];
717- (BOOL) isFinishedLoading
721 if (textures != NULL)
723 for (i = 0; i != texCount; ++i)
725 if (![textures[i] isFinishedLoading])
return NO;
737 if (![next isKindOfClass:[OOShaderMaterial class]])
740 [OOShaderProgram applyNone];
750 count = texCount ? texCount : 1;
751 for (i = 0; i !=
count; ++i)
753 OOGL(glActiveTextureARB(GL_TEXTURE0_ARB + i));
756 if (
count != 1)
OOGL(glActiveTextureARB(GL_TEXTURE0_ARB));
761- (void)setBindingTarget:(
id<OOWeakReferenceSupport>)target
763 [[uniforms allValues] makeObjectsPerformSelector:@selector(setBindingTarget:) withObject:target];
764 [bindingTarget release];
765 bindingTarget = [target weakRetain];
769- (BOOL) permitSpecular
776- (NSSet *) allTextures
778 return [NSSet setWithObjects:textures count:texCount];
785@implementation OOShaderMaterial (OOPrivate)
787- (NSArray *) loadTexturesFromArray:(NSArray *)textureSpecs unitCount:(GLuint)max
789 GLuint i,
count = (GLuint)
MIN([textureSpecs
count], (NSUInteger)max);
790 NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
792 for (i = 0; i <
count; i++)
794 id textureSpec = [textureSpecs objectAtIndex:i];
797 [result addObject:texture];
804- (void) addTexturesFromArray:(NSArray *)textureObjects unitCount:(GLuint)max
807 texCount = (uint32_t)
MIN([textureObjects
count], (NSUInteger)max);
808 if (texCount == 0)
return;
810 textures = malloc(texCount *
sizeof *textures);
811 if (textures == NULL)
819 for (i = 0; i != texCount; ++i)
821 textures[i] = [textureObjects objectAtIndex:i];
822 [textures[i] retain];
829static NSString *MacrosToString(NSDictionary *macros)
831 NSMutableString *result =
nil;
832 id key =
nil, value =
nil;
834 if (macros ==
nil)
return nil;
836 result = [NSMutableString string];
839 if (![key isKindOfClass:[NSString class]]) continue;
840 value = [macros objectForKey:key];
842 [result appendFormat:@"#define %@ %@\n", key, value];
845 if ([result length] == 0)
return nil;
846 [result appendString:@"\n\n"];
857static BOOL
GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult)
859 NSString *result =
nil;
860 NSArray *extensions =
nil;
861 NSString *extension =
nil;
862 NSString *nameWithExtension =
nil;
864 if (fileName ==
nil)
return YES;
869 extensions = [NSArray arrayWithObjects:shaderType, [shaderType substringToIndex:4], nil];
872 if (![fileName pathHasExtensionInArray:extensions])
874 foreach (extension, extensions)
876 nameWithExtension = [fileName stringByAppendingPathExtension:extension];
879 if (result !=
nil)
break;
889 if (outResult != NULL) *outResult = result;
#define foreachkey(VAR, DICT)
@ 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)