Rendering

This example shows and explain how rendering works in combination with the Wikitude SDK Native API. There are two methods of rendering available in the Wikitude Native SDK. We call them internal and external rendering. Internal means the OpenGL view is setup by the Wikitude SDK and the SDK user can define custom rendering, that is executed by the Wikitude SDK. On the other hand external rendering means the SDK user sets up the OpenGL view and integrates the Wikitude SDK into this rendering setup.

External Rendering

Most of the examples in the Wikitude Native SDK example application use external rendering, simply because it might be the preferred usage by developers. The example application comes with a simple OpenGL ES 2 view (ExternalEAGLView) and an also very simple renderer (ExternalRenderer).

To activate external rendering you need to pass WTRenderingMode_External to the WTWikitudeNativeSDK method -initWithRenderingMode:delegate.

self.wikitudeSDK = [[WTWikitudeNativeSDK alloc] initWithRenderingMode:WTRenderingMode_External delegate:self];

During the Wikitude Native SDK startup phase, which is initialized with a call to -start:completion:, several methods from the WTWikitudeNativeSDKDelegate protocol are called. They are needed to prepare the Wikitude Native SDK for external rendering. All methods in the WTWikitudeNativeSDKDelegate protocol are marked as optional, but the Wikitude Native SDK will call its -wikitudeNativeSDK:didEncounterInternalError: delegate method if a required method is not implemented or returned an invalid value. The required methods are:

 - (void)wikitudeNativeSDK:(WTWikitudeNativeSDK * __nonnull)wikitudeNativeSDK didCreatedExternalUpdateHandler:(WTWikitudeUpdateHandler __nonnull)updateHandler
{
    self.wikitudeUpdateHandler = updateHandler;
}

- (void)wikitudeNativeSDK:(WTWikitudeNativeSDK * __nonnull)wikitudeNativeSDK didCreatedExternalDrawHandler:(WTWikitudeDrawHandler __nonnull)drawHandler
{
    self.wikitudeDrawHandler = drawHandler;
}

- (CGRect)eaglViewSizeForExternalRenderingInWikitudeNativeSDK:(WTWikitudeNativeSDK * __nonnull)wikitudeNativeSDK
{
    return self.eaglView.bounds;
}

- (EAGLContext *)eaglContextForVideoCameraInWikitudeNativeSDK:(WTWikitudeNativeSDK * __nonnull)wikitudeNativeSDK
{
    if (!_sharedWikitudeEAGLCameraContext )
    {
        EAGLContext *rendererContext = [self.renderer internalContext];
        self.sharedWikitudeEAGLCameraContext = [[EAGLContext alloc] initWithAPI:[rendererContext API] sharegroup:[rendererContext sharegroup]];
    }
    return self.sharedWikitudeEAGLCameraContext;
}

The first two methods provide block objects, that need to be called every frame from the external rendering system. There purpose is to update internal SDK logic and draw the camera stream in OpenGL. The last one is used to get to know the current OpenGL view size in order to draw the camera frame fullscreen and with the correct aspect ratio. External rendering also requires a shared EAGLContext object that is used by the Wikitude Native SDK to issue camera related OpenGL ES 2 calls. This way custom rendering and Wikitude rendering do not influence each other and less conflicts can occur.

An example of how those block objects can be used is the following:

if ( self.wikitudeUpdateHandler
    &&
     self.wikitudeDrawHandler )
{
    self.wikitudeUpdateHandler();
    self.wikitudeDrawHandler();
}
// ...external rendering code...

Such a snippet is typically found somewhere in the external render loop.

Internal Rendering

In case no OpenGL ES 2 rendering is already setup in the hosting Wikitude Native SDK application, the Wikitude Native SDK brings its own set of OpenGL ES 2 compatible classes to do so. These classes are WTEAGLView and WTRenderer. Objects of those classes can be retrieved through WTWikitudeNativeSDK methods. To start the Wikitude Native SDK with internal rendering, pass WTRenderingMode_Internal to the -initWithRenderingMode:delegate: method.

self.wikitudeSDK = [[WTWikitudeNativeSDK alloc] initWithRenderingMode:WTRenderingMode_Internal delegate:self];

self.wikitudeRenderer = [self.wikitudeSDK createRenderer];
self.wikitudeEAGLView = [self.wikitudeSDK createEAGLView];

After all required objects are created, view and renderer have to be connected through a call to setRenderer:.

[self.wikitudeEAGLView setRenderer:self.wikitudeRenderer];

After those two objects are connected, the view has to be added to the view hierarchy of your application. This can either be done using Storyboards or programmatically. The Wikitude Native SDK example application does it in code.

[self.view addSubview:self.wikitudeEAGLView];
[self.wikitudeEAGLView setTranslatesAutoresizingMaskIntoConstraints:NO];

NSDictionary *views = NSDictionaryOfVariableBindings(_wikitudeEAGLView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_wikitudeEAGLView]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_wikitudeEAGLView]|" options:0 metrics:nil views:views]];

To do some custom OpenGL ES 2 calls from outside the Wikitude Native SDK, custom update and draw block objects can be passed to the Wikitude Native SDK. The corresponding delegate methods are called during the startup phase.

- (WTCustomUpdateHandler)wikitudeNativeSDKNeedsExternalUpdateHandler:(WTWikitudeNativeSDK * __nonnull)wikitudeNativeSDK
{
    /* Intentionally returning a nil handler here */
    return ^(){};
}

- (WTCustomDrawHandler)wikitudeNativeSDKNeedsExternalDrawHandler:(WTWikitudeNativeSDK * __nonnull)wikitudeNativeSDK
{
    return ^(){
        if ( _isTracking ) {
            [self.renderableRectangle drawInContext:[EAGLContext currentContext]];
        }
    };
}

In this snippet only custom rendering is done and no update logic.