Search code examples
iosopengl-esglkit

Render multiple GLKViewControllers at a time


I have a simple GLKViewcontroller subclass that renders a texture when told. This works great on its own, exactly what I need it to do. However, for some reason, when I am trying to use it in a scene in which I have 6 copies of the same view controller at a time (set up as container views pointing to the same view) it doesn't work. I have a load and show texture method to select which texture to draw from a map of loaded textures. It would seem that only the last view i loaded the textures into draws them properly, the remainder show up as black squares.

Here's a screenshot of my storyboard:

enter image description here

Here's my load and draw code:

-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    [EAGLContext setCurrentContext:self.context];
    self.view.backgroundColor = [UIColor clearColor];
    self.view.opaque = NO;
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    //sprite render:
    if ( self.showTexture )
    {
        if ( firstDraw )
        {
            self.imageShowTime = [ATAppDelegate getCurrentTime];
            firstDraw = NO;
        }
        self.effect.texture2d0.name = selectedTexture.name;
        self.effect.texture2d0.enabled = YES;
        self.effect.transform.modelviewMatrix = self.modelMatrix;

        [self.effect prepareToDraw];

        long offset = (long)&_quad;

        glEnableVertexAttribArray(GLKVertexAttribPosition);
        glEnableVertexAttribArray(GLKVertexAttribTexCoord0);

        glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, geometryVertex)));
        glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, textureVertex)));

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    }
    else
    {
        firstDraw = YES;
    }
}

-(void)loadTextures:(NSArray *)textureNames
{
    if ( textures == nil )
    {
        textures = [[NSMutableDictionary alloc] init];
    }
    [textures removeAllObjects];

    NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
                              [NSNumber numberWithBool:YES],
                              GLKTextureLoaderOriginBottomLeft,
                              nil];
    NSError * error;

    for ( NSString *texName in textureNames )
    {

        NSString *path = [[NSBundle mainBundle] pathForResource:texName ofType:nil];
        GLKTextureInfo *newTex = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
        if ( newTex == nil || error != nil )
        {
            NSLog(@"Error loading file: %@", [error localizedDescription]);
            return ;
        }

        NSLog(@"Loaded texture: %@ for name: %@", newTex, texName);

        CGSize contentSize = CGSizeMake(newTex.width, newTex.height);

        NSDictionary *texInfo = @{@"texture" : newTex, @"contentSize" : [NSValue valueWithCGSize:contentSize]};

        textures[texName] = texInfo;
    }
}

-(void)showTexture:(NSString *)texture
{
    NSDictionary *texInfo = textures[texture];

    if ( texInfo == nil )
    {
        NSLog(@"ERROR: no texture info found for texture name :%@ in %@", texture, textures);
    }
    else
    {
        NSLog(@"Showing texture: %@", texInfo);
    }

    selectedTexture = texInfo[@"texture"];
    curContentSize = [texInfo[@"contentSize"] CGSizeValue];
}

Solution

  • The issue is most likely in the openGL contexts. The thing is each of the controllers will create each own context and each must be set before used in any, the drawing, presenting, texture loading.

    I never use such nonsense as the GLKView or the GLKViewController so bear with me. Either the issue is that the textures are loaded in only one context for which you either need to load them for each and every context (the view controller) or find a way to create a context with the share group from one of the controllers: When you create a context you can get a share group from it and then initialize the new context with this share group (a context property). The result is the new context may use the elements such as textures created on the primary context and vice versa. A bigger issue may be that these controllers are doing some additional work with the openGL and the context is not reset before doing so.

    Anyway making multiple views using openGL should work just fine so I hope you find a nice solution for your issue. Maybe all you need to do is set the current context in the top of your loadTextures method.