I also use Mac OSX as my primary development platform along side of iOS. You said that you'd rather not develop for MacOSX? I personally think it's a good idea to develop for every and any platform possible to broaden your audience.
For languages, you can use C for almost any platform, but there are a few indie platforms that limit you to use C# (which I personally hate). Most people will recommend using C++ as your primary programming language. I personally prefer C over C++, but don't avoid learning it because it can be very useful.
I see that SDL has already been recommended, but if you plan on creating something of great complexity, I recommend you learn OpenGL 3+ as well as OpenAL. They are also cross platform and compatible with SDL. OpenGL is the cross platform 3D graphics API, while OpenAL is a cross platform 3D sound API.
OpenGL 3.2 tutorials: http://nopper.tv/opengl.html
OpenAL video tutorial:
OpenAL w/ SDL: http://pastebin.com/rYp59nYg
A few notes I also wanted to share:
1. For MacOSX, the latest version of OpenGL supported is 3.2. It sounds unfortunate, but IMO, I don't see how much better 4.0 is for most applications.
2. If you plan to use OpenGL 3+ with SDL, either make sure you are using the beta version of SDL 2.0 (the final release hasn't been made yet), or you can roll your own code to initialize and use an OpenGL 3.2 context. Finding code on how to do that is extremely hard to find, but after a VERY long time searching google, I finally found some example code.
main.m
// Based on the Cocoa variant of the "minimal animation" demo
// Working, reasonably simple 3.2 demo!!!
// Preliminary demo by Ingemar Ragnemalm 2012
// for the 3.2 update of my course book.
// Partially based on other GL3.2 demos, especially that demo with two triangles.
// (Should be referenced I guess but I can't do that right now.)
// Later versions appears in my book.
#import <Cocoa/Cocoa.h>
#include <OpenGL/gl3.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
// Globals
// Data would normally be read from files
GLfloat vertices[] = { -1.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f };
GLfloat colours[] = { 1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f };
GLfloat vertices2[] = { 0.0f,0.0f,0.0f,
0.0f,-1.0f,0.0f,
1.0f,0.0f,0.0f };
// two vertex array objects, one for each object drawn
unsigned int vertexArrayObjID[2];
// three vertex buffer objects in this example
unsigned int vertexBufferObjID[3];
void checkError(const char *functionName);
// Note: free data afterwards.
char* loadFile(char *filename, GLint *fSize)
{
char * data;
FILE *theFile;
char c;
long howMuch;
// Get file length
theFile = fopen(filename, "rb");
if (theFile == NULL)
{
printf("%s not found\n", filename);
return NULL;
}
howMuch = 0;
c = 0;
while (c != EOF)
{
c = getc(theFile);
howMuch++;
}
fclose(theFile);
printf("%ld bytes\n", howMuch);
// Read it again
data = (char *)malloc(howMuch);
theFile = fopen(filename, "rb");
fread(data, howMuch-1,1,theFile);
fclose(theFile);
data[howMuch-1] = 0;
printf("\n--- Shader source %s ---\n%s\n-- end of %s --\n", filename, data, filename);
printf("%s loaded from disk\n", filename);
*fSize = howMuch;
return data;
}
void dumpInfo(void)
{
printf ("Vendor: %s\n", glGetString (GL_VENDOR));
printf ("Renderer: %s\n", glGetString (GL_RENDERER));
printf ("Version: %s\n", glGetString (GL_VERSION));
printf ("GLSL: %s\n", glGetString (GL_SHADING_LANGUAGE_VERSION));
checkError ("dumpInfo");
}
/* report GL errors, if any, to stderr */
void checkError(const char *functionName)
{
GLenum error;
while (( error = glGetError() ) != GL_NO_ERROR)
{
fprintf (stderr, "GL error 0x%X detected in %s\n", error, functionName);
}
}
// printShaderInfoLog
// From OpenGL Shading Language 3rd Edition, p215-216
// Display (hopefully) useful error messages if shader fails to compile
void printShaderInfoLog(GLint shader)
{
GLint infoLogLen = 0;
GLsizei charsWritten = 0;
GLchar *infoLog;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen);
// should additionally check for OpenGL errors here
if (infoLogLen > 0)
{
infoLog = (GLchar *)malloc(infoLogLen); //new GLchar[infoLogLen];
// error check for fail to allocate memory omitted
glGetShaderInfoLog(shader,infoLogLen, &charsWritten, infoLog);
printf("Infolog: %s\n", infoLog);
free(infoLog);
}
else
printf("No infolog\n");
// should additionally check for OpenGL errors here
}
void init(void)
{
// GL inits
glClearColor(0.2,0.2,0.5,0);
glEnable(GL_DEPTH_TEST);
checkError("GL inits");
// Would load objects from file here - but using globals in this example
// Allocate Vertex Array Objects
glGenVertexArrays(2, &vertexArrayObjID[0]);
// Setup first Vertex Array Object
glBindVertexArray(vertexArrayObjID[0]);
glGenBuffers(2, vertexBufferObjID);
// VBO for vertex data
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID[0]);
glBufferData(GL_ARRAY_BUFFER, 9*sizeof(GLfloat), vertices, GL_STATIC_DRAW);
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
// VBO for colour data
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID[1]);
glBufferData(GL_ARRAY_BUFFER, 9*sizeof(GLfloat), colours, GL_STATIC_DRAW);
glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
// Setup second Vertex Array Object
glBindVertexArray(vertexArrayObjID[1]);
glGenBuffers(1, &vertexBufferObjID[2]);
// VBO for vertex data
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID[2]);
glBufferData(GL_ARRAY_BUFFER, 9*sizeof(GLfloat), vertices2, GL_STATIC_DRAW);
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
checkError("init");
}
void initShaders(void)
{
GLuint p, f, v;
char *vs,*fs;
v = glCreateShader(GL_VERTEX_SHADER);
f = glCreateShader(GL_FRAGMENT_SHADER);
// load shaders & get length of each
GLint vlen;
GLint flen;
vs = loadFile("OpenGL3Test.app/Contents/Resources/minimal.vert", &vlen);
fs = loadFile("OpenGL3Test.app/Contents/Resources/minimal.frag", &flen);
const char * vv = vs;
const char * ff = fs;
glShaderSource(v, 1, &vv, NULL); // &vlen);
glShaderSource(f, 1, &ff, NULL); // &flen);
GLint compiled;
glCompileShader(v);
glGetShaderiv(v, GL_COMPILE_STATUS, &compiled);
if (!compiled)
{
printf("Vertex shader not compiled.\n");
printShaderInfoLog(v);
}
glCompileShader(f);
glGetShaderiv(f, GL_COMPILE_STATUS, &compiled);
if (!compiled)
{
printf("Fragment shader not compiled.\n");
printShaderInfoLog(f);
printf("-----\n");
}
p = glCreateProgram();
glBindAttribLocation(p,0, "in_Position");
glBindAttribLocation(p,1, "in_Color");
glAttachShader(p,v);
glAttachShader(p,f);
glLinkProgram(p);
glUseProgram(p);
free(vs); // dont forget to free allocated memory
free(fs); // we allocated this in the loadFile function...
checkError("init shader");
}
void display(void)
{
checkError("pre display");
// clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(vertexArrayObjID[0]); // First VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // draw first object
glBindVertexArray(vertexArrayObjID[1]); // select second VAO
glVertexAttrib3f((GLuint)1, 1.0, 0.0, 0.0); // set constant color attribute
glDrawArrays(GL_TRIANGLES, 0, 3); // draw second object
glBindVertexArray(0);
checkError("display");
}
void reshape(int w, int h)
{
glViewport(0,0,(GLsizei)w,(GLsizei)h);
}
// -----------
// Globals (was in GLViewDataPtr)
NSOpenGLContext *m_context;
float lastWidth, lastHeight;
NSView *theView;
void MakeContext(NSView *view)
{
// NSWindow *w;
NSOpenGLPixelFormat *fmt;
NSOpenGLPixelFormatAttribute attrs[] =
{
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFADepthSize, 32,
0
};
// Create custom data pointer
theView = view;
// Init GL context
fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: attrs];
m_context = [[NSOpenGLContext alloc] initWithFormat: fmt shareContext: nil];
[fmt release];
[m_context makeCurrentContext];
checkError("makeCurrentContext");
}
// -------------------- View ------------------------
@interface TestView : NSView { }
-(void)drawRect:(NSRect)rect;
@end
float loop;
#define Pi 3.1415
@implementation TestView
-(void)drawRect:(NSRect)rect
{
if (([theView frame].size.width != lastWidth) || ([theView frame].size.height != lastHeight))
{
lastWidth = [theView frame].size.width;
lastHeight = [theView frame].size.height;
// Only needed on resize:
[m_context clearDrawable];
reshape([theView frame].size.width, [theView frame].size.height);
}
[m_context setView: theView];
[m_context makeCurrentContext];
// Draw
display();
[m_context flushBuffer];
[NSOpenGLContext clearCurrentContext];
loop = loop + 0.1;
}
-(void)windowWillClose:(NSNotification *)note
{
[[NSApplication sharedApplication] terminate:self];
}
@end
// -------------------- Timer ------------------------
// Mini-mini class for the timer
@interface TimerController : NSObject { }
-(void)timerFireMethod:(NSTimer *)t;
@end
NSTimer *gTimer;
TimerController *myTimerController;
NSView *view;
// Timer!
@implementation TimerController
-(void)timerFireMethod:(NSTimer *)t;
{
[view setNeedsDisplay: YES];
}
@end
// home()
#include <Carbon/Carbon.h>
#include <stdio.h>
void home()
{
/*CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
char path[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
{
// error!
return;
}
CFRelease(resourcesURL);
chdir(path);
printf("Current Path: %s\n", path);*/
}
// ------------------ Main program ---------------------
NSApplication *myApp;
NSView *view;
NSAutoreleasePool *pool;
NSWindow *window;
int main(int argc, const char *argv[])
{
pool = [NSAutoreleasePool new];
myApp = [NSApplication sharedApplication];
home();
NSRect frame = NSMakeRect(10., 1000., 640., 480.);
window = [NSWindow alloc];
[window initWithContentRect:frame
styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask
backing:NSBackingStoreBuffered
defer:false];
[window setTitle:@"Minimal OpenGL animation (Cocoa)"];
view = [TestView alloc];
[view initWithFrame: frame];
// OpenGL init!
MakeContext(view);
init ();
initShaders();
dumpInfo ();
[window setContentView: view];
[window setDelegate: view];
[window makeKeyAndOrderFront: nil];
// Timer
myTimerController = [TimerController alloc];
gTimer = [NSTimer
scheduledTimerWithTimeInterval: 0.02
target: myTimerController
selector: @selector(timerFireMethod:)
userInfo: nil
repeats: YES];
// Main loop
[myApp run];
[pool release]; // Free;
return( EXIT_SUCCESS );
}
minimal.vert:
#version 150
in vec3 in_Position;
in vec3 in_Color;
out vec3 ex_Color;
void main(void)
{
ex_Color = in_Color;
gl_Position = vec4(in_Position, 1.0);
}
minimal.frag:
#version 150
in vec3 ex_Color;
out vec4 out_Color;
void main(void)
{
out_Color = vec4(ex_Color,1.0);
}
That should be enough to get you going on the most basic of basics with OpenGL 3.2
3. While OpenAL is an awesome audio API, it's support for Mac OSX is rather poor. A good example would be the fact that the native framework doesn't work in 64-bit mode. An alternative to this is to use OpenALSoft.framework which is a wrapper that uses CoreAudio to handle audio. Works very well and I have no complaints using it!
4. When reading and referencing OpenAL tutorials, please try to avoid using ALUT because it is very depreciated. The SDL example using OpenAL is strongly preferred since it loads sound effects without the need to use ALUT.
I really hope that this helps you in one way or another. Be sure to take your time and learn at a pace that works best for you! Feel free to ask me anything when it comes to developing games on OSX or iOS. OpenGL on OSX is becoming my new specialty.
Shogun.