Digital Media • 40:49
This session covers the development of OpenGL applications for Mac OS, including the Mac OS X graphics architecture, OpenGL interfaces, texture management, and surface handling. Coverage includes insights into building OpenGL projects on Mac OS X.
Speaker: Dave Springer
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
I'd like to introduce Dave Springer, a senior OpenGL graphics engineer at Apple who's done a tremendous amount of work with OS X, with NSGL, and he's going to talk specifically about OpenGL on Mac OS X and the nuances of working there and talk about some of the new architectures and give you a lot of good code to work through. He's actually going to work through some code with you to show exactly how you can do it with OS X. So, Dave Springer.
I'm Dave. We'll talk about OpenGL on OS X. Just had a big long talk about it on OS 9. There's three main pillars of OpenGL and OS X. When we wrote it, we kept all these three things in mind all the time. First thing is that we want it to be fully integrated with OS X. We'll talk about what that means. Not, you know, a bag on the side.
It's like, you get OS X and then somehow you get to do 3D. This is fully integrated. We want to preserve the full OpenGL programming model so that if you're already an OpenGL programmer and you know how to do it, you still know how to do it. Nothing changed.
Plus, it's got to be fast. So we made a lot of architectural decisions inside of OpenGL on OS X. We'll talk about what some of those are so that you get a lot of speed. And you know that OS X is very different from OS 9. It's multitasking, it's multithreaded, it's multiprocessor, it's multi-a lot of things. And that means that the operating system is always in your way, slowing you down.
So we had to think about that a lot. is the founder of OpenGL, and he's been working on the development of OpenGL applications for Mac OS X for a long time. OpenGL is a very important part of the OS. It's a very important part of the OS. It's a very important part of the OS.
Okay. OS X gives us a lot of stuff. We have multi-headed graphics, multi-threaded, multi-processor. These are all things that come with the OS X operating system kernel. And that means that OS X has to do a lot of work. It takes up a lot of cycles in the CPU to do all this stuff, so that it really works properly.
We had to integrate in with all of that so that you can preserve a lot of your performance that you used to get on OS 9, which was none of those things. OS 9, you could own the whole machine if you wanted to, and just kind of elbow everything out of the way, run your game, and you're on the iron.
Well, that's not really true in OS X. We've made it Just as close to that as possible, and still preserve full OS X. So you're still fully multi-threaded, you're still fully multi-tasking, all those little neato little Unix demons are still running in the background, but you get your performance anyways. We've worked inside the OS X hardware acceleration layers inside the drivers so that we are really on the iron. We have the ATI RAGE 128 fully supported. Other stuff is coming soon. It's really awesome, and I can't tell you what it is.
We support Cocoa APIs, which Jeff showed you that was in its OpenGL view and things like that. We have full Carbon support, so if you've written something in OS 9, you can carbonate it and bring it over to OS 10, it'll still work. And we have GLUT as well. It's 3.7.
We preserved the full OpenGL programming model. It's kind of implicit within the API. We changed nothing. So it's still the same OpenGL state machine. You still make polygons with vertices. You transform and light them. You stick textures to them. You render stuff. It's exactly the same. So all your OpenGL code that you wrote, those big, beautiful applications you've labored over for so long, can just come right over with a little bit of work.
and I will be talking about that in a few minutes. wrote high performance stuff in OS 9. You can do the same thing in OS X. It's the same model. OpenGL is fully integrated with Cocoa and Carbon, both, so that you can write your applications native OS X in Cocoa, or you can bring Carbon stuff over from OS 9, or you can write Carbon apps native OS X, too. So if you have a bunch of legacy code that you want to rewrite, or port, up to OS 10 you can do that as well.
The optimizations in OS X are centered around this preemptive model that we have. It really has to do with texture paging and how we access the driver. I don't want to get into a great deal of detail here, partly because I don't know it, and also partly because we can leave that to Q&A later for those of you who really want to get into how that technology really works. It is fully supported inside the Rage 128 driver.
It gives you really fast texturing. We have an excellent paging model that'll get your textures in and out of the card. On your, it does it kind of on your behalf. So you don't have to have fancy texture paging algorithms and know how to do a VM system just to make things texture mapped. We do it for you. It also works so that when your application is running on 10, it acts like it owns the the whole machine, which is the same as nine.
But on 10 you actually can't own the whole machine. The kernel owns it. You never can. But we made it so that it seems like you do. So if you have two applications running and they're both using OpenGL, they don't beat against each other. But they both perform really well. You're gonna see some degradation, of course, 'cause you have two apps.
So, you know, they go slower. Duh. But you can get it so it seems like you have the whole machine. You don't have to do special programming for that The APIs that we currently support, this is in DP4 right now, so the CDs you either got or are going to get have these extensions in them.
And I'm not going to go into a huge amount of detail on what each of these do. They're, they're documented pretty well. This is the OpenGL 1.1 API that is on your CD right now. So you can program this as soon as you get it. What's coming post-DP4, so the next user seed or few seeds after, it'll be full OpenGL 1.2.2. And we'll have these other extensions as well. I know Jeff went over some too. And again, I think in a Q&A session we can address these in great detail if you need to know.
Alright, the, when you're programming on OS X, if you've come over from OS 9, you're You have to think about a few things. We don't have any color index mode on OS X, so you can't do those cheats, you know, the, when you should be using compositing and you're not, you can't do those anymore. You really have to use compositing now. You can't go into color index mode and use mask off planes and things like that. It's not going to work.
In this, in no 8-bit color look up mode either on OS X, so you really have to, you know, pull up your socks, do compositing. There's no real true single buffer window mode. There's, there's some technical reasons for that in the Windows server. And it's really, all really is double buffered, although in the API you can ask for a single buffer, you just won't get it.
Multiple contexts can share a drawable. That's the same as OS 9. So you can have a bunch of OpenGL contexts all drawing into the same bit, if you want. And we will have true full screen support. Not in DP4, didn't make it. Sorry. It's coming soon. But that means that you can own the whole screen. Blow away the desktop, do cool games that are full screen.
So we'll have that, and it'll be really super fast. Just some notes on implementation in OS X. Classic is not in DP4, so if you have an app, an OS 9 app that you wrote and you didn't bother to carbonate it, you're just hoping that Classic will handle it, it's not going to work in DP4.
We do have full Carbon support, full Cocoa support, and I'll show you the level of integration we have with Cocoa later with an example. We support GLUT 3.7, so we ported that over, and that is a real OS X framework. It's not a carbonated version of the one on OS X. It's a rewrite for OS X. It sits underneath Cocoa. It accesses methods inside Cocoa.
It's a full native OS X. But I want to add to Jeff's note there is we don't really encourage using GLUT for shipping applications. It's an excellent way to get started. If you're not really hip to how to make Windows and do mouse events and OS X and stuff like that, GLUT is a fairly easy way to get started. It's got a pretty low admission price, and you can make Windows that have mouse control events and stuff like that.
and we'll have a full screen API that's coming soon. And that full screen API will also be integrated back into GLUT so that you can get stuff up and running pretty fast. Okay, enough slides. Now, I want to do something here, which is, I'm going to write some code.
Okay. Can you guys switch me over? Thank you. All right. Remember how we all talk about, I've got to take a swig of this. We all talk about how fabulous our integration is with OS X. I want to show you how, rather than talk about it. So I have Project Builder here. OK. No, is this, this is coming through, oh. How many of you have used Project Builder or had any exposure to OS X at all? About half? OK. Well, there are a lot of bugs got fixed.
There's still some in there. OK, so I'm going to start a new project. I'm going to make a 3D application. is the director of the OpenGL project team. and let's call it our favorite, the teapot. I don't know, you guys probably can't see I see this stuff way in the back there, but bear with me for a sec.
Okay, so Project Builder thinks about it for a little while. This is, here's the start of our application. We've got, Project Builder's made a whole Cocoa App framework for me. Now what I'm going to do is use Interface Builder is the director of the OpenGL project development team.
He's been working on the OpenGL project and the main menu and all that kind of good stuff. So all I have to do to make a 3D application is take this custom view, all right, so I'm going to stick a custom view in there, and this big gray area here is going to be my 3D view, in my 3D app.
I have to, right now this is just a basic view within Cocoa. So I have to tell it that I want it to be a 3D view. I'm gonna do it in a couple steps. First I'm gonna say, alright, I'm gonna take an NSOpenGL view, which is right there, it's built into Interface Builder. But I also, I need to subclass it 'cause I'm gonna do some of my own drawing. I'm gonna draw, guess what, a teapot.
Okay, so I want to call this subclass of OpenGL view a T view, and I'm going to get it to create the H dot H and dot M file for me. And then I take the custom view here and I say, all right, interface builder. I want you to make it one of my TView classes. So now it says, you can't, probably can't read that too well, but that says TView right there. So that means this view is now one of my OpenGL view subclasses.
Okay. And then I'm also going to tell it to resize itself when the window resizes. Okay, save that. And I already made the class file, so now I'm done. I basically just made a 3D app and finished. Oh, quit. Interface builder. I have to do a little thing behind the scenes here, and just don't pay attention to this for a second. Uh-oh. Really don't pay attention to this for a second.
For some reason, Interface Builder puts things in funny places. And, you know, well, that's Okay, this is, you know, a little Unix shell hacking here. I'm sure you're all very familiar with it. All right. Now, to come back to my fabulous 3D project, I need to take those class files that Interface Builder wrote for me and add them to the project. From what I can understand, this is all going to be much more tightly integrated in later versions. Probably available on the web. Any project builder people here? No. Too bad.
So now I have my classes here, my 3D view, and I'm basically done. This is a 3D application. It's not real exciting yet. I know you guys are excited by code. I know you are. So, okay. Now, let me grab some, do you guys all remember the galoping gourmet? This isn't going to work either.
I've got I made some other files. I'm not going to hack the whole thing now. So I'm going to take these pre-built things. There are a bunch of objects that I built. We can go through some of them that do a lot of the 3D stuff to draw the teapot. And I'm going to add these to the project as well. Okay, so that's done. And I have to add the thing I always forget to add, which is this.
Now, I'm going to open my cut and paste source because isn't that what we all really do, is we all just cut and paste. I don't think anybody ever really types anything. Okay, copy, and I want to go to here and...
[Transcript missing]
Objective C is, well, it's a little bit of a It's really object oriented, so these are actual real method names. They're like your class functions inside C++, only these are messaged at runtime, so that's a big difference. Just a little side note. OK, so let me get some implementation for that. Excuse me. First thing I'm gonna do is is, I have to include, whoops, wrong file.
: I have a problem with doing stuff live. First of all, I have to include the standard headers, which in OS X are in OpenGL/OpenGL.h. So GL programmers here, you've probably seen big GL there instead. So this is a, you know, it's a different, it's always a pain. On every platform, everybody puts their header files in a different spot. I don't know why, we just do. So, you know, you have to get over it.
Okay, now I'm going to pull up some implementation here. And I hope everybody can see this. You guys in the back, can you see the code okay? All right. Now let me go over this real fast and then we can build and run. This is not a super exciting program yet, and you'll see what I mean in a minute.
What happens here is you, if you were in Jeff's talk you heard about the pixel format. That's a way to ask for a certain kind of a drawable, in our case in OS 10 it's a certain kind of a window, you want a particular pixel depth, you want a Z buffer, a depth buffer stuck to it, you want it hardware accelerated. And you send that off to the, the Cocoa layer, which is in an OpenGL pixel format. That's this line right here.
And you, it goes into the GL engine, which queries the hardware, and says, OK, yes, I can support that. Or, here is the closest that I can get. So what I ask for here is a depth size of one. Well, you, obviously I don't want a one bit deep Z buffer. That would be dumb.
What I'm asking for there is, is, is, is The smallest Z buffer that you have, in this case I happen to know it's sixteen. I think we have sixteen in thirty-two bit, and there are cards that support different, different Z depths, but this will give me a sixteen bit deep, deep Z buffer. I've asked for accelerated, and this is just zero to, to terminate the list.
Okay. So then I get the pixel format here. This generates one of these pixel formats. And then, since I'm a subclass of NSOpenGL view, let me do this, I just say, hey, superclass, I'm all ready. I've got my pixel format. You know what the frame rect is. Go ahead and initialize yourself. Okay. That makes it 3D. Now I've hooked up everything. It takes care of hooking up the GL context, everything I need.
Then I have some responsibility to draw something. Well, that's done with this method right here. DrawRect. It gives me a rect to draw inside of. And all I'm going to do now, at first, is set the clear color to green. It's RGB and alpha. And then I'm just going to say clear the color buffer and the depth buffer and then GL finish. OK, so let's see if this works. I'm going to say build and run. And it, it's worked before on another machine.
Doing something. There it is. It's a 3D app. When you say, "Dave, it's a green square." It's 3D. Okay. Well, let's do something more. Now what I'm going to do is cut and paste more stuff. And I'll add a teapot. How about that? OK. I made a teapot object because, you know, I know how to do that. And all I do here in my view, I'm still inside my TVU object, my subclass of OpenGL view. I'm going to say I want to, teapot star, oops. These little keyboards, you know, they're annoying.
First step in, oops. First step in making my teapot is, in my view class, when it initializes, I'm just gonna go ahead and Make an instance of the teapot. And so in Objective-C, this is the same as your C++ constructor. Right, so I have a teapot object that I wrote that has constructors and destructors and stuff like that, and except that they're not called that in Objective-C. And this is just calling the constructor. So, makes a new one. Okay? Big deal.
Now I have to draw it, but drawing it is going to get a little more complicated than my green square, because the teapot is more complicated. The teapot object, I implemented a draw method on the teapot object. Right here. So you just mess, send the message draw, and it's gonna go and pump all the polygons and the texture information and the color and stuff like that into the GL engine to draw the teapot. Pretty straight forward. Well, before that can happen, I have to set up my 3D environment.
And that's what all this stuff is. So the first thing I do, I is I need to set up a light. I'm going to put one light in this scene, and this is how I do it. I put it somewhere in space with the GL position parameter. Okay, and the GL position is here. It's 0, 0, 6, 6.
Then I give the light some ambient properties so it lights everything, and this is a good way to avoid black screens, and, boy, you get them a lot in GL. Then there's the diffuse component of the light, which is really just saying that it's a white light by saying 1, 1, 1 here.
Okay, and then I turn lighting on with this. Then I turn on the light that I made, GL light zero. Then I have to say, okay, OpenGL, I want you to handle all visible surface computation for me. Turn on the depth test so that it doesn't show stuff, you know, inside out and weird like that. Then I want you to make sure the teapot looks nice and smooth. I want you to clear the buffer, the color buffer to all black. And that's the clear statement there.
Then I'm gonna set up a way to look at the teapot. Now I'm using glue perspective here because it's a handy way to think about positioning the eye instead of going straight to GL frustum, which is really a brain cramp. So I say I want a 60 degree field of view that's, you know, like 35 millimeter camera.
Then I take the drawing rectangle that I'm given, which is the whole view, and I figure out the aspect ratio by this. You know, it's width over height. It's pretty easy, X over Y. And then these are just parameters. You just keep changing them around until you can see the picture.
is the moderator. Okay. And then... Then, I'm going to position the teapot in space, six units down the negative Z axis. And I always like to think of units in, like, real, like, meters, you know. So I say six meters. So that way, when I get a black picture, I can go and get a meter stick, and I can put it on the ground, and I can measure six meters, and then I can stand and measure me, and say, "Oh, that's why I have the black picture." 'Cause these six units are,
[Transcript missing]
does it all automatically. And that's all just from Cocoa. Okay. But it doesn't move with the mouse. Okay. So how about if I make it move with the mouse? Yeah.
Okay. Okay. All right. Fortunately, I made another object, which is a trackball. And it's the virtual trackball. I'm going to show you a little object for, you know, it's the thing that takes the 2D mouse motions and turns it into 3D rotations. Actually, it turns them into quaternions, which are really cool. I recommend you read up on those. When I figured out how to do them, I felt really smart. It's like math.
Okay, so I'm just adding a bunch of stuff here. I'm happily copying and pasting. And what I'm doing is adding a bunch of interface stuff onto this TVU object. The interesting thing here is that I can add these member variables. And by the way, you know, I'm an old Microsoft hacker, and Apple hired me. So I put M underbar in front of all my classes. That's a real no-no in Apple, apparently.
and David Y. Anyways, so I have, I'm going to put a trackball object in here. And then I'm going to add some rotations. So before you saw that I had no rotation. I just, I just moved the teapot, that's all with GL translate. Now what I'm going to do is I'm going to make some rotations.
GL stores its rotation in angle axis, which is almost a quaternion but not quite, and Well, I'll talk to some people about that. So then I'm gonna add this rotate by method that the trackball object is gonna call, and I'm going to take the standard NSView methods, that are already in Cocoa, and I'm gonna add some code, my own code underneath them, alright.
So this is mouse down, mouse up, and mouse dragged, and I think you can probably guess what these do. This is when the mouse is clicked, this method gets called, and when the mouse is let up, this gets called, and when it's dragged, this gets called, with the events and stuff like that. So your object sits there and gets messages from the event system in Cocoa.
Alright, so let's go ahead and add the implementation of these things. First, I have to initialize is the director of the OpenGL project management team. Dave Springer is the director of the OpenGL project management team. Dave Springer is the director of the OpenGL project management team.
[Transcript missing]
Remember that linear algebra stuff you were all supposed to study? Yeah, this is it. Okay? Learn it. You'll feel smarter.
Alright, now I'm going to add the rotations. One thing you have to remember when doing OpenGL, if you've done 3D programming, you know, you got Foley and Van Damme and you read it all and it's really cool and you can write renders and stuff and make models and, you know, you're cool, right? You can make 3D, it's great. Well, the thing to remember is that in GL, the matrix functions are backwards.
I don't know why. They just, they're backwards. So in normal life, you would say, OK, first I want to take the teapot and I'm going to rotate it by whatever rotation it currently has. Then I'm going to add the little bit that the trackball does and then I'm going to move it away from me.
All right. That's normally what you'd say. Well, in GL you have to do it backwards. So I like to think of, if you're standing in the code at the teapot and looking backwards, it's sort of almost forwards. Well, you can figure it out. So, all right. Let's just say that that'll work. OK. Then I'm gonna take all this code and this, this stuff. Oops. Uh oh. I showed off my secret that I wanted to save till the end.
So let's talk about what this code does here. The rotate by is going to be called from the trackball object. And do you want to see what that looks like, the trackball object? It's cool. It uses quaternions. It's like math and stuff. So, it does this junk. And then somewhere in here, It's going to call, aha. OK. It figures, see it has stuff like ATAN too. It's cool.
It's gonna, it figures out a rotation. The trackball, you know, I don't really need to explain this, but you've got the, I'm gonna anyways. The mouse defines a 2D vector which you then map to a sphere. is a developer at OpenGL. He's been working on a lot of stuff for us. I'm sure he's been working on a lot of stuff for us.
Okay, so let's look back at the view. This is still my NSOpenGL view, which is an NS view. And what I do in, when the mouse goes down, is I tell the trackball to start. And I say, here, here's where the mouse was clicked. So the view knows where the mouse was clicked. The view is handling all the 2D stuff, the mouse events and everything like that. It's coming into the view. Then I send that.
I, I, I send The event is, is a, is an object, in Cocoa. I say what's the location in the window, so it does all the window transformation for me, and I get an x, y that's relative to window coordinates. I give that to the trackball, the trackball starts. Then, as I drag the trackball around, This...wait...that looks better.
I say, first of all, I'm going to do some drawing inside this view, so I have to do some things according to Cocoa. I have to lock focus on myself. Then, track, I say trackball, Roll to the new mouse location, because as I get these mouse dragged, it's telling me the x, y of where the mouse is.
And there's the event location window, so I give that to trackball. Now trackball, when trackball does roll to, it's gonna figure out all that cool math stuff, and then it's gonna say rotate by to this view. It's gonna message this view back with the new rotations, which is right here. And then those get accumulated up here. So this all sounds pretty good. Let's see what happens when the rubber meets the road. Okay. Hang on to your pocket protectors.
And that's it. Now I can--oops. That was the wrong thing to do. Do you want me to get Project Builder back alive here? I can put a brick texture map on that. Yeah. Why not? We've got time. Okay. Why is it, why is it saying the same thing? Yeah. Yes, it's rotating with the teapot, I think.
Hang on a second. Let me get this up here and then--I've got to finish the scripted part. And then-- Because now you're asking for stuff I haven't tried. That's really scary. This is bad enough. Alright. Let's go here. And then I put a little method on my teapot object that says, in, in is the director of the OpenGL project management team. Dave Springer is the director of the OpenGL project management team. Dave Springer is the director of the OpenGL project management team. There. See, I typed it in, too. I didn't even cut and paste. How do you like that? All right. Build and run. Okay.
Okay, so now you want me to make it so that the light doesn't move with the teapot? Well, you guys are pushy. This is, okay. Well, we have to go to Q&A. I specify the texture inside of the teapot object, which is right here. These are the teapot coordinates.
Okay, this is kind of an interesting little Cocoa tidbit, which is, I use, there's NSBundles. People work with those before? There's CFBundles. Yeah. They're a way of accessing, see, applications are built in what are called app wrappers, because we don't have resource forks in OS X. So you can't just take your stuff you need to append to your app and all your little adornments that go with your app and stick them in a resource fork and call it good. You have to put them in the Unix file system somewhere. Well, NSBundle and CFBundle are ways to abstract away the fact that you have to do that on 10 and not on 9.
And what I do here is I say find the brick.rgb resource. So that's just a file sitting in the file system. Then, I have this file called retexture, or this function I mean called retexture, which goes and sucks up all the RGB pixels. Excuse me. So now I have a big array, which is my texture map.
And here is where I set all the stuff to turn the texturing on. And if you were in Jeff's talk, you saw a lot of this stuff already. So this turns on a 2D texture. I want to repeat in S, repeat in T, use linear filters. Then here is where I say, What the texture is. So M texture is a pointer to that big array of RGB pixels, and this is where I tell it to use the texture.