Using Opengl

From AwkwardTV
Jump to: navigation, search


Drawing Using OpenGL

If you want to draw with OpenGL directly do the following:

You have to:

  • Add /System/Library/Frameworks/Opengl.framework
  • import the Header
#import <OpenGL/OpenGL.h>
#import <OpenGL/glu.h>
  • Subclass BRRenderLayer
  • add a method (void)renderLayer;
  • In renderLayer do the following:
- (void)renderLayer
	int oldMatMode;
	glGetIntegerv(GL_MATRIX_MODE, &oldMatMode);

       // Using glPushAttrib you don't need to worry about setting render states
       // Of course using GL_ALL_ATTRIB_BITS is a bit overkill, you should 
       // restrict it to the actual types of attributes you really set

       // This is needed for the glColor4f alphaValue to take effect

       // Setting the Viewport so that it takes into account the frame of the layer.
       // Make sure that your control sets the layers frame absolut.
       glViewport(_frame.origin.x, _frame.origin.y, _frame.size.width, _frame.size.height);

	gluPerspective(45.0, _frame.size.width / _frame.size.height, 0.1, 100.0);
	gluLookAt(	0.0,0.0,-10.0,
				0.0,1.0,0.0 );
	_rot += 0.2;


       // Restore the previous Attributes


_rot is a member variable of your subclass. Initialize it with 0.0 or something

The important part is to:

  • If you enable something (for instance GL_TEXTURE_RECTANGLE_ARB, used by most BR layers), always disable it at the end.
  • glPush/PopMatrix all the matrices you change
  • Reset the glMatrixMode to the old one after you are done.

You do not need to call [super renderLayer] unless you want the superclass's drawing to appear on top of yours. BRRenderLayer does nothing in -renderLayer, and you will usually implement this function yourself to provide all your own rendering. Similarly, you usually should not explicitly enable/disable blending in your render routine; the scene is set up for blending using glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA). The framework may disable this, however. So if you *need* blending, or if you need a different algorithm, *always* fetch the the current setting first, and restore that upon completion.

Periodic Updates

Updates are fairly simple to do, although a proper, clean implementation requires a couple of extra things:

Firstly, call [self setNeedsUpdates: YES] to get your layer attached to the CoreVideo rendering callback (usually called with each refresh, or thereabouts). You then need to implement the function - (BOOL) updateFrameForTime: (const CVTimeStamp *) timeStamp in your layer. This function will be called each time the CoreVideo refresh timer fires.

For the structure declaration, you need to include <QuartzCore/CVBase.h>. The structure declaration looks as follows:

typedef struct
    uint32_t			version;		// Currently will be 0.
    int32_t			videoTimeScale;     	// Video timescale (units per second)
    int64_t			videoTime;		// This represents the start of a frame (or field for interlaced) .. think vsync  - still not 100% sure on the name
    uint64_t			hostTime;		// Host root timebase time
    double			rateScalar;		// Current rate as measured by the timestamps divided by the nominal rate
    int64_t			videoRefreshPeriod;    	// Hint for nominal output rate
    CVSMPTETime			smpteTime;
    uint64_t			flags;
    uint64_t			reserved;
} CVTimeStamp; 

Because the callbacks aren't guaranteed to occur at specific intervals known at compile-time, it is advised that you do the following:

  • Add two member variables to your class: double _timeFreq; and double _prevTime;.
  • In your initializer, do the following: _timeFreq = CVGetHostClockFrequency( );
  • Implement the -updateFrameForTime: function similar to the following, to update on a specific schedule:
- (BOOL) updateFrameForTime: (const CVTimeStamp *) time
    BOOL result = NO;

        double hostTime = (double) time->hostTime;
        double now = hostTime / _timeFreq;

        // this will not update unless 1/30th of a second has passed since the last update
        if ( now < _prevTime + (1.0 / 30.0) )
            // returning NO will cause the layer to NOT be redrawn
            result = NO;
            // change whatever you want to change here, as a function of time elapsed
            _prevTime = now;
            // return YES to have your layer redrawn
            result = YES;

    return ( result );

Adding Effects to a Layer

There is a means of rendering a layer using 'effects' — this is seen for example on the scrolling done by menu items whose title is too long to fit within the allotted space. In that case, the text (as it scrolls, using the method outlined above) fades in and out on each side. This is achieved by settings an effects delegate on the layer. The delegate is not retained, so it is appropriate for a layer to set itself as its own effects delegate, like so:

[self setEffectsDelegate: self];

The effects are applied through a texture. When an effects delegate is set for a layer, a BRPBufferTexture object is created (the _effectsTexture member variable). Then when the layer is rendered (-renderLayer is called by a private method in BRRenderLayer), the GL context is set to that of the pixel buffer, and so -renderLayer actually draws into that buffer. Then the buffer is passed to the effects delegate, which should implement - (void) renderLayerTexture: (BRTexture *) texture to create the 'effects' by drawing the texture on screen with any chosen transformations.

For example, to implement a fade similar to the one in BRAutoScrollingTextLayer, you can use the following implementation:

- (void) renderLayerTexture: (BRTexture *) texture
    This is defined in <BackRow/CDStructures.h>
    struct BRTextureInfo
        unsigned int textureID;
        unsigned int target;        // GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_2D, etc.
        struct _NSSize size;         // The size of the texture
        float topLeft[2];                // vectors for the corners of the image
        float topRight[2];
        float bottomLeft[2];
        float bottomRight[2];

    const struct BRTextureInfo * texInfo = [texture textureInfo];

    // enable the texture's target and bind in the texture
    glEnable( texInfo->target );
    glBindTexture( texInfo->target, texInfo->textureID );

    // using GL_QUAD_STRIP, because we'll be defining vertical lines as the fade endpoints, not whole rectangles
    glBegin( GL_QUAD_STRIP );

    // the distance within the texture for the fade
    float inset = texInfo->size.width * 0.05f;

    // start at black for the left-hand end:
    glColor4f( 0.0f, 0.0f, 0.0f, 0.0f );

    glTexCoord2f( 0.0f, 0.0f );
    glVertex2f( 0.0f, 0.0f );

    glTexCoord2f( 0.0f, texInfo->size.height );
    glVertex2f( 0.0f, texInfo->size.height );

    // now fade up to white a little way in
    glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );

    glTexCoord2f( inset, 0.0f );
    glVertex2f( inset, 0.0f );

    glTexCoord2f( inset, texInfo->size.height );
    glVertex2f( inset, texInfo->size.height );

    // begins to fade out here (next white point)
    glTexCoord2f( texInfo->size.width - inset, 0.0f );
    glVertex2f( texInfo->size.width - inset, 0.0f );

    glTexCoord2f( texInfo->size.width - inset, texInfo->size.height );
    glVertex2f( texInfo->size.width - inset, texInfo->size.height );

    // fades to black by the end of the texture
    glColor4f( 0.0f, 0.0f, 0.0f, 0.0f );

    glTexCoord2f( texInfo->size.width, 0.0f );
    glVertex2f( texInfo->size.width, 0.0f );

    glTexCoord2f( texInfo->size.width, texInfo->size.height );
    glVertex2f( texInfo->size.width, texInfo->size.height );

    glEnd( );

    // release the texture
    glBindTexture( texInfo->target, 0 );

    // disable the target
    glDisable( texInfo->target );