Выйти замуж за Core Animation с помощью OpenGL ES

Изменить: я полагаю, что вместо длинного объяснения ниже я также могу спросить: Отправка -setNeedsDisplay экземпляру CAEAGLLayer не приводит к перерисовке слоя (т.е. -drawInContext: не вызывается). Вместо этого я получаю это сообщение консоли:

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

Есть ли способ решить эту проблему? Могу ли я вызвать -drawInContext:, когда вызывается -setNeedsDisplay? Длинное объяснение ниже:


У меня есть сцена OpenGL, которую я хотел бы оживить, используя анимацию Core Animation.

Следуя стандартным подходам к анимации пользовательских свойств в CALayer, я создал подкласс CAEAGLLayer и определил свойство sceneCenterPoint в нем, значение которого должно быть анимировано. Мой слой также содержит ссылку на рендеринг OpenGL:

#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;

Затем я объявляю свойство @dynamic, чтобы CA создавал accessors, переопределял +needsDisplayForKey: и реализовал -drawInContext:, чтобы передать текущее значение свойства sceneCenterPoint в средство визуализации и попросить его визуализировать сцену:

#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];
}
...

(Если у вас есть доступ к видеороликам сеанса WWDC 2009, вы можете просмотреть этот метод в сеансе 303 ( "Анимированный чертеж" )).

Теперь, когда я создаю явную анимацию для слоя на keyPath @"sceneCenterPoint", Core Animation должна рассчитать интерполированные значения для пользовательских свойств и вызвать -drawInContext: для каждого шага анимации:

- (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];
}

По крайней мере, это будет происходить для обычного подкласса CALayer. Когда я подклассом CAEAGLLayer, я получаю этот вывод на консоли для каждого шага анимации:

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

Похоже, что, по соображениям производительности, для слоев OpenGL, -drawInContext: не получает вызов, потому что эти слои не используют стандартный метод -display для рисования. Кто-нибудь может это подтвердить? Есть ли способ обойти это?

Или я не могу использовать технику, изложенную выше? Это означало бы, что я должен был бы выполнить анимацию вручную в рендерере OpenGL (что возможно, но не как изящная ИМО).

Ответ 1

Пробовали ли вы создать родительский слой выше уровня OpenGL и анимировать это?

Ответ 2

Вы можете переопределить display вместо drawInContext. Во время анимации анимированное значение находится на уровне презентации.

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

В конце слой презентации будет иметь значение уровня модели, поэтому перед запуском анимации вам нужно будет установить окончательное значение на уровне модели.