CADisplayLink and static variable magic in Apple OpenGl ES Sample - objective-c

I would like an explanation of why XCode's OpenGl ES Sample works, please. It does the following to start the drawFrame method (in the blablaViewController.m - name is dependent on the project's name):
//sets up a CADisplayLink to do a regular (draw & update) call like this
CADisplayLink *aDisplayLink = [[UIScreen mainScreen] displayLinkWithTarget:self
selector:#selector(drawFrame)];
[aDisplayLink setFrameInterval:animationFrameInterval];
[aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
and inside the drawFrame method it does the following:
//start of method
...
static float transY = 0.0f;
...
//Quite a lot of OpenGl code, I am showing only parts of the OpenGL ES1 version:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f, (GLfloat)(sinf(transY)/2.0f), 0.0f);
transY += 0.075f;
...
//end of method
I don't know a lot of Objective C yet, but the way this transY variable is reset, then incremented in the same method is very weird. Since the GL_MODELVIEW matrix is reset to identity before being shifted, I don't think it could keep an accumulated value in opengl somewhere.
Is the static keyword the trick here? Does Objective C ignore all future variable declarations once something has been declared static once?
Thanks for the help!

Static variables get initializated at compile time in the binary, so only once, and for that reason you're forbidden to assign dynamic values for their initialization. Here, the variable transY is not set to 0.0 at every method call, but just on startup. That's why subsequent calls of the method can retrieve the old value.

Related

OpenGL with Cocoa: No matching function call when trying to call CGLLockContext([[self openGLContext] CGLContextObj]);

I am learning OpenGL. To get an OpenGL context setup I was following the GlEssentials example from Apple. The GlContext is there locked in the draw method as follows:
- (void) drawView
{
[[self openGLContext] makeCurrentContext];
// We draw on a secondary thread through the display link
// When resizing the view, -reshape is called automatically on the main
// thread. Add a mutex around to avoid the threads accessing the context
// simultaneously when resizing
CGLLockContext([[self openGLContext] CGLContextObj]);
[m_renderer render];
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
When I tried to call CGLLockContext with exactly the same arguments as above in my view class I the following error:
No matching function for call to 'CGLLockContext
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/OpenGL.framework/Headers/OpenGL.h:111:17: Candidate function not viable: cannot convert argument of incomplete type 'void *' to 'CGLContextObj' (aka '_CGLContextObject *')
Quickly inserting a typecast fixed the issue:
CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
Question is why? In Apples example it works fine without this typecast.
Two thoughts:
1) Are you doing this inside a C++ or ObjC++ file? That whole “candidate function” thing sounds like C++ to me, but I don’t really know C++.
2) Are your compiler flags (especially warnings and errors) the same in your project files as they are in Apple’s sample project. (I took a quick look at Xcode 5’s compiler settings and nothing jumped out at me.)

How do i set a CGContextRef property rather than using UIGraphicsGetCurrentContext()

Ive been trying to work with UIGraphicsGetCurrentContext and CGContextRef. I was told that using UIGraphicsGetCurrentContext() numerous times is bad and to rather work on using CGContextRef and referring to it.
I have been trying to work on the second part and I am having issues setting the #property and referring to it. Can someone give me a declaration and usage example of this? Tried googling and couldn't find any references to it.
Ta
You probably shouldn't store the return value from UIGraphicsGetCurrentContext in a property. You usually either don't know how long the context will be valid, or the context has a short lifetime anyway. For example, if you're calling UIGraphicsGetCurrentContext from your drawRect: method, you don't know how long that context will survive after you return from drawRect:. If you're calling UIGraphicsGetCurrentContext after calling UIGraphicsBeginImageContextWithOptions, you will probably be calling UIGraphicsEndImageContext soon anyway. It would be inappropriate to keep references to those contexts around.
If you are calling many Core Graphics functions on the same context, then you want to store the context in a local variable. For example, here is a drawRect: method from one of my test projects:
- (void)drawRect:(CGRect)dirtyRect {
NSLog(#"drawRect:%#", NSStringFromCGRect(dirtyRect));
[self layoutColumnsIfNeeded];
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSaveGState(gc); {
// Flip the Y-axis of the context because that's what CoreText assumes.
CGContextTranslateCTM(gc, 0, self.bounds.size.height);
CGContextScaleCTM(gc, 1, -1);
for (NSUInteger i = 0, l = CFArrayGetCount(_columnFrames); i < l; ++i) {
CTFrameRef frame = CFArrayGetValueAtIndex(_columnFrames, i);
CGPathRef path = CTFrameGetPath(frame);
CGRect frameRect = CGPathGetBoundingBox(path);
if (!CGRectIntersectsRect(frameRect, dirtyRect))
continue;
CTFrameDraw(frame, gc);
}
} CGContextRestoreGState(gc);
}
You can see that I'm doing a bunch of stuff with the context: I'm saving and restoring the graphics state, I'm changing the CTM, and I'm drawing some Core Text frames into it. Instead of calling UIGraphicsGetCurrentContext many times, I call it just once and save the result in a local variable named gc.

cocos2d sub-classing

I know that cocos2d has scheduling callbacks to do nice things but when you need to use one CCAction (like CCMoveTo one) in order to move a sprite from position a to b, you do not have the ability to make small position arrangements to the sprite position for as long as the action is in effect.
The only possible way I found is by making a sub-class of CCMoveTo in order to check for obstacles and therefore provide some kind of movement to the left or right to a sprite that was moving from top to the bottom of the iPhone screen. The problem is that the sub-class does not have access to the parent class' instance variables (like the startPosition_ one) because they have not been declared as properties.
So I used the following snippet to overcome this situation but I wonder if I am doing something wrong...
- (void)myUpdate:(ccTime)time {
if(delegate && method_) {
NSNumber *num = (NSNumber *)[delegate performSelector:method_ withObject:ownTarget];
if(num) {
double xpos = [num doubleValue];
[num release];
CCMoveTo *parent = [super retain];
parent->startPosition_.x += xpos;
[parent release];
}
[super update:time];
}
Is it correct to retain/release the super-class? The "[super update:time];" at the bottom of the code will make the final positioning.
CCMoveTo *parent = [super retain];
Ouch! This statement makes absolutely no sense. It is the same as writing:
[self retain];
As for accessing the super class' instance variables: unless they're declared #private you can access them. I just checked: they're not #private. You should be able to write in your subclass:
startPosition_.x += xpos;
If that doesn't work make sure your class is really a subclass of CCMoveTo, and not some other class.
Finally, I'd like to say that actions are very limited when it comes to implementing gameplay. You're probably much better off to simply animate your game objects by modifying their position property every frame, based on a velocity vector. You have much more freedom over the position and position updates, and none of the side effects of actions such as a one-frame delay every time you run a new action.
-(void) update:(ccTime)delta
{
// modify velocity based on whatever you need, ie gravity, or just heading in one direction
// then update the node's position by adding the current velocity to move it:
self.position = CGPointMake(self.position.x + velocity.x, self.position.y + velocity.y);
}

Setting the frame.size using the dot operator

I have a UIController and as you all know UIController is associated to a view and you can access it using the getter and setters methods which are synthesized
UIController controller = init code ..
..
controller.view -> this gives me my UIView object which retained and autoreleased, this will be synthesized get method(If at all my synthesized getmethod understanding is correct)
controller.view.frame -> this gives me my CGRect struct
controller.view.frame.size -> CGSize struct
why cannot I assign a value directly to this frame structure
controller.view.frame.size.width = 20;
for the above statement I get this error "lvalue required as left operand of assignment"
This is a normal c dot operator I think it should work.Please enlighten if I am missing anything
Using the dot operator in this situation is using the frame getter method behind the scenes. Since the frame property is a CGRect, which is a simple C struct, frame returns you a copy of the value, not a pointer to the value. Changing it will modify the CGRect you have copied locally on the stack, not the CGRect of your view's frame property. To update the actual frame property you must go through the setter method [yourView setFrame:yourNewFrame]; or yourView.frame = yourNewFrame;.
the easiest to solve that is to set the whole frame again
controller.view.frame = CGRectMake(controller.view.frame.origin.x, controller.view.frame.origin.y, 20, controller.view.frame.size.height);
the reason why this is not working directly is described in James link, it has only getters.
Dot syntax is just syntax sugar.
In this case, this code:
controller.view.frame.size.width = 20;
Is actually this code:
[[controller view] frame].size.width = 20;
In C terms, that's like this:
ViewGetFrame(ControllerGetView(controller)).size.width = 20;
Bottom line is you can't set a subfield of a function result this way. And even if you could, it wouldn't affect the original but only a copy.

Why I can't draw in a loop? (Using UIView in iPhone)

I can draw many things using this :
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"dummy2.png" ofType:nil];
UIImage *img = [UIImage imageWithContentsOfFile:imagePath];
image = CGImageRetain(img.CGImage);
CGRect imageRect;
double x = 0;
double y = 0;
for (int k=0; k<someValue; k++) {
x += k;
y += k;
imageRect.origin = CGPointMake(x, y);
imageRect.size = CGSizeMake(25, 25);
CGContextDrawImage(UIGraphicsGetCurrentContext(), imageRect, image);
}
}
CGImageRelease(img.CGImage);
So, it works, so, I put it into a command object's execute method. Then, I want to do similar thing, but this time, my execute method only do this:
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"dummy2.png" ofType:nil];
UIImage *img = [UIImage imageWithContentsOfFile:imagePath];
image = CGImageRetain(img.CGImage);
CGRect imageRect;
double x = inComingX;
double y = inComingY;
imageRect.origin = CGPointMake(x, y);
imageRect.size = CGSizeMake(25, 25);
CGContextDrawImage(UIGraphicsGetCurrentContext(), imageRect, image);
CGImageRelease(img.CGImage);
This time, this is also a Command, and it is the execute method. But I take the for loop away. I will have another method that pass the inComingX , and inComingY into my Command object.
My Drawing method is simply execute the Cmd that passed in my drawingEngine:
-(void)drawInContext:(CGContextRef)context
{
[self.cmdToBeExecuted execute];
}
I also have the assign method to assign the command,:
-(void)assignCmd:(Command* )cmd{
self.cmdToBeExecuted = cmd;
}
And this is the way I called the drawingEngine
for(int k=0; k<5; k++){
[self.drawingEngine assignCmd:[DrawingCmd setDrawingInformation:(10*k):0:#"dummy.png"]];
[self.drawingEngine setNeedsDisplay];
}
It can draw, but the sad thing is it only draw the last one. Why? and how to fix it? I can draw all the things in my First code, but after I take the loop outside, and use the loop in last code, it just only draw the last one. Plz help
That's because setNeedsDisplay does not actually call drawRect:. It simply schedules the view to be redrawn at the next "convenient" time, which is likely the next time, the application re-enters the run-loop. Since you overwrite the remembered command object on each call to the assignment function, by the time, the drawRect: is actually called, only the last assigned command is available and will be drawn.
A better way to do it would be: remember all commands to be drawn instead just the last one, say in an array, like:
#interface MyCanvas {
...
NSMutableArray* commandList;
...
}
and add commands to that array instead of assigning a single command member:
-(void) addCommand:(Command*) cmd {
[self.commandList addObject: cmd];
}
The commands should then be processed in your draw method
for( Command* cmd in self.commandList ) {
[cmd execute ...];
}
Alternatively, you could define "complex" commands, which consist of more than a single drawing step.
(EDIT to answer the question in the comments): Your original code did work, because it does the work all in one place in a single invocation of the appropriate draw method. Your last code does not draw anything at all while it runs. It simply remembers (via command object) that something has to be done, and notifies the view, that it should redraw itself on the next convenient occasion. It is important to note, that setNeedsDisplay will not cause any repainting to be done directly. It simply marks the view as "dirty", which will be picked up by other code in the Cocoa run-time later.
There is another thing in your code which I find slightly suspicious: your method drawInContext: takes a context argument, which is simply ignored. Neither is it passed on to the execute method of you command object, nor is it installed as some kind of "current" drawing context in an instance variable or somesuch. If you expect the code in drawRect: (or the command's execute method) to actually use that context, you have to pass it on to whoever is supposed to use it.