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:

  • How to make generics in collection type constraint?
  • Custom font in XCode 4.3.2
  • Incorrect rotation of a view controller in iOS 6
  • Distinguish between an iPhone App crashing and being killed
  • NSUserDefaults not working with UITableView in Swift
  • What is the iOS 6 user agent string?

  • 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).

    2 Solutions Collect From Internet About “Marrying Core Animation with OpenGL ES”

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

    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.