Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2000-187
$eventId
ID of event: wwdc2000
$eventContentId
ID of session without event part: 187
$eventShortId
Shortened ID of event: wwdc00
$year
Year of session: 2000
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC00 • Session 187

OpenGL on Mac OS X

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 may have transcription errors.

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.

to make OpenGL really fast. We'll talk a bit about what kind of APIs we have, and I'll touch on how we, well, not how we did it, but what you get in implementation. Now this is a picture of OS X, shows the level of integration that we have achieved with OpenGL. It really is a full part of OS X. It's, it's like I said, it's not some afterthought thing that we glued on. It's, it's built right into it. under, in application services. So when you're writing your stuff in Classic or Carbon or Cocoa, it's really a part of the OS. It's a substrate that's in there for you.

OK. 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 OS9, which was none of those things. OS9, you could own the whole machine if you wanted to, and just kinda elbow everything out of the way, run your game, and you're on the iron. Well, that's not really true in OS10. We've made it, we've made just as close to that as possible, and still preserve full OS X. So you're still fully multithreaded, you're still fully multitasking, all those little neato little Unix demons are still running in the background, but you get your performance anyways. Thank you. We've worked inside the OS X hardware acceleration layers inside the drivers so that we are really on the 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.

You can, we added also, this is a feature that's in OS 9 as well. And we pulled it over to OS 10, which is that you can not use the, how shall I say, the current context. You don't have to set it all the time. This gives you about a 5% performance gain, which doesn't sound like a lot. But when you're going from 60 to 70 frames per second, it makes a huge difference. So we've preserved all that stuff. those of you who wrote high performance stuff in OS 9, you can do the same thing in OS 10. It's the same model. OpenGL is fully integrated with Cocoa and Carbon, both, so that you can write your applications native OS 10 and Cocoa, or you can bring Carbon stuff over from OS 9, or you can write Carbon apps native OS 10, 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, 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 and 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 ten, it acts like it owns 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 kind of stuff.

The APIs that we currently support, this is in DP4 right now, so the CDs you either got or are gonna get have these extensions in them. And I'm not gonna 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.

All right, the, when you're programming in OS X, if you've come over from OS 9, 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, 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. And no 8-bit color lookup 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 Multiple contexts can share a drawable, that's the same as OS9. So you can have a bunch of OpenGL contexts all drawing into the same bits 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 OS9 app that you wrote and you didn't bother to carbonate it, you're just hoping that classic will, will handle it, it's not gonna 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 9. It's a rewrite for OS X. It sits underneath Cocoa. it accesses methods inside Cocoa to run. So 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 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. OK, enough slides. Now, I want to do something here, which is, I'm gonna 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. Okay. 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? Okay. Well, a lot of the 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. for your very eyes, nothing on my sleeves. Now, I'll start with a Cocoa application. And let's call it our favorite, the teapot. I don't know, you guys probably can't see it. 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 to, to make the 3D application. OK. So here Project Builder's given me a window.

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 gonna stick a custom view in there, and this big gray area here is going to be my 3D view, it's 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, alright, 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 gonna 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 all I'm gonna say.

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. Thank you. Any project-building people here? No. Too bad. OK.

So now I have my, my classes here, my, my 3D view, and I'm, 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, you guys all remember the galoping gourmet? This isn't gonna work either. I gotta get the dang terminal again.

I made some other files. I'm not gonna hack the whole thing now. So I'm gonna 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 gonna 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... Okay, this is Objective-C stuff. For those of you who don't know, how many worked with Objective-C before?

Okay, about half. C++ programmers? Nearly everybody, too bad. Straight C and stuff? Yeah, okay. Objective C is, is, is, is, 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 run time. 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.

with doing stuff live. First of all, I have to include the standard headers, which in OS 10 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 gonna pull up some implementation here, And I hope everybody can see this. You guys in the back, can you see the code OK? They're asleep. All right. Now let me go over this real fast, and then we can build a 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 X, it's a certain kind of a window. So 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 a 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 The smallest Z buffer that you have, in this case I happen to know it's 16. I think we have 16 and 32 bit, and there are cards that support different Z depths, but this will give me a 16 bit deep Z buffer. I've asked for accelerated, and this is just zero to terminate the list.

