GLSLShowpiece Lite/Viewer/Sources/OpenGLView.m
//------------------------------------------------------------------------- |
// |
// File: OpenGLView.m |
// |
// Abstract: Main rendering class |
// |
// Disclaimer: IMPORTANT: This Apple software is supplied to you by |
// Apple Inc. ("Apple") in consideration of your agreement to the |
// following terms, and your use, installation, modification or |
// redistribution of this Apple software constitutes acceptance of these |
// terms. If you do not agree with these terms, please do not use, |
// install, modify or redistribute this Apple software. |
// |
// In consideration of your agreement to abide by the following terms, and |
// subject to these terms, Apple grants you a personal, non-exclusive |
// license, under Apple's copyrights in this original Apple software (the |
// "Apple Software"), to use, reproduce, modify and redistribute the Apple |
// Software, with or without modifications, in source and/or binary forms; |
// provided that if you redistribute the Apple Software in its entirety and |
// without modifications, you must retain this notice and the following |
// text and disclaimers in all such redistributions of the Apple Software. |
// Neither the name, trademarks, service marks or logos of Apple Inc. |
// may be used to endorse or promote products derived from the Apple |
// Software without specific prior written permission from Apple. Except |
// as expressly stated in this notice, no other rights or licenses, express |
// or implied, are granted by Apple herein, including but not limited to |
// any patent rights that may be infringed by your derivative works or by |
// other works in which the Apple Software may be incorporated. |
// |
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
// |
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
// POSSIBILITY OF SUCH DAMAGE. |
// |
// Copyright (c) 2004-2007 Apple Inc., All rights reserved. |
// |
//------------------------------------------------------------------------- |
//------------------------------------------------------------------------ |
#import <Accelerate/Accelerate.h> |
//------------------------------------------------------------------------ |
#import "OpenGLView.h" |
//------------------------------------------------------------------------ |
//------------------------------------------------------------------------ |
static const double kViewRotationDegreesPerSecond = 20.0; |
static const double kExhibitHeightMovementUnitsPerSecond = 0.5; |
static const NSTimeInterval kScheduledTimerInSeconds = 1.0f/150.0f; |
//------------------------------------------------------------------------ |
//------------------------------------------------------------------------ |
typedef struct |
{ |
GLuint imageBitsPerPixel; |
GLuint imageBitsPerComponent; |
GLuint imageSamplesPerPixel; |
GLuint imageStorageSize; |
CGBitmapInfo imageBitmapInfo; |
vImage_Buffer imageBuffer; |
} CGImageBitmap; |
//------------------------------------------------------------------------ |
//------------------------------------------------------------------------ |
static inline GLboolean CheckForExtension( const char *extensionName, const GLubyte *extensions ) |
{ |
GLboolean bExtensionAvailable = gluCheckExtension( (GLubyte *)extensionName, extensions ); |
return bExtensionAvailable; |
} // CheckForExtension |
//------------------------------------------------------------------------ |
static inline void CheckForAndLogExtensionAvailable( const GLboolean extensionAvailable, const char *extensionName ) |
{ |
if ( extensionAvailable == GL_FALSE ) |
{ |
NSLog( @">> WARNING: \"%s\" extension is not available!\n", extensionName ); |
} // if |
} // CheckForExtensions |
//------------------------------------------------------------------------ |
static void CheckForShaders( NSOpenGLPixelFormatAttribute *thePixelAttributes ) |
{ |
const GLubyte *extensions = glGetString( GL_EXTENSIONS ); |
GLboolean bShaderObjectAvailable = CheckForExtension( "GL_ARB_shader_objects", extensions ); |
GLboolean bShaderLanguage100Available = CheckForExtension( "GL_ARB_shading_language_100", extensions ); |
GLboolean bVertexShaderAvailable = CheckForExtension( "GL_ARB_vertex_shader", extensions ); |
GLboolean bFragmentShaderAvailable = CheckForExtension( "GL_ARB_fragment_shader", extensions ); |
GLboolean bForceSoftwareRendering = ( bShaderObjectAvailable == GL_FALSE ) |
|| ( bShaderLanguage100Available == GL_FALSE ) |
|| ( bVertexShaderAvailable == GL_FALSE ) |
|| ( bFragmentShaderAvailable == GL_FALSE ); |
if ( bForceSoftwareRendering ) |
{ |
// Force software rendering, so fragment shaders will execute |
CheckForAndLogExtensionAvailable( bShaderObjectAvailable, "GL_ARB_shader_objects" ); |
CheckForAndLogExtensionAvailable( bShaderLanguage100Available, "GL_ARB_shading_language_100" ); |
CheckForAndLogExtensionAvailable( bVertexShaderAvailable, "GL_ARB_vertex_shader" ); |
CheckForAndLogExtensionAvailable( bFragmentShaderAvailable, "GL_ARB_fragment_shader" ); |
thePixelAttributes [3] = NSOpenGLPFARendererID; |
thePixelAttributes [4] = kCGLRendererGenericFloatID; |
} // if |
} // CheckForShaders |
//------------------------------------------------------------------------ |
static void CheckForClipVolumeHint( NSOpenGLPixelFormatAttribute *thePixelAttributes ) |
{ |
const GLubyte *extensions = glGetString(GL_EXTENSIONS); |
// Inform OpenGL that the geometry is entirely within the view volume and that view-volume |
// clipping is unnecessary. Normal clipping can be resumed by setting this hint to GL_DONT_CARE. |
// When clipping is disabled with this hint, results are undefined if geometry actually falls |
// outside the view volume. |
GLboolean bClipVolumeHintExtAvailable = CheckForExtension( "GL_EXT_clip_volume_hint", extensions ); |
if ( bClipVolumeHintExtAvailable == GL_TRUE ) |
{ |
glHint( GL_CLIP_VOLUME_CLIPPING_HINT_EXT,GL_FASTEST ); |
} // if |
} // CheckForClipVolumeHint |
//------------------------------------------------------------------------ |
static void CheckForGLSLHardwareSupport( NSOpenGLPixelFormatAttribute *thePixelAttributes ) |
{ |
// Create a pre-flight context to check for GLSL hardware support |
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: thePixelAttributes]; |
if ( pixelFormat != nil ) |
{ |
NSOpenGLContext *preflight = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; |
if ( preflight != nil ) |
{ |
[preflight makeCurrentContext]; |
CheckForShaders( thePixelAttributes ); |
CheckForClipVolumeHint( thePixelAttributes ); |
[preflight release]; |
} // if |
[pixelFormat release]; |
} // if |
} // CheckForGLSLHardwareSupport |
//------------------------------------------------------------------------ |
//------------------------------------------------------------------------ |
static void OpenGLSetupProjections() |
{ |
glMatrixMode(GL_PROJECTION); |
glLoadIdentity(); |
glFrustum(-0.3, 0.3, 0.0, 0.6, 1.0, 8.0); |
glMatrixMode(GL_MODELVIEW); |
glLoadIdentity(); |
glTranslatef(0.0, 0.0, -2.0); |
} // OpenGLSetupProjections |
//------------------------------------------------------------------------ |
static void OpenGLSetupBlending() |
{ |
glEnable(GL_BLEND); |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
} // OpenGLSetupBlending |
//------------------------------------------------------------------------ |
static void OpenGLSetup( const GLboolean setupBlending ) |
{ |
// For some OpenGL implementations, texture coordinates generated during rasterization |
// aren't perspective correct. However, you can usually make them perspective correct |
// by calling glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST). Colors generated at the |
// rasterization stage aren't perspective correct in almost every OpenGL implementation, |
// and can't be made so. For this reason, you're more likely to encounter this problem |
// with colors than texture coordinates. |
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); |
// Set up the projection |
OpenGLSetupProjections(); |
// Use blending |
if ( setupBlending ) |
{ |
OpenGLSetupBlending(); |
} // if |
// Turn on depth test |
glEnable( GL_DEPTH_TEST ); |
// front- or back-facing facets can be culled |
glEnable( GL_CULL_FACE ); |
} // OpenGLSetup |
//------------------------------------------------------------------------ |
static void OpenGLNoError( ) |
{ |
while( glGetError() != GL_NO_ERROR ) |
{ |
; |
} // while |
} // OpenGLNoError |
//------------------------------------------------------------------------ |
static void OpenGLDisableCapabilitiesForCopy( ) |
{ |
glDisable(GL_COLOR_TABLE); |
glDisable(GL_CONVOLUTION_1D); |
glDisable(GL_CONVOLUTION_2D); |
glDisable(GL_HISTOGRAM); |
glDisable(GL_MINMAX); |
glDisable(GL_POST_COLOR_MATRIX_COLOR_TABLE); |
glDisable(GL_POST_CONVOLUTION_COLOR_TABLE); |
glDisable(GL_SEPARABLE_2D); |
} // OpenGLDisableCapabilitiesForCopy |
//------------------------------------------------------------------------ |
static void OpenGLPixelMap( ) |
{ |
GLfloat values = 0.0f; |
glPixelMapfv(GL_PIXEL_MAP_R_TO_R, 1, &values); |
glPixelMapfv(GL_PIXEL_MAP_G_TO_G, 1, &values); |
glPixelMapfv(GL_PIXEL_MAP_B_TO_B, 1, &values); |
glPixelMapfv(GL_PIXEL_MAP_A_TO_A, 1, &values); |
} // OpenGLPixelMap |
//------------------------------------------------------------------------ |
static void OpenGLPixelStore( NSPoint *origin, NSRect *rect1, NSRect *rect2 ) |
{ |
glPixelStorei(GL_PACK_SWAP_BYTES, 0); |
glPixelStorei(GL_PACK_LSB_FIRST, 0); |
glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); |
glPixelStoref(GL_PACK_ROW_LENGTH, NSWidth(*rect2)); |
glPixelStoref(GL_PACK_SKIP_PIXELS, origin->x); |
glPixelStoref(GL_PACK_SKIP_ROWS, NSHeight(*rect2) - (origin->y + NSHeight(*rect1))); |
glPixelStorei(GL_PACK_SKIP_IMAGES, 0); |
} // OpenGLPixelStore |
//------------------------------------------------------------------------ |
static void OpenGLPixelTransfer( ) |
{ |
glPixelTransferi(GL_MAP_COLOR, 0); |
glPixelTransferf(GL_RED_SCALE, 1.0f); |
glPixelTransferf(GL_RED_BIAS, 0.0f); |
glPixelTransferf(GL_GREEN_SCALE, 1.0f); |
glPixelTransferf(GL_GREEN_BIAS, 0.0f); |
glPixelTransferf(GL_BLUE_SCALE, 1.0f); |
glPixelTransferf(GL_BLUE_BIAS, 0.0f); |
glPixelTransferf(GL_ALPHA_SCALE, 1.0f); |
glPixelTransferf(GL_ALPHA_BIAS, 0.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_RED_SCALE, 1.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_RED_BIAS, 0.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_GREEN_SCALE, 1.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_GREEN_BIAS, 0.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_BLUE_SCALE, 1.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_BLUE_BIAS, 0.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_ALPHA_SCALE, 1.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_ALPHA_BIAS, 0.0f); |
} // OpenGLPixelTransfer |
//------------------------------------------------------------------------ |
static inline void OpenGLReadRGBAPixels( NSRect *rect, GLvoid *pixels ) |
{ |
GLint x = (GLint) NSMinX(*rect); |
GLint y = (GLint) NSMinY(*rect); |
GLsizei width = (GLsizei) NSWidth(*rect); |
GLsizei height = (GLsizei) NSHeight(*rect); |
GLenum format = GL_RGBA; |
GLenum type = GL_UNSIGNED_BYTE; |
glReadPixels( x, y, width, height, format, type, pixels ); |
} // OpenGLReadRGBAPixels |
//------------------------------------------------------------------------ |
//------------------------------------------------------------------------ |
static GLvoid CGImageBitmapMemset( CGImageBitmap *imageBitmap ) |
{ |
imageBitmap->imageBitsPerPixel = 0; |
imageBitmap->imageBitsPerComponent = 0; |
imageBitmap->imageSamplesPerPixel = 0; |
imageBitmap->imageBitmapInfo = 0; |
imageBitmap->imageStorageSize = 0; |
imageBitmap->imageBuffer.width = 0; |
imageBitmap->imageBuffer.height = 0; |
imageBitmap->imageBuffer.rowBytes = 0; |
imageBitmap->imageBuffer.data = NULL; |
} // CGImageBitmapMemset |
//------------------------------------------------------------------------ |
static BOOL CGImageBitmapMalloc( CGImageRef imageRef, CGImageBitmap *imageBitmap ) |
{ |
BOOL imageBitmapAllocated = NO; |
imageBitmap->imageBitsPerPixel = 32; |
imageBitmap->imageBitsPerComponent = 8; |
imageBitmap->imageSamplesPerPixel = 4; |
imageBitmap->imageBitmapInfo = kCGImageAlphaPremultipliedLast; // RGBA |
imageBitmap->imageBuffer.width = CGImageGetWidth( imageRef ); |
imageBitmap->imageBuffer.height = CGImageGetHeight( imageRef ); |
imageBitmap->imageBuffer.rowBytes = imageBitmap->imageBuffer.width * imageBitmap->imageSamplesPerPixel; |
imageBitmap->imageStorageSize = imageBitmap->imageBuffer.rowBytes * imageBitmap->imageBuffer.height; |
imageBitmap->imageBuffer.data = (GLvoid *)malloc( imageBitmap->imageStorageSize ); |
if ( imageBitmap->imageBuffer.data != NULL ) |
{ |
imageBitmapAllocated = YES; |
} // if |
else |
{ |
CGImageBitmapMemset( imageBitmap ); |
} // else |
return imageBitmapAllocated; |
} // CGImageBitmapMalloc |
//------------------------------------------------------------------------ |
static BOOL CGImageBitmapFree( CGImageBitmap *imageBitmap ) |
{ |
BOOL imageBitmapFreed = NO; |
if ( imageBitmap->imageBuffer.data != NULL ) |
{ |
free( imageBitmap->imageBuffer.data ); |
imageBitmapFreed = YES; |
} // if |
CGImageBitmapMemset( imageBitmap ); |
return imageBitmapFreed; |
} // CGImageBitmapFree |
//------------------------------------------------------------------------ |
static CGContextRef CGImageBitmapContexMalloc( CGImageBitmap *imageBitmap ) |
{ |
CGContextRef imageContextRef = NULL; |
CGColorSpaceRef imageColorSpaceRef = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ); |
if ( imageColorSpaceRef != NULL ) |
{ |
imageContextRef = CGBitmapContextCreate( imageBitmap->imageBuffer.data, |
imageBitmap->imageBuffer.width, |
imageBitmap->imageBuffer.height, |
imageBitmap->imageBitsPerComponent, |
imageBitmap->imageBuffer.rowBytes, |
imageColorSpaceRef, |
imageBitmap->imageBitmapInfo |
); |
CGColorSpaceRelease( imageColorSpaceRef ); |
} // if |
return imageContextRef; |
} // CGImageBitmapContexMalloc |
//------------------------------------------------------------------------ |
static BOOL CGImageBitmapVerticalReflect( CGImageRef imageRef, CGImageBitmap *imageBitmap ) |
{ |
BOOL imageBitmapReflected = NO; |
CGContextRef imageContextRef = CGImageBitmapContexMalloc( imageBitmap ); |
if ( imageContextRef != NULL ) |
{ |
CGRect imageRect = { { 0, 0 }, { imageBitmap->imageBuffer.width, imageBitmap->imageBuffer.height } }; |
// The alpha will be added here |
CGContextDrawImage( imageContextRef, imageRect, imageRef ); |
vImageVerticalReflect_ARGB8888( &(imageBitmap->imageBuffer), &(imageBitmap->imageBuffer), kvImageNoFlags ); |
CGContextRelease( imageContextRef ); |
imageBitmapReflected = YES; |
} // if bitmap context |
return imageBitmapReflected; |
} // CGImageBitmapVerticalReflect |
//------------------------------------------------------------------------ |
static CGImageRef CGImageGetFromCGImageBitmap( CGImageBitmap *imageBitmap ) |
{ |
CGImageRef imageRef = NULL; |
// Create a data provider |
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithData( NULL, |
imageBitmap->imageBuffer.data, |
imageBitmap->imageStorageSize, |
NULL ); |
if ( imageDataProvider != NULL ) |
{ |
// Create a color space for the image |
CGColorSpaceRef imageColorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ); |
if ( imageColorSpace != NULL ) |
{ |
// Create the actual image |
GLfloat *imageDecode = NULL; |
bool imageShouldInterpolate = true; |
CGColorRenderingIntent imageRenderingIntent = kCGRenderingIntentDefault; |
imageRef = CGImageCreate( imageBitmap->imageBuffer.width, |
imageBitmap->imageBuffer.height, |
imageBitmap->imageBitsPerComponent, |
imageBitmap->imageBitsPerPixel, |
imageBitmap->imageBuffer.rowBytes, |
imageColorSpace, |
imageBitmap->imageBitmapInfo, |
imageDataProvider, |
imageDecode, |
imageShouldInterpolate, |
imageRenderingIntent ); |
// the image will retain the data provider & colorspace as needed, so we can release them now |
CGColorSpaceRelease( imageColorSpace ); |
} //if |
CGDataProviderRelease( imageDataProvider ); |
} // if |
return imageRef; |
} // CGImageGetFromCGImageBitmap |
//------------------------------------------------------------------------ |
//------------------------------------------------------------------------ |
static NSImage *NSImageGetFromCGImage( CGImageRef imageRef ) |
{ |
NSImage *image = nil; |
if ( imageRef != NULL ) |
{ |
NSRect imageRect = NSMakeRect(0.0, 0.0, 0.0, 0.0); |
// Get the image dimensions |
imageRect.size.height = CGImageGetHeight( imageRef ); |
imageRect.size.width = CGImageGetWidth( imageRef ); |
// Create a new image to receive the Quartz image data |
image = [[[NSImage alloc] initWithSize:imageRect.size] autorelease]; |
if ( image != nil ) |
{ |
[image lockFocus]; |
// Get the Quartz context and draw |
CGContextRef imageContextRef = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort]; |
if ( imageContextRef != NULL ) |
{ |
CGContextDrawImage( imageContextRef, *(CGRect*)&imageRect, imageRef ); |
} // if |
[image unlockFocus]; |
} // if |
} // if |
return image; |
} // NSImageGetFromCGImage |
//------------------------------------------------------------------------ |
static NSImage *NSImageGetFromCGImageAddAlphaAndVerticalReflect( CGImageRef imageRefSrc ) |
{ |
NSImage *imageDst = nil; |
if ( imageRefSrc != NULL ) |
{ |
CGImageBitmap imageBitmap; |
if ( CGImageBitmapMalloc( imageRefSrc, &imageBitmap ) ) |
{ |
if ( CGImageBitmapVerticalReflect( imageRefSrc, &imageBitmap ) ) |
{ |
CGImageRef imageRefDst = CGImageGetFromCGImageBitmap( &imageBitmap ); |
if ( imageRefDst != NULL ) |
{ |
imageDst = NSImageGetFromCGImage( imageRefDst ); |
CGImageRelease( imageRefDst ); |
} // if |
} // if image vertical reflect |
} // if bitmap data |
} // if image ref |
return imageDst; |
} // NSImageGetFromCGImageAddAlphaAndVerticalReflect |
//------------------------------------------------------------------------ |
//------------------------------------------------------------------------ |
@implementation OpenGLView |
//------------------------------------------------------------------------ |
- (GLdouble) getExhibitChangeInTime:(GLdouble *)theLastTime |
{ |
GLdouble deltaTime = 0.0; |
GLdouble now = (GLdouble)[NSDate timeIntervalSinceReferenceDate]; |
if ( *theLastTime < 0 ) |
{ |
deltaTime = 0; |
} // if |
else |
{ |
deltaTime = now - *theLastTime; |
} // else |
*theLastTime = now; |
return deltaTime; |
} // getExhibitChangeInTime |
//------------------------------------------------------------------------ |
- (void) renderExhibit |
{ |
glPushMatrix( ); |
glTranslatef(0.0f, 0.45f, 0.0f); |
glScalef(0.2f, 0.2f, 0.2f); |
if ( targetExhibit ) |
{ |
[targetExhibit renderFrame]; |
} // if |
glPopMatrix( ); |
} // renderExhibit |
//------------------------------------------------------------------------ |
- (void) setExhibitPitch |
{ |
if (pitch < -45.0f) |
{ |
pitch = -45.0f; |
} // if |
else if (pitch > 90.0f) |
{ |
pitch = 90.0f; |
} // else if |
glRotatef(pitch, 1.0f, 0.0f, 0.0f); |
} // setExhibitPitch |
//------------------------------------------------------------------------ |
- (void) setExhibitAngle |
{ |
if ( !leftMouseIsDown && !rightMouseIsDown ) |
{ |
GLdouble deltaTime = [self getExhibitChangeInTime:&lastFrameReferenceTime]; |
angle += kViewRotationDegreesPerSecond * deltaTime; |
if ( angle >= 360.0f ) |
{ |
angle -= 360.0f; |
} // if |
} // if |
glRotatef( angle, 0.0f, 1.0f, 0.0f ); |
} // setExhibitAngle |
//------------------------------------------------------------------------ |
-(void) rotateExhibit |
{ |
glPushMatrix( ); |
// Constant rotation of the subject |
[self setExhibitPitch]; |
glTranslatef(0.0f, -0.5f, -0.15f); |
[self setExhibitAngle]; |
// Draw the exhibit |
[self renderExhibit]; |
glPopMatrix( ); |
} // rotateExhibit |
//------------------------------------------------------------------------ |
- (void) setExhibitPrespectiveMatrix:(GLfloat)theWidth boundsHeight:(GLfloat)theHeight |
{ |
GLfloat aspect = theWidth / theHeight; |
GLdouble right = 0.15 * aspect * zoom; |
GLdouble left = -right; |
GLdouble top = 0.15 * zoom; |
GLdouble bottom = -top; |
GLdouble zNear = 1.0; |
GLdouble zFar = 8.0; |
glMatrixMode( GL_PROJECTION ); |
glLoadIdentity( ); |
glFrustum( left, right, bottom, top, zNear, zFar ); |
glMatrixMode( GL_MODELVIEW ); |
} // setExhibitPrespectiveMatrix |
//------------------------------------------------------------------------ |
- (void) setExhibitViewport |
{ |
NSRect theBounds = [self bounds]; |
GLfloat theWidth = NSWidth(theBounds); |
GLfloat theHeight = NSHeight(theBounds); |
[self setExhibitPrespectiveMatrix:theWidth boundsHeight:theHeight]; |
glViewport( 0, 0, theWidth, theHeight ); |
} // setExhibitViewport |
//------------------------------------------------------------------------ |
- (void) drawExhibit |
{ |
[[self openGLContext] makeCurrentContext]; |
[self setExhibitViewport]; |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
[self rotateExhibit]; |
} // drawExhibit |
//------------------------------------------------------------------------ |
- (void) updateExhibit |
{ |
[self drawExhibit]; |
[self setNeedsDisplay:YES]; |
} // updateExhibit |
//------------------------------------------------------------------------ |
- (void) setExhibit: (OpenGLExhibit *) newExhibit |
{ |
if ( newExhibit ) |
{ |
[targetExhibit release]; |
targetExhibit = [newExhibit retain]; |
} // if |
} // setExhibit |
//------------------------------------------------------------------------ |
-(void) setupOpenGL |
{ |
OpenGLSetup( GL_FALSE ); |
} // setupOpenGL |
//------------------------------------------------------------------------ |
- (void) newUpdateTimer |
{ |
GLint swapInterval = 1; |
timer = [NSTimer timerWithTimeInterval: kScheduledTimerInSeconds |
target: self |
selector: @selector(updateExhibit) |
userInfo: nil |
repeats: YES]; |
[timer retain]; |
[[NSRunLoop currentRunLoop] addTimer: timer forMode: NSDefaultRunLoopMode]; |
[[NSRunLoop currentRunLoop] addTimer: timer forMode: NSEventTrackingRunLoopMode]; |
lastFrameReferenceTime = -1; |
leftMouseIsDown = NO; |
rightMouseIsDown = NO; |
angle = 0; |
pitch = 25; |
zoom = 1; |
// Sync to VBL to avoid tearing. |
[[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; |
} // newUpdateTimer |
//------------------------------------------------------------------------ |
- (id) initWithFrame:(NSRect)theFrame pixelFormat:(NSOpenGLPixelFormat*)thePixelFormat |
{ |
// Create a GL Context to use - i.e. init the superclass |
if( thePixelFormat == nil ) |
{ |
NSOpenGLPixelFormatAttribute pixelAttributes [] |
= { |
NSOpenGLPFADoubleBuffer, |
NSOpenGLPFADepthSize, 24, |
NSOpenGLPFAStencilSize, 8, |
0 |
}; |
CheckForGLSLHardwareSupport( pixelAttributes ); |
thePixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes: pixelAttributes] autorelease]; |
} // if |
if ( ( self = [super initWithFrame:theFrame pixelFormat:thePixelFormat] ) ) |
{ |
// Basic GL initializations |
[[self openGLContext] makeCurrentContext]; |
[self setupOpenGL]; |
// Basic initializations |
[self setFrameSize:theFrame.size]; |
// Create an update timer |
[self newUpdateTimer]; |
// Did the frame change? |
[self setPostsFrameChangedNotifications:YES]; |
} // if |
return self; |
} // initWithFrame |
//------------------------------------------------------------------------ |
- (id)init |
{ |
if ( ( self = [super init] ) ) |
{ |
// superclass may return nil |
targetExhibit = nil; |
timer = nil; |
} // if |
return self; |
} // init |
//------------------------------------------------------------------------ |
- (id) initWithFrame:(NSRect)theFrame |
{ |
return [self initWithFrame:theFrame pixelFormat:nil]; |
} // initWithFrame |
- (void)update // moved or resized |
{ |
[super update]; |
[self drawExhibit]; |
} // update |
//------------------------------------------------------------------------ |
- (void)reshape // scrolled, moved or resized |
{ |
[super reshape]; |
[self drawExhibit]; |
} // reshape |
//------------------------------------------------------------------------ |
- (void) dealloc |
{ |
// Release the update timer |
if (timer) |
{ |
[timer invalidate]; |
[timer release]; |
} // if |
//Dealloc the superclass |
[super dealloc]; |
} // dealloc |
//------------------------------------------------------------------------ |
- (void) drawRect: (NSRect) theRect |
{ |
[[self openGLContext] flushBuffer]; |
} // drawRect |
//------------------------------------------------------------------------ |
- (void)mouseDown:(NSEvent *)event |
{ |
lastMousePoint = [self convertPoint:[event locationInWindow] fromView:nil]; |
leftMouseIsDown = YES; |
} // mouseDown |
//------------------------------------------------------------------------ |
- (void)rightMouseDown:(NSEvent *)event |
{ |
lastMousePoint = [self convertPoint:[event locationInWindow] fromView:nil]; |
rightMouseIsDown = YES; |
} // rightMouseDown |
//------------------------------------------------------------------------ |
- (void)mouseUp:(NSEvent *)event |
{ |
leftMouseIsDown = NO; |
} // mouseUp |
//------------------------------------------------------------------------ |
- (void)rightMouseUp:(NSEvent *)event |
{ |
rightMouseIsDown = NO; |
} // rightMouseUp |
//------------------------------------------------------------------------ |
- (void)mouseDragged:(NSEvent *)event |
{ |
if ( [event modifierFlags] & NSRightMouseDown ) |
{ |
[self rightMouseDragged:event]; |
} // if |
else |
{ |
NSPoint mouse = [self convertPoint:[event locationInWindow] fromView:nil]; |
pitch += lastMousePoint.y - mouse.y; |
angle -= lastMousePoint.x - mouse.x; |
lastMousePoint = mouse; |
[self setNeedsDisplay:YES]; |
} // else |
} // mouseDragged |
//------------------------------------------------------------------------ |
- (void)rightMouseDragged:(NSEvent *)event |
{ |
NSPoint mouse = [self convertPoint:[event locationInWindow] fromView:nil]; |
zoom += 0.01f * (lastMousePoint.y - mouse.y); |
if (zoom < 0.05f) |
{ |
zoom = 0.05f; |
} // if |
else if (zoom > 2.0f) |
{ |
zoom = 2.0f; |
} // else if |
lastMousePoint = mouse; |
[self setNeedsDisplay:YES]; |
} // rightMouseDragged |
//------------------------------------------------------------------------ |
- (void)pageLayoutDidEnd: (NSPageLayout *)pageLayout returnCode: (int)returnCode contextInfo: (id)printInfo |
{ |
if(returnCode == NSOKButton) |
{ |
[NSPrintInfo setSharedPrintInfo: printInfo]; |
} // if |
} // pageLayoutDidEnd |
//------------------------------------------------------------------------ |
- (void)runPageLayout: (id)sender |
{ |
NSPageLayout *pageLayout = [NSPageLayout pageLayout]; |
NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo]; |
[pageLayout beginSheetWithPrintInfo: printInfo |
modalForWindow: [self window] |
delegate: self |
didEndSelector: @selector(pageLayoutDidEnd:returnCode:contextInfo:) |
contextInfo: printInfo]; |
} // runPageLayout |
//------------------------------------------------------------------------ |
- (void)copyPixelsTo: (GLvoid *)imageData sourceRect:(NSRect)srcRect baseView:(NSView *)view |
{ |
NSRect rect = NSIntersectionRect([self bounds], srcRect); |
NSPoint origin = [self convertPoint:rect.origin toView:view]; |
GLvoid *pixels = imageData; |
[self lockFocus]; |
OpenGLNoError(); |
glPushAttrib(GL_ALL_ATTRIB_BITS); |
glReadBuffer(GL_BACK); |
OpenGLDisableCapabilitiesForCopy( ); |
OpenGLPixelMap( ); |
OpenGLPixelStore( &origin, &rect, &srcRect ); |
OpenGLPixelTransfer( ); |
OpenGLReadRGBAPixels( &rect, pixels ); |
glPopAttrib(); |
// Get rid of any error, in order to not mislead the rest of the app |
OpenGLNoError(); |
[self unlockFocus]; |
} // copyPixelsTo |
//------------------------------------------------------------------------ |
- (NSImage *)getImageFromRect:(NSRect)rect |
{ |
if( NSIsEmptyRect(rect) ) |
{ |
rect = [self bounds]; |
} // if |
NSImage *image = nil; |
GLuint imageWidth = NSWidth( rect ); |
GLuint imageHeight = NSHeight( rect ); |
GLuint imageSamplesPerPixel = 4; |
GLuint imageRowBytes = imageWidth * imageSamplesPerPixel; |
GLuint imageStorageSize = imageRowBytes * imageHeight; |
GLvoid *imageData = (GLvoid *)malloc( imageStorageSize ); |
if ( imageData != NULL ) |
{ |
// Create a data provider |
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithData( NULL, imageData, imageStorageSize, NULL ); |
if ( imageDataProvider != NULL ) |
{ |
// Create a color space for the image |
CGColorSpaceRef imageColorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ); |
if ( imageColorSpace != NULL ) |
{ |
// Create an empty CGImageRef so that we may utilize OpenGL read pixels to copy the |
// pixels from OpenGL view into this newly created CGImageRef |
GLuint imageBitsPerPixel = 32; |
GLuint imageBitsPerComponent = 8; |
GLfloat *imageDecode = NULL; |
bool imageShouldInterpolate = true; |
CGColorRenderingIntent imageRenderingIntent = kCGRenderingIntentDefault; |
CGBitmapInfo imageBitmapInfo = kCGImageAlphaNone; // For now RGB; but later we'll fix the alpha |
CGImageRef imageRef = CGImageCreate( imageWidth, |
imageHeight, |
imageBitsPerComponent, |
imageBitsPerPixel, |
imageRowBytes, |
imageColorSpace, |
imageBitmapInfo, |
imageDataProvider, |
imageDecode, |
imageShouldInterpolate, |
imageRenderingIntent ); |
if ( imageRef != NULL ) |
{ |
[self copyPixelsTo:imageData sourceRect:rect baseView:self]; |
// Get an NSImage from CGImageRef, add alpha (RGBA from RGB) and |
// vertically reflect the image |
image = NSImageGetFromCGImageAddAlphaAndVerticalReflect( imageRef ); |
CGImageRelease( imageRef ); |
} // if |
CGColorSpaceRelease( imageColorSpace ); |
} //if |
CGDataProviderRelease( imageDataProvider ); |
} // if |
} // if |
return image; |
} // getImageFromRect |
//------------------------------------------------------------------------ |
- (void)print:(id)sender |
{ |
NSImage *tiffImage = [self getImageFromRect:[self bounds]]; |
NSImageView *imageView = [[[NSImageView alloc] initWithFrame:[self bounds]] autorelease]; |
[imageView setImage: tiffImage]; |
if( imageView && tiffImage ) |
{ |
// setup some reasonable state for GL printing. To get better output results, |
// fine tune this section of the code for better scaling. |
NSPrintInfo *printinfo = [NSPrintInfo sharedPrintInfo]; |
[printinfo setHorizontalPagination: NSAutoPagination]; |
[printinfo setVerticalPagination: NSAutoPagination]; |
[printinfo setTopMargin: 0.0f]; |
[printinfo setBottomMargin: 0.0f]; |
[printinfo setRightMargin: 7.0f]; |
[printinfo setLeftMargin: 7.0f]; |
if ([imageView bounds].size.height < [imageView bounds].size.width) |
{ |
[printinfo setOrientation: NSLandscapeOrientation]; |
} // if |
else |
{ |
[printinfo setOrientation: NSPortraitOrientation]; |
} // else |
// print imageView |
NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView: imageView printInfo: printinfo]; |
[printOperation runOperationModalForWindow: [self window] |
delegate: nil |
didRunSelector: nil |
contextInfo: nil]; |
} // if |
else |
{ |
NSRunCriticalAlertPanel(@"Print Error",@"Could not generate image data for printing.", @"OK", nil, nil); |
} // else |
} // sender |
//------------------------------------------------------------------------ |
@end |
//------------------------------------------------------------------------ |
//------------------------------------------------------------------------ |
Copyright © 2007 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2007-11-05