OpenGL behaving strangely - objective-c

OpenGL is acting very strangely for some reason. In my subclass of NSOpenGLView, I have the following code in the -prepareOpenGL method:
- (void)prepareOpenGL {
GLfloat lightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
GLfloat lightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat lightPosition[] = { 0.0f, 0.0f, 2.0f };
quality = 0;
zCoord = -6;
[self loadTextures];
glEnable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glShadeModel(GL_SMOOTH);
glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glLightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient);
glLightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse);
glLightfv(GL_LIGHT1, GL_POSITION, lightPosition);
glEnable(GL_LIGHT1);
gameState = kGameStateRunning;
int i = 0; // HERE ********
[NSTimer scheduledTimerWithTimeInterval:0.03f target:self
selector:#selector(processKeys) userInfo:nil repeats:YES];
// Synchronize buffer swaps with vertical refresh rate
GLint swapInt = 1;
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
// Setup and start displayLink
[self setupDisplayLink];
}
I wanted to assign the timer that processes key input to an ivar so that I could invalidate it when the game is paused (and reinstantiate it on resume), however when I did that (as apposed to leaving it at [NSTimer scheduledTimer…), OpenGL doesn't display the cube I draw. When I take it away, it's fine. So i tried just adding a harmless statement, int i = 0; (maked // HERE *******), and that makes the lighting not work! When I take it away, everything is fine, but when I put it back, everything is dark. Can someone come up with a rational explanation for this? Just creating any variable makes it do this, even though there are already several declared.
Thanks.

Very strange, but I found the problem. In this line:
GLfloat lightPosition[] = { 0.0f, 0.0f, 2.0f };
I have only 3 elements instead of 4. I have no idea why it worked when I didn't have int i = 0;, but when I add 0.0f to the array everything works normally.

Related

CATiledLayers on OS X

This has been driving me crazy.. I have a large image, and need to have a view that is both zoomable, and scrollable (ideally it should also be able to rotate, but I've given up on that part). Since the image is very large, I plan on using CATiledLayer, but I simply can't get it to work.
My requirements are:
I need to be able to zoom (on mouse center) and pan
The image should not change its width:height ratio (shouldn't resize, only zoom).
This should run on Mac OS 10.9 (NOT iOS!)
Memory use shouldn't be huge (although up to like 100 MB should be ok).
I have the necessary image both complete in one file, and also tiled into many (even have it for different zoom levels). I prefer using the tiles, as that should be easier on memory, but both options are available.
Most of the examples online refer to iOS, and thus use UIScrollView for the zoom/pan, but I can't get to copy that behaviour for NSScrollView. The only example for Mac OS X I found is this, but his zoom always goes to the lower left corner, not the middle, and when I adapt the code to use png files instead of pdf, the memory use gets around 400 MB...
This is my best try so far:
#implementation MyView{
CATiledLayer *tiledLayer;
}
-(void)awakeFromNib{
NSLog(#"Es geht los");
tiledLayer = [CATiledLayer layer];
// set up this view & its layer
self.wantsLayer = YES;
self.layer = [CALayer layer];
self.layer.masksToBounds = YES;
self.layer.backgroundColor = CGColorGetConstantColor(kCGColorWhite);
// set up the tiled layer
tiledLayer.delegate = self;
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 5;
tiledLayer.anchorPoint = CGPointZero;
tiledLayer.bounds = CGRectMake(0.0f, 0.0f, 41*256, 22*256);
tiledLayer.autoresizingMask = kCALayerNotSizable;
tiledLayer.tileSize = CGSizeMake(256, 256);
self.frame = CGRectMake(0.0f, 0.0f, 41*256, 22*256);
self.layer = tiledLayer;
//[self.layer addSublayer:tiledLayer];
[tiledLayer setNeedsDisplay];
}
-(void)drawRect:(NSRect)dirtyRect{
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGFloat scale = CGContextGetCTM(context).a;
CGSize tileSize = tiledLayer.tileSize;
tileSize.width /= scale;
tileSize.height /= scale;
// calculate the rows and columns of tiles that intersect the rect we have been asked to draw
int firstCol = floorf(CGRectGetMinX(dirtyRect) / tileSize.width);
int lastCol = floorf((CGRectGetMaxX(dirtyRect)-1) / tileSize.width);
int firstRow = floorf(CGRectGetMinY(dirtyRect) / tileSize.height);
int lastRow = floorf((CGRectGetMaxY(dirtyRect)-1) / tileSize.height);
for (int row = firstRow; row <= lastRow; row++) {
for (int col = firstCol; col <= lastCol; col++) {
NSImage *tile = [self tileForScale:scale row:row col:col];
CGRect tileRect = CGRectMake(tileSize.width * col, tileSize.height * row,
tileSize.width, tileSize.height);
// if the tile would stick outside of our bounds, we need to truncate it so as
// to avoid stretching out the partial tiles at the right and bottom edges
tileRect = CGRectIntersection(self.bounds, tileRect);
[tile drawInRect:tileRect];
}
}
}
-(BOOL)isFlipped{
return YES;
}
But this deforms the image, and doesn't zoom or pan correctly (but at least the tile selection works)...
I can't believe this is so hard, any help would be greatly appreciated. Thanks :)
After a lot of research and tries, I finally managed to get this to work using this example. Decided to post it for future reference. Open the ZIP > CoreAnimationLayers> TiledLayers, there's a good example there. That's how CATiledLayer works with OS X, and since the example there doesn't handle zoom very well, I leave here my zoom code
-(void)magnifyWithEvent:(NSEvent *)event{
[super magnifyWithEvent:event];
if (!isZooming) {
isZooming = YES;
BOOL zoomOut = (event.magnification > 0) ? NO : YES;
if (zoomOut) {
[self zoomOutFromPoint:event.locationInWindow];
} else {
[self zoomInFromPoint:event.locationInWindow];;
}
}
}
-(void)zoomInFromPoint:(CGPoint)mouseLocationInWindow{
if(zoomLevel < pow(2, tiledLayer.levelsOfDetailBias)) {
zoomLevel *= 2.0f;
tiledLayer.transform = CATransform3DMakeScale(zoomLevel, zoomLevel, 1.0f);
tiledLayer.position = CGPointMake((tiledLayer.position.x*2) - mouseLocationInWindow.x, (tiledLayer.position.y*2) - mouseLocationInWindow.y);
}
}
-(void)zoomOutFromPoint:(CGPoint)mouseLocationInWindow{
NSInteger power = tiledLayer.levelsOfDetail - tiledLayer.levelsOfDetailBias;
if(zoomLevel > pow(2, -power)) {
zoomLevel *= 0.5f;
tiledLayer.transform = CATransform3DMakeScale(zoomLevel, zoomLevel, 1.0f);
tiledLayer.position = CGPointMake((tiledLayer.position.x + mouseLocationInWindow.x)/2, (tiledLayer.position.y + mouseLocationInWindow.y)/2);
}
}

How to use glClearBuffer* in Cocoa OpenGLView to clear color buffer

I'm trying to understand how to use glClearBuffer* to change the background color in a (either single or double buffered) NSOpenGLView in Cocoa for OS X.
The following code fragment as suggested by the OpenGL Superbible fails with GL_INVALID_OPERATION:
GLfloat red[] = {1.0f, 0.0f, 0.0f, 1.0f};
glClearBufferfv(GL_COLOR, 0, red);
What do I need to supply for the second parameter?
I'm using a double buffered View extending OpenGLView.
#import "MyOpenGLView.h"
#include <OpenGL/gl3.h>
#implementation MyOpenGLView
-(void) drawRect: (NSRect) bounds
{
GLfloat red[] = {1.0f, 0.0f, 0.0f, 1.0f};
glClearBufferfv(GL_COLOR, 0, red);
GLenum e = glGetError();
// e == GL_INVALID_OPERATION after this call
// and the view is drawn in black
// The following two lines work as intended:
//glClearColor(1.0, 0.f, 0.f, 1.f);
//glClear(GL_COLOR_BUFFER_BIT);
[[self openGLContext] flushBuffer];
}
#end
Really? It is giving you GL_INVALID_OPEARATION?
This function is not supposed to generate that error... are you sure something earlier in your program did not create the error and you are mistaking the source?
The bigger problem however, is that using GL_COLOR as the buffer in this API call expects the second parameter to be an index into your set of draw buffers. It is unclear how your draw buffers are setup in this code, it is possible that you have GL_NONE. As there is no defined error behavior if you try to clear a draw buffer when GL_NONE is used, I suppose an implementation might choose to raise GL_INVALID_OPERATION.
In order for your current usage of glClearBufferfv (...) to make sense, I would expect to see something like this:
GLenum buffers [] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
GLfloat red [] = { 1.0f, 0.0f, 0.0f, 1.0f };
glDrawBuffers (2, buffers);
glClearBufferfv (GL_COLOR, 0, red);
Now this call will clear GL_COLOR_ATTACHMENT0, if you wanted to clear GL_COLOR_ATTACHMENT1, you could replace 0 with 1.

Xcode with OpenGL: new windows do not automatically become active context

I have a strange problem when I create a Documents Based Xcode project with a NSOpenGLView. The application works fine with a single document, responding to mouse clicks. When you choose File/New to open up a second document a new front window appears on top, but mouse clicks are sent to the previous (background) object. You can move the new window and clicks are still sent to the previous object. However, if you resize the new window or click back and forth with the previous window all works well. So the problem seems to be that the new (front) window does not become the active context. Can you suggest a solution? I have placed the Xcode 4.4 code (44kb) at http://www.mccauslandcenter.sc.edu/CRNL/sw/GLdoc.zip.
Create a new Document based application
Add the MyOpenGLView.m and MyOpenGLView.h files to project
For Targets/BuildPhases, add OpenGL.framework to "Link Binary With Libraries"
For Targets/BuildPhases, add MyOpenGLView.m to "Compile Sources"
In interface builder
a.) select Document's Window and in the Attributes inspector turn off "One shot"
b.) add a OpenGL View from the Object Library
c.) with the Identity inspector, set the Class of newly created NSOpenGLView to MyOpenGLView
Run the project
----START MyOpenGLView.h
#import <Cocoa/Cocoa.h>
#import <GLUT/GLUT.h>
#interface MyOpenGLView : NSOpenGLView
{
NSTimer* timer; //animation timer
NSPoint mouseloc;
NSPoint screenSize;
int hourglassSize;
BOOL updateGL;
}
#end
----START MyOpenGLView.m
// document based OpenGL application inspired by http://www.alecjacobson.com/weblog/?p=2110
#import "MyOpenGLView.h"
#implementation MyOpenGLView
-(void)prepareOpenGL
{
//NSLog(#"preparing");
}
- (void)reshape
{
//NSLog(#"reshaping");
NSRect rectView = [self bounds];
screenSize.x = rectView.size.width;
screenSize.y = rectView.size.height;
}
void enter2D (int width, int height) //Enter2D = reshapeGL
{
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, 0, height,-1,1); //map view to match pixel size, e.g. gluOrtho2D(0, width, 0, height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable (GL_BLEND); //allow transparent objects
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void drawTriangle (int sz, int left, int bottom)
{
glLoadIdentity ();
glTranslatef(left,bottom,0.0f);
glBegin(GL_TRIANGLES);
glVertex3f( sz/2, sz, 0.0f);// Top
glVertex3f(0.0,0.0, 0.0); // Bottom Left
glVertex3f( sz,0.0f, 0.0f); // Bottom Right
glEnd(); // Finished Drawing The Triangle
}
void drawHourGlass (int sz, NSPoint center)
{
glLoadIdentity ();
glTranslatef(center.x,center.y,0.0f);
glBegin(GL_TRIANGLES); //lower triangle
glVertex3f( 0.0, 0.0, 0.0f); // Top
glVertex3f(-sz,-sz, 0.0);// Bottom Left
glVertex3f( sz,-sz, 0.0f);// Bottom Right
glEnd();
glBegin(GL_TRIANGLES); //upper triangle
glVertex3f( 0.0, 0.0, 0.0f); // bottom
glVertex3f(-sz,sz, 0.0);// Top Left
glVertex3f( sz,sz, 0.0f);// Top Right
glEnd();
}
-(void)drawRect:(NSRect)rect
{
updateGL = FALSE;
//NSLog(#" %f %f",rect.size.width,rect.size.height);
enter2D(screenSize.x,screenSize.y);
glClearColor(0.8,0.9,1,1); //background - blue sky
glClear(GL_COLOR_BUFFER_BIT);
glColor4f(0.6,0.3,0.1,1.0); //mountains - brown opaque
drawTriangle(100,10,0);
drawTriangle(70,80,0);
glColor4f(0.1,0.1,0.1,0.7); //crosshair - gray, slightly translucent
drawHourGlass (hourglassSize, mouseloc);
glFlush(); // Flush all OpenGL calls
}
- (void)mouseDown:(NSEvent *)event
{
mouseloc = [self convertPoint:[event locationInWindow] fromView:nil];
updateGL = TRUE;
}
int constrain (int size)
{
if (size < 5)
return 5;
else if (size > 50)
return 50;
else
return size;
}
- (void) magnifyWithEvent:(NSEvent *)event;
{
if ([event magnification] > 0)
hourglassSize = hourglassSize + 5;
else
hourglassSize = hourglassSize - 5;
hourglassSize = constrain(hourglassSize);
updateGL = TRUE;
}
- (void)scrollWheel:(NSEvent *)event
{
if (event.deltaY < 0)
hourglassSize = hourglassSize + 5;
if (event.deltaY > 0)
hourglassSize = hourglassSize - 5;
hourglassSize = constrain(hourglassSize);
updateGL = TRUE;
}
- (void)animationTimer:(NSTimer *)timer
{
if (updateGL == TRUE)
[self drawRect:[self bounds]];
}
-(BOOL)acceptsFirstResponder { return YES; }
-(BOOL)becomeFirstResponder { return YES; }
-(BOOL)resignFirstResponder { return YES; }
- (void) awakeFromNib
{
// [[self window] setAcceptsMouseMovedEvents:YES];
mouseloc.x = 30;
mouseloc.y = 40;
hourglassSize = 30;
timer = [NSTimer timerWithTimeInterval:(1.0f/60.0f) target:self selector:#selector(animationTimer:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// ensure timer fires during resize
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
updateGL = TRUE;
}
#end
For others who encounter this problem (which is present in some of the older demos Apple distributes), with modern versions of OSX (10.4 and later) you use CVDisplayLink instead of a timer to update OpenGL views.
http://developer.apple.com/library/mac/#qa/qa1385/_index.html
Alec Jacobson outlines this solution here
http://www.alecjacobson.com/weblog/?p=2185

OpenGL depth test problem

I've got a problem with OpenGL on mac, and I think the problem is the Depth test.
So, to my problem: Rather than explaining, I made two screenshots:
My scene from far: http://c0848462.cdn.cloudfiles.rackspacecloud.com/dd2267e27ad7d0206526b208cf2ea6910bcd00b4fa.jpg
And from near: http://c0848462.cdn.cloudfiles.rackspacecloud.com/dd2267e27a561b5f02344dca57508dddce21d2315f.jpg
If I do not draw the green floor, everything looks (kinda) fine. But still, like this it looks just aweful.
Here are the three codeblocks I use to set up Opengl:
+ (NSOpenGLPixelFormat*) defaultPixelFormat
{
NSOpenGLPixelFormatAttribute attributes [] = {
NSOpenGLPFAWindow,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)16,
(NSOpenGLPixelFormatAttribute)nil
};
return [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease];
}
- (void) prepareOpenGL
{
NSLog(#"Preparing OpenGL");
glClearColor( 0.0f, 0.0f, 1.0f, 1.0f );
glEnable(GL_TEXTURE_2D);
glClearDepth(1);
glEnable(GL_DEPTH_TEST);
glEnable (GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}
- (void)reshape
{
NSLog(#"Reshaping view");
glViewport( 0, 0, (GLsizei)[self bounds].size.width, (GLsizei)[self bounds].size.height);
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 45.0, [self bounds].size.width / [self bounds].size.height, 0.1f /*Nearest render distance*/, 5000.0 /*Render distance*/);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
gluPerspective( 45.0, [self bounds].size.width / [self bounds].size.height, 0.1f /*Nearest render distance*/, 5000.0 /*Render distance*/);
That's way too small of a near clip plane. The closer your near clip value is to 0, the less precision you get on values that are farther away. Push your near clip back to at least 1.0, if not farther. In general, you should push it back as far away as you can live with.
Oh, and you should be using a 24-bit depth buffer, not 16-bit.

My NSOpenGLView won't work. I've tried everything

I want to display something in an NSOpenGLView, but since there is a total of zero bytes of documentation on it, and the sample code is as big and complex as the documentation should be, I can't get any wise out of it. This is my code so far, and my ANOpenGLView is an NSOpenGLView in a NIB subclassed as ANOpenGLView:
#implementation ANOpenGLView
#synthesize animationTimer;
// MEM
- (void)dealloc {
[animationTimer release];
[super dealloc];
}
// INIT
- (id)initWithFrame:(NSRect)frameRect {
if (self = [super initWithFrame:frameRect]) {
NSOpenGLPixelFormatAttribute pixelFormatAttributes[] = {
NSOpenGLPFADoubleBuffer,
NSOpenGLPFADepthSize, 32,
0
};
NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttributes];
[self setOpenGLContext:[[[NSOpenGLContext alloc] initWithFormat:format shareContext:nil] autorelease]];
}
return self;
}
- (void)awakeFromNib {
/* 60 FPS */
animationTimer = [[NSTimer timerWithTimeInterval:(1.0f/60.0f) target:self selector:#selector(redraw:) userInfo:nil repeats:YES] retain];
[[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSDefaultRunLoopMode];
}
// DRAW
- (void)redraw:(NSTimer*)theTimer {
[self drawRect:[self bounds]];
}
- (void)drawRect:(NSRect)dirtyRect {
NSLog(#"Redraw");
[[self openGLContext] clearDrawable];
[[self openGLContext] setView:self];
[[self openGLContext] makeCurrentContext];
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glDisable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glViewport(0, 0, [self frame].size.width, [self frame].size.height);
glMatrixMode(GL_PROJECTION); glLoadIdentity();
glMatrixMode(GL_MODELVIEW); glLoadIdentity();
glTranslatef(-1.5f, 0.0f, -6.0f);
glBegin( GL_TRIANGLES );
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(0.0f, 1.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2f(-1.0f, -1.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2f(1.0f, -1.0f);
glEnd();
[[self openGLContext] flushBuffer];
[NSOpenGLContext clearCurrentContext];
}
#end
How can I get the triangle to show up? The only thing I get is a blank, white screen.
P.S. I want to draw in 2D.
EDIT I have updated my code, and this is what I have now:
I'm not sure that it's the only problem, but:
You haven't defined the pixel format
You haven't set up the matrices in the code
You haven't set up the viewport
Here http://www.cocoadev.com/index.pl?NSOpenGLView is a short example, it's almost what you need, but there is perspective matrix set up while you need orthogonal to render in 2D space (function glOrtho). World & View can be identity in this case.
Since 2D is your target, you won't deal with this matrices too much, just set them once.
For one thing, you've requested a double buffered context but you never swap them, so you're probably always drawing into the unseen back buffer. Add a call to glSwapAPPLE() after you flush.
I'm also a bit unsure about all your direct [self openGLContext] calls. Normally NSOpenGLView ought to handle this stuff for you, but in this case you're bypassing the set up of the view by calling drawRect directly from your timer, so things might be a bit messed up.
Have you tried just calling [self setNeedsDisplay:YES] from your timer method and letting the normal view setup occur? I would expect the redrawing still to be fast enough that way and it would make life easier...
Edit: #Peter is right, Still a great resource, just not applicable to this question.
Jeff Lamarche's openGL posts are a great introduction and they include a project template that handles most of the setup for you.
http://iphonedevelopment.blogspot.com/2009/05/opengl-es-from-ground-up-table-of.html