OK? So then I get the pixel format here. This generates one of these pixel formats. And then, since I'm a subclass of NSOpenGLView-- 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. OK, 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. Okay, so let's see if this works. I'm going to say build and run. And it's worked before. on another machine.

doing something. There it is. It's a 3D app. And you say, Dave, it's a green square. It's 3D. OK. Well, let's do something more. Now what I'm gonna do is cut and paste more stuff. And I'll add a teapot. How about that? OK. I made a teapot object 'cause, you know, I know how to do that. And all I do here in my view, I'm still inside my TView object, my subclass of OpenGL view. I'm gonna say I want to, teapot star, oops. These little keyboards, 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 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 send the message draw, and it's going to 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 straightforward. 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... 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 going to set up a way to look at the teapot. 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.

Although, once you figure it out, it's cool. 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 around until you can see the picture.

what I do. Okay. 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 and say, "Oh, that's why I have the black picture." 'Cause these six units are, What units? You don't know. So I say meters. Okay, so six meters. All right, so that's it. Now, and then I tell my teapot to draw itself. Okay. Let's try it.

Okay, it looks black back there. It resizes. does it all automatically. And that's all just from Cocoa. OK. But it doesn't move with the mouse. OK. So how about if I make it move with the mouse? Yeah. OK. All right. Fortunately, I made another object, which is a trackball. And it's the virtual trackball.

object for, you know, it's the thing that, that takes the 2D mouse motions and turns it into 3D rotations. Actually turns them into quaternions, which are really cool. I, I recommend you, you read up on those. When I figured out how to do them, I, 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 under bar in front of all my classes. That's a real no-no in Apple apparently.

Why? 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 moved the teapot. That's all with glTranslate. 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 rotateBy 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 under, 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.

Right. So let's go ahead and add the implementation of these things. First, I have to initialize these rotations. So up here in the initialization code, I'm going to go ahead and add-- this line here, which makes a new trackable object, then these lines here just initialize the rotations of the teapot to an identity rotation. In other words, no angle and unit vector along the x-axis. Remember that linear algebra stuff you were all supposed to study? Yeah, this is it. Okay? Learn it. You'll feel smarter.

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, okay, first I wanna take the teapot, and I'm gonna rotate it by whatever rotation it currently has. Then I'm gonna add the little bit that the trackball does, and then I'm gonna move it away from me. Alright. 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, alright. 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 rotateby is gonna be called from the trackball object. And, do you wanna see what that looks like, the trackball object? It's cool. It uses quaternions. It's like math and stuff. Saying it does this junk. And then somewhere in here, It's going to call, aha. OK. It figures he 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. a unit sphere usually, and that gives you two vectors and you take the cross product and you've got a rotation angle, and if you take the dot product, you have the cosine of that angle and stuff like that, and then you can turn that into an angle axis rotation through some quaternion math, which is down here, and then I just send that back to the view that requested the trackball.

OK, so let's look back at the view. This is still my NSOpenGL view, which is an NSView. And what I do when the mouse goes down is I tell the trackball to start. And I say, 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.

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, I Okay, this, wait, that looks better.

I say, first of all, I'm gonna 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 and 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. Thank you. Do you want me to get Project Builder back alive here? I can put a brick texture map on that. Yeah. Why not? We got time. Okay. Yeah. Yes, it's rotating with the teapot, I think.

Hang on a second, let me get this up here and then, I gotta finish the scripted part. And then, 'Cause now you're asking for stuff I haven't tried. That's really scary. This is bad enough. All right. Let's go here. And then I put a little method on my teapot object that says, in, set use texture. 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.

OK, so now you want me to make it so that the light doesn't move with the teapot? You guys are pushy. This is-- OK. 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 worked 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 'em 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 nine.

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 mTexture is a pointer to that big array of RGB pixels. And this is where I tell it to use the texture. And then I turn that switch on down here. This is the set use texture method you saw before. So this is the implementation. I just turn a texture flag on. That's all. Okay, why don't we go to Q&A? Because I think... Yeah. Okay, that's it. Let's go to Q&A. Thank you.