Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Marrying Core Animation with OpenGL ES

Edit: I suppose instead of the long explanation below I might also ask: Sending -setNeedsDisplay to an instance of CAEAGLLayer does not cause the layer to redraw (i.e., -drawInContext: is not called). Instead, I get this console message:

<GLLayer: 0x4d500b0>: calling -display has no effect.

Is there a way around this issue? Can I invoke -drawInContext: when -setNeedsDisplay is called? Long explanation below:


I have an OpenGL scene that I would like to animate using Core Animation animations.

Following the standard approach to animate custom properties in a CALayer, I created a subclass of CAEAGLLayer and defined a property sceneCenterPoint in it whose value should be animated. My layer also holds a reference to the OpenGL renderer:

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "ES2Renderer.h"

@interface GLLayer : CAEAGLLayer
{
    ES2Renderer *renderer;
}

@property (nonatomic, retain) ES2Renderer *renderer;
@property (nonatomic, assign) CGPoint sceneCenterPoint;

I then declare the property @dynamic to let CA create the accessors, override +needsDisplayForKey: and implement -drawInContext: to pass the current value of the sceneCenterPoint property to the renderer and ask it to render the scene:

#import "GLLayer.h"

@implementation GLLayer

@synthesize renderer;
@dynamic sceneCenterPoint;

+ (BOOL) needsDisplayForKey:(NSString *)key
{
    if ([key isEqualToString:@"sceneCenterPoint"]) {
        return YES;
    } else {
        return [super needsDisplayForKey:key];
    }
}

- (void) drawInContext:(CGContextRef)ctx
{
    self.renderer.centerPoint = self.sceneCenterPoint;
    [self.renderer render];
}
...

(If you have access to the WWDC 2009 session videos, you can review this technique in session 303 ("Animated Drawing")).

Now, when I create an explicit animation for the layer on the keyPath @"sceneCenterPoint", Core Animation should calculate the interpolated values for the custom properties and call -drawInContext: for each step of the animation:

- (IBAction)animateButtonTapped:(id)sender
{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"sceneCenterPoint"];
    animation.duration = 1.0;
    animation.fromValue = [NSValue valueWithCGPoint:CGPointZero];
    animation.toValue = [NSValue valueWithCGPoint:CGPointMake(1.0f, 1.0f)];
    [self.glView.layer addAnimation:animation forKey:nil];
}

At least that is what would happen for a normal CALayer subclass. When I subclass CAEAGLLayer, I get this output on the console for each step of the animation:

2010-12-21 13:59:22.180 CoreAnimationOpenGL[7496:207] <GLLayer: 0x4e0be20>: calling -display has no effect.
2010-12-21 13:59:22.198 CoreAnimationOpenGL[7496:207] <GLLayer: 0x4e0be20>: calling -display has no effect.
2010-12-21 13:59:22.216 CoreAnimationOpenGL[7496:207] <GLLayer: 0x4e0be20>: calling -display has no effect.
2010-12-21 13:59:22.233 CoreAnimationOpenGL[7496:207] <GLLayer: 0x4e0be20>: calling -display has no effect.
...

So it seems that, possibly for performance reasons, for OpenGL layers, -drawInContext: is not getting called because these layers do not use the standard -display method to draw themselves. Can anybody confirm that? Is there a way around it?

Or can I not use the technique I laid out above? This would mean I would have to implement the animations manually in the OpenGL renderer (which is possible but not as elegant IMO).

like image 495
Ole Begemann Avatar asked Dec 21 '10 15:12

Ole Begemann


2 Answers

You can override display instead of drawInContext. During the animation, the animated value is in the presentation layer.

- (void) display
{
    GLLayer* myPresentationLayer = (GLLayer*)[self presentationLayer];
    self.renderer.centerPoint = myPresentationLayer.sceneCenterPoint;
    [self.renderer render];
}

At the end the presentation layer will have the model layer value, so you’ll need to set the final value on the model layer before starting the animation.

like image 133
Jeff Argast Avatar answered Nov 15 '22 23:11

Jeff Argast


Have you tried making a parent layer above the OpenGL layer and animating that instead?

like image 21
Dad Avatar answered Nov 15 '22 23:11

Dad