Line data Source code
1 0 : /*
2 :
3 : OOShaderUniform.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 "OOShaderUniform.h"
30 :
31 : #if OO_SHADERS
32 :
33 : #import "OOShaderProgram.h"
34 : #import "OOFunctionAttributes.h"
35 : #include <string.h>
36 : #import "OOMaths.h"
37 : #import "OOOpenGLExtensionManager.h"
38 : #import "OOShaderUniformMethodType.h"
39 :
40 :
41 : @interface OOShaderUniform (OOPrivate)
42 :
43 : - (id)initWithName:(NSString *)uniformName shaderProgram:(OOShaderProgram *)shaderProgram;
44 :
45 : - (void)applySimple;
46 : - (void)applyBinding;
47 :
48 : @end
49 :
50 :
51 : @implementation OOShaderUniform
52 :
53 : - (id)initWithName:(NSString *)uniformName shaderProgram:(OOShaderProgram *)shaderProgram intValue:(GLint)constValue
54 : {
55 : self = [self initWithName:uniformName shaderProgram:shaderProgram];
56 : if (self != nil)
57 : {
58 : type = kOOShaderUniformTypeInt;
59 : value.constInt = constValue;
60 : }
61 :
62 : return self;
63 : }
64 :
65 :
66 : - (id)initWithName:(NSString *)uniformName shaderProgram:(OOShaderProgram *)shaderProgram floatValue:(GLfloat)constValue
67 : {
68 : self = [self initWithName:uniformName shaderProgram:shaderProgram];
69 : if (self != nil)
70 : {
71 : type = kOOShaderUniformTypeFloat;
72 : value.constFloat = constValue;
73 : }
74 :
75 : return self;
76 : }
77 :
78 :
79 : - (id)initWithName:(NSString *)uniformName shaderProgram:(OOShaderProgram *)shaderProgram vectorValue:(GLfloat[4])constValue
80 : {
81 : self = [self initWithName:uniformName shaderProgram:shaderProgram];
82 : if (self != nil)
83 : {
84 : type = kOOShaderUniformTypeVector;
85 : memcpy(value.constVector, constValue, sizeof value.constVector);
86 : }
87 :
88 : return self;
89 : }
90 :
91 :
92 : - (id)initWithName:(NSString *)uniformName shaderProgram:(OOShaderProgram *)shaderProgram colorValue:(OOColor *)constValue
93 : {
94 : if (EXPECT_NOT(constValue == nil))
95 : {
96 : [self release];
97 : return nil;
98 : }
99 :
100 : self = [self initWithName:uniformName shaderProgram:shaderProgram];
101 : if (self != nil)
102 : {
103 : type = kOOShaderUniformTypeVector;
104 : value.constVector[0] = [constValue redComponent];
105 : value.constVector[1] = [constValue greenComponent];
106 : value.constVector[2] = [constValue blueComponent];
107 : value.constVector[3] = [constValue alphaComponent];
108 : }
109 :
110 : return self;
111 : }
112 :
113 :
114 : - (id)initWithName:(NSString *)uniformName shaderProgram:(OOShaderProgram *)shaderProgram quaternionValue:(Quaternion)constValue asMatrix:(BOOL)asMatrix
115 : {
116 : self = [self initWithName:uniformName shaderProgram:shaderProgram];
117 : if (self != nil)
118 : {
119 : if (asMatrix)
120 : {
121 : type = kOOShaderUniformTypeMatrix;
122 : value.constMatrix = OOMatrixForQuaternionRotation(constValue);
123 : }
124 : else
125 : {
126 : type = kOOShaderUniformTypeVector;
127 : value.constVector[0] = constValue.x;
128 : value.constVector[1] = constValue.y;
129 : value.constVector[2] = constValue.z;
130 : value.constVector[3] = constValue.w;
131 : }
132 : }
133 :
134 : return self;
135 : }
136 :
137 :
138 : - (id)initWithName:(NSString *)uniformName shaderProgram:(OOShaderProgram *)shaderProgram matrixValue:(OOMatrix)constValue
139 : {
140 : self = [self initWithName:uniformName shaderProgram:shaderProgram];
141 : if (self != nil)
142 : {
143 : type = kOOShaderUniformTypeMatrix;
144 : value.constMatrix = constValue;
145 : }
146 :
147 : return self;
148 : }
149 :
150 :
151 : - (id)initWithName:(NSString *)uniformName
152 : shaderProgram:(OOShaderProgram *)shaderProgram
153 : boundToObject:(id<OOWeakReferenceSupport>)target
154 : property:(SEL)selector
155 : convertOptions:(OOUniformConvertOptions)options
156 : {
157 : BOOL OK = YES;
158 :
159 : if (EXPECT_NOT(uniformName == NULL || shaderProgram == NULL || selector == NULL)) OK = NO;
160 :
161 : if (OK)
162 : {
163 : self = [super init];
164 : if (self == nil) OK = NO;
165 : }
166 :
167 : if (OK)
168 : {
169 : location = glGetUniformLocationARB([shaderProgram program], [uniformName UTF8String]);
170 : if (location == -1)
171 : {
172 : OK = NO;
173 : OOLog(@"shader.uniform.bind.failed", @"Could not bind uniform \"%@\" to -[%@ %@] (no uniform of that name could be found).", uniformName, [target class], NSStringFromSelector(selector));
174 : }
175 : }
176 :
177 : // If we're still OK, it's a bindable method.
178 : if (OK)
179 : {
180 : name = [uniformName retain];
181 : isBinding = YES;
182 : value.binding.selector = selector;
183 :
184 : convertClamp = (options & kOOUniformConvertClamp) != 0;
185 : convertNormalize = (options & kOOUniformConvertNormalize) != 0;
186 : convertToMatrix = (options & kOOUniformConvertToMatrix) != 0;
187 : bindToSuper = (options & kOOUniformBindToSuperTarget) != 0;
188 :
189 : if (target != nil) [self setBindingTarget:target];
190 : }
191 :
192 : if (!OK)
193 : {
194 : [self release];
195 : self = nil;
196 : }
197 : return self;
198 : }
199 :
200 :
201 : - (void)dealloc
202 : {
203 : [name release];
204 : if (isBinding) [value.binding.object release];
205 :
206 : [super dealloc];
207 : }
208 :
209 :
210 : - (NSString *)description
211 : {
212 : NSString *valueDesc = nil;
213 : NSString *valueType = nil;
214 : id object;
215 :
216 : if (isBinding)
217 : {
218 : object = [value.binding.object weakRefUnderlyingObject];
219 : if (object != nil)
220 : {
221 : valueDesc = [NSString stringWithFormat:@"[<%@ %p> %@]", [object class], value.binding.object, NSStringFromSelector(value.binding.selector)];
222 : }
223 : else
224 : {
225 : valueDesc = @"0";
226 : }
227 : }
228 : else
229 : {
230 : switch (type)
231 : {
232 : case kOOShaderUniformTypeInt:
233 : valueDesc = [NSString stringWithFormat:@"%i", value.constInt];
234 : break;
235 :
236 : case kOOShaderUniformTypeFloat:
237 : valueDesc = [NSString stringWithFormat:@"%g", value.constFloat];
238 : break;
239 :
240 : case kOOShaderUniformTypeVector:
241 : {
242 : Vector v = { value.constVector[0], value.constVector[1], value.constVector[2] };
243 : valueDesc = VectorDescription(v);
244 : }
245 : break;
246 :
247 : case kOOShaderUniformTypeMatrix:
248 : valueDesc = OOMatrixDescription(value.constMatrix);
249 : break;
250 : }
251 : }
252 :
253 : switch (type)
254 : {
255 : case kOOShaderUniformTypeChar:
256 : case kOOShaderUniformTypeUnsignedChar:
257 : case kOOShaderUniformTypeShort:
258 : case kOOShaderUniformTypeUnsignedShort:
259 : case kOOShaderUniformTypeInt:
260 : case kOOShaderUniformTypeUnsignedInt:
261 : case kOOShaderUniformTypeLong:
262 : case kOOShaderUniformTypeUnsignedLong:
263 : valueType = @"int";
264 : break;
265 :
266 : case kOOShaderUniformTypeFloat:
267 : case kOOShaderUniformTypeDouble:
268 : valueType = @"float";
269 : break;
270 :
271 : case kOOShaderUniformTypeVector:
272 : case kOOShaderUniformTypeHPVector:
273 : valueType = @"vec4";
274 : break;
275 :
276 : case kOOShaderUniformTypeQuaternion:
277 : valueType = @"vec4 (quaternion)";
278 : break;
279 :
280 : case kOOShaderUniformTypeMatrix:
281 : valueType = @"matrix";
282 : break;
283 :
284 : case kOOShaderUniformTypePoint:
285 : valueType = @"vec2";
286 : break;
287 :
288 : case kOOShaderUniformTypeObject:
289 : valueType = @"object-binding";
290 : break;
291 :
292 : }
293 : if (valueType == nil) valueDesc = @"INVALID";
294 : if (valueDesc == nil) valueDesc = @"INVALID";
295 :
296 : /* Examples:
297 : <OOShaderUniform 0xf00>{1: int tex1 = 1;}
298 : <OOShaderUniform 0xf00>{3: float laser_heat_level = [<ShipEntity 0xba8> laserHeatLevel];}
299 : */
300 : return [NSString stringWithFormat:@"<%@ %p>{%i: %@ %@ = %@;}", [self class], self, location, valueType, name, valueDesc];
301 : }
302 :
303 :
304 : - (void)apply
305 : {
306 :
307 : if (isBinding)
308 : {
309 : if (isActiveBinding) [self applyBinding];
310 : }
311 : else [self applySimple];
312 : }
313 :
314 :
315 : - (void)setBindingTarget:(id<OOWeakReferenceSupport>)target
316 : {
317 : BOOL OK = YES;
318 : NSMethodSignature *signature = nil;
319 : NSUInteger argCount;
320 : NSString *methodProblem = nil;
321 : id<OOWeakReferenceSupport> superCandidate = nil;
322 :
323 : if (!isBinding) return;
324 :
325 : // Resolve "supertarget" if applicable
326 : if (bindToSuper)
327 : {
328 : for (;;)
329 : {
330 : if (![target respondsToSelector:@selector(superShaderBindingTarget)]) break;
331 :
332 : superCandidate = [(id)target superShaderBindingTarget];
333 : if (superCandidate == nil || superCandidate == target) break;
334 : target = superCandidate;
335 : }
336 : }
337 :
338 : [value.binding.object release];
339 : value.binding.object = [target weakRetain];
340 :
341 : if (target == nil)
342 : {
343 : isActiveBinding = NO;
344 : return;
345 : }
346 :
347 : if (OK)
348 : {
349 : if (![target respondsToSelector:value.binding.selector])
350 : {
351 : methodProblem = @"target does not respond to selector";
352 : OK = NO;
353 : }
354 : }
355 :
356 : if (OK)
357 : {
358 : value.binding.method = [(id)target methodForSelector:value.binding.selector];
359 : if (value.binding.method == NULL)
360 : {
361 : methodProblem = @"could not retrieve method implementation";
362 : OK = NO;
363 : }
364 : }
365 :
366 : if (OK)
367 : {
368 : signature = [(id)target methodSignatureForSelector:value.binding.selector];
369 : if (signature == nil)
370 : {
371 : methodProblem = @"could not retrieve method signature";
372 : OK = NO;
373 : }
374 : }
375 :
376 : if (OK)
377 : {
378 : argCount = [signature numberOfArguments];
379 : if (argCount != 2) // "no-arguments" methods actually take two arguments, self and _msg.
380 : {
381 : methodProblem = @"only methods which do not require arguments may be bound to";
382 : OK = NO;
383 : }
384 : }
385 :
386 : if (OK)
387 : {
388 : type = OOShaderUniformTypeFromMethodSignature(signature);
389 : if (type == kOOShaderUniformTypeInvalid)
390 : {
391 : OK = NO;
392 : methodProblem = [NSString stringWithFormat:@"unsupported type \"%s\"", [signature methodReturnType]];
393 : }
394 : }
395 :
396 : isActiveBinding = OK;
397 : if (!OK) OOLog(@"shader.uniform.bind.failed", @"Shader could not bind uniform \"%@\" to -[%@ %@] (%@).", name, [target class], NSStringFromSelector(value.binding.selector), methodProblem);
398 : }
399 :
400 : @end
401 :
402 :
403 : @implementation OOShaderUniform (OOPrivate)
404 :
405 : // Designated initializer.
406 : - (id)initWithName:(NSString *)uniformName shaderProgram:(OOShaderProgram *)shaderProgram
407 : {
408 : BOOL OK = YES;
409 :
410 : if (EXPECT_NOT(uniformName == NULL || shaderProgram == NULL)) OK = NO;
411 :
412 : if (OK)
413 : {
414 : self = [super init];
415 : if (self == nil) OK = NO;
416 : }
417 :
418 : if (OK)
419 : {
420 : location = glGetUniformLocationARB([shaderProgram program], [uniformName UTF8String]);
421 : if (location == -1) OK = NO;
422 : }
423 :
424 : if (OK)
425 : {
426 : name = [uniformName copy];
427 : }
428 :
429 : if (!OK)
430 : {
431 : [self release];
432 : self = nil;
433 : }
434 : return self;
435 : }
436 :
437 : - (void)applySimple
438 : {
439 : switch (type)
440 : {
441 : case kOOShaderUniformTypeInt:
442 : OOGL(glUniform1iARB(location, value.constInt));
443 : break;
444 :
445 : case kOOShaderUniformTypeFloat:
446 : OOGL(glUniform1fARB(location, value.constFloat));
447 : break;
448 :
449 : case kOOShaderUniformTypeVector:
450 : OOGL(glUniform4fvARB(location, 1, value.constVector));
451 : break;
452 :
453 : case kOOShaderUniformTypeMatrix:
454 : GLUniformMatrix(location, value.constMatrix);
455 : }
456 : }
457 :
458 :
459 : - (void)applyBinding
460 : {
461 :
462 : id object = nil;
463 : GLint iVal;
464 : GLfloat fVal;
465 : Vector vVal;
466 : HPVector hpvVal;
467 : GLfloat expVVal[4];
468 : OOMatrix mVal;
469 : Quaternion qVal;
470 : NSPoint pVal = {0};
471 : BOOL isInt = NO, isFloat = NO, isVector = NO, isMatrix = NO, isPoint = NO;
472 : id objVal = nil;
473 :
474 : /* Design note: if the object has been dealloced, or an exception occurs,
475 : do nothing. Shaders can specify a default value for uniforms, which
476 : will be used when no setting has been provided by the host program.
477 :
478 : I considered clearing value.binding.object if the underlying object is
479 : gone, but adding code to save a small amount of spacein a case that
480 : shouldn't occur in normal usage is silly.
481 : */
482 : object = [value.binding.object weakRefUnderlyingObject];
483 : if (object == nil) return;
484 :
485 : switch (type)
486 : {
487 : case kOOShaderUniformTypeChar:
488 : case kOOShaderUniformTypeUnsignedChar:
489 : case kOOShaderUniformTypeShort:
490 : case kOOShaderUniformTypeUnsignedShort:
491 : case kOOShaderUniformTypeInt:
492 : case kOOShaderUniformTypeUnsignedInt:
493 : case kOOShaderUniformTypeLong:
494 : case kOOShaderUniformTypeUnsignedLong:
495 : iVal = (GLint)OOCallIntegerMethod(object, value.binding.selector, value.binding.method, type);
496 : isInt = YES;
497 : break;
498 :
499 : case kOOShaderUniformTypeFloat:
500 : case kOOShaderUniformTypeDouble:
501 : fVal = OOCallFloatMethod(object, value.binding.selector, value.binding.method, type);
502 : isFloat = YES;
503 : break;
504 :
505 : case kOOShaderUniformTypeVector:
506 : vVal = ((VectorReturnMsgSend)value.binding.method)(object, value.binding.selector);
507 : if (convertNormalize) vVal = vector_normal(vVal);
508 : expVVal[0] = vVal.x;
509 : expVVal[1] = vVal.y;
510 : expVVal[2] = vVal.z;
511 : expVVal[3] = 1.0f;
512 : isVector = YES;
513 : break;
514 :
515 : case kOOShaderUniformTypeHPVector:
516 : hpvVal = ((HPVectorReturnMsgSend)value.binding.method)(object, value.binding.selector);
517 : if (convertNormalize) hpvVal = HPvector_normal(hpvVal);
518 : expVVal[0] = (GLfloat)hpvVal.x;
519 : expVVal[1] = (GLfloat)hpvVal.y;
520 : expVVal[2] = (GLfloat)hpvVal.z;
521 : expVVal[3] = 1.0f;
522 : isVector = YES;
523 : break;
524 :
525 : case kOOShaderUniformTypeQuaternion:
526 : qVal = ((QuaternionReturnMsgSend)value.binding.method)(object, value.binding.selector);
527 : if (convertToMatrix)
528 : {
529 : mVal = OOMatrixForQuaternionRotation(qVal);
530 : isMatrix = YES;
531 : }
532 : else
533 : {
534 : expVVal[0] = qVal.x;
535 : expVVal[1] = qVal.y;
536 : expVVal[2] = qVal.z;
537 : expVVal[3] = qVal.w;
538 : isVector = YES;
539 : }
540 : break;
541 :
542 : case kOOShaderUniformTypeMatrix:
543 : mVal = ((MatrixReturnMsgSend)value.binding.method)(object, value.binding.selector);
544 : isMatrix = YES;
545 : break;
546 :
547 : case kOOShaderUniformTypePoint:
548 : pVal = ((PointReturnMsgSend)value.binding.method)(object, value.binding.selector);
549 : isPoint = YES;
550 : break;
551 :
552 : case kOOShaderUniformTypeObject:
553 : objVal = ((ObjectReturnMsgSend)value.binding.method)(object, value.binding.selector);
554 : if ([objVal isKindOfClass:[NSNumber class]])
555 : {
556 : fVal = [objVal floatValue];
557 : isFloat = YES;
558 : }
559 : else if ([objVal isKindOfClass:[OOColor class]])
560 : {
561 : OOColor *color = objVal;
562 : expVVal[0] = [color redComponent];
563 : expVVal[1] = [color greenComponent];
564 : expVVal[2] = [color blueComponent];
565 : expVVal[3] = [color alphaComponent];
566 : isVector = YES;
567 : }
568 : break;
569 : }
570 :
571 : if (isFloat)
572 : {
573 : if (convertClamp) fVal = OOClamp_0_1_f(fVal);
574 : OOGL(glUniform1fARB(location, fVal));
575 : }
576 : else if (isInt)
577 : {
578 : if (convertClamp) iVal = iVal ? 1 : 0;
579 : OOGL(glUniform1iARB(location, iVal));
580 : }
581 : else if (isPoint)
582 : {
583 : GLfloat v2[2] = { pVal.x, pVal.y };
584 : OOGL(glUniform2fvARB(location, 1, v2));
585 : }
586 : else if (isVector)
587 : {
588 : OOGL(glUniform4fvARB(location, 1, expVVal));
589 : }
590 : else if (isMatrix)
591 : {
592 : GLUniformMatrix(location, mVal);
593 : }
594 : }
595 :
596 : @end
597 :
598 : #endif // OO_SHADERS
|