Oolite 1.91.0.7645-241119-222d325
Loading...
Searching...
No Matches
OOShaderUniform.m
Go to the documentation of this file.
1/*
2
3OOShaderUniform.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 "OOShaderUniform.h"
30
31#if OO_SHADERS
32
33#import "OOShaderProgram.h"
35#include <string.h>
36#import "OOMaths.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 {
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 {
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 {
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 {
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 {
122 value.constMatrix = OOMatrixForQuaternionRotation(constValue);
123 }
124 else
125 {
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 {
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 {
233 valueDesc = [NSString stringWithFormat:@"%i", value.constInt];
234 break;
235
237 valueDesc = [NSString stringWithFormat:@"%g", value.constFloat];
238 break;
239
241 {
242 Vector v = { value.constVector[0], value.constVector[1], value.constVector[2] };
243 valueDesc = VectorDescription(v);
244 }
245 break;
246
248 valueDesc = OOMatrixDescription(value.constMatrix);
249 break;
250 }
251 }
252
253 switch (type)
254 {
263 valueType = @"int";
264 break;
265
268 valueType = @"float";
269 break;
270
273 valueType = @"vec4";
274 break;
275
277 valueType = @"vec4 (quaternion)";
278 break;
279
281 valueType = @"matrix";
282 break;
283
285 valueType = @"vec2";
286 break;
287
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 {
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 {
442 OOGL(glUniform1iARB(location, value.constInt));
443 break;
444
446 OOGL(glUniform1fARB(location, value.constFloat));
447 break;
448
450 OOGL(glUniform4fvARB(location, 1, value.constVector));
451 break;
452
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 {
495 iVal = (GLint)OOCallIntegerMethod(object, value.binding.selector, value.binding.method, type);
496 isInt = YES;
497 break;
498
501 fVal = OOCallFloatMethod(object, value.binding.selector, value.binding.method, type);
502 isFloat = YES;
503 break;
504
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
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
526 qVal = ((QuaternionReturnMsgSend)value.binding.method)(object, value.binding.selector);
527 if (convertToMatrix)
528 {
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
543 mVal = ((MatrixReturnMsgSend)value.binding.method)(object, value.binding.selector);
544 isMatrix = YES;
545 break;
546
548 pVal = ((PointReturnMsgSend)value.binding.method)(object, value.binding.selector);
549 isPoint = YES;
550 break;
551
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
#define EXPECT_NOT(x)
#define OOLog(class, format,...)
Definition OOLogging.h:88
OOMatrix OOMatrixForQuaternionRotation(Quaternion orientation)
Definition OOMatrix.m:65
#define OOGL(statement)
Definition OOOpenGL.h:251
return nil
long long OOCallIntegerMethod(id object, SEL selector, IMP method, OOShaderUniformType type)
HPVector(* HPVectorReturnMsgSend)(id, SEL)
id(* ObjectReturnMsgSend)(id, SEL)
OOShaderUniformType OOShaderUniformTypeFromMethodSignature(NSMethodSignature *signature)
double OOCallFloatMethod(id object, SEL selector, IMP method, OOShaderUniformType type)
Vector(* VectorReturnMsgSend)(id, SEL)
@ kOOShaderUniformTypeQuaternion
@ kOOShaderUniformTypeMatrix
@ kOOShaderUniformTypeVector
@ kOOShaderUniformTypeLong
@ kOOShaderUniformTypeUnsignedChar
@ kOOShaderUniformTypeShort
@ kOOShaderUniformTypePoint
@ kOOShaderUniformTypeUnsignedLong
@ kOOShaderUniformTypeFloat
@ kOOShaderUniformTypeUnsignedInt
@ kOOShaderUniformTypeUnsignedShort
@ kOOShaderUniformTypeDouble
@ kOOShaderUniformTypeObject
@ kOOShaderUniformTypeHPVector
@ kOOShaderUniformTypeInt
@ kOOShaderUniformTypeChar
@ kOOShaderUniformTypeInvalid
Quaternion(* QuaternionReturnMsgSend)(id, SEL)
NSPoint(* PointReturnMsgSend)(id, SEL)
OOMatrix(* MatrixReturnMsgSend)(id, SEL)
float alphaComponent()
Definition OOColor.m:486
float blueComponent()
Definition OOColor.m:362
float redComponent()
Definition OOColor.m:350
float greenComponent()
Definition OOColor.m:356