Digital Media • 1:01:47
OpenGL provides a powerful cross-platform 2D and 3D graphics API for all applications, not just games. This session covers handling multiple contexts, combining 2D and 3D graphics, and using the orthographic projection to create accelerated 2D graphics in any window.
Speaker: Geoff Stahl
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good morning. I'm Geoff Stahl. I'm going to be talking about OpenGL and talking about using OpenGL for non-game applications, even though a lot of this may be applicable to game applications. I'm manager of the DTS group that handles OpenGL, handles 2D graphics, handles QuickTime, Java, and printing. But the real people who do the real work are here today also visiting, throwing things at me, and making comments about things that get wrong later.
John Stauffer is the engineering manager for OpenGL group over here. Also, we have a number of guests from ATI who do OpenGL drivers for us. Chris Bentley is sitting on the side, Brian Raderman, and Jeremy Sammel working on the OS X work with ATI. We appreciate them being here. Let's jump right into the good stuff.
Okay, again, as I said, we're going to use OpenGL for non-full-screen immersive applications. So a lot of people look at OpenGL and see Quake or see some of the full-screen immersive things that have been done. OpenGL works really well for maybe a 2D application that just needs to do some rendering or some drawing and wants to have high-speed rendering, wants to have that acceleration, and doesn't want to have to reinvent the wheel yourself.
It takes advantage of the fastest Macintosh pipeline. What this means is basically that all your graphics there, even if it's a 2D texture map, just a 2D image sent to the screen, is going to go through all the acceleration layers and use all the accelerated context. So this allows you to get truly high-performance graphics without having to get down into the bits and figure out how to flush caches and what the bus bandwidth is and how to keep that optimized. That's all done for you.
Another thing about this is that they've done a lot of hand-coding and hard work to take advantage of the Velocity Engine. There's significant improvement you can see on OpenGL applications from a non-Velocity Engine to a Velocity Engine machine, and this is for free. You just get this and it works great.
It's good for 2D, 2.5D, and 3D. And for those of you unfamiliar with how I'm using 2.5D, let's talk about maybe, let's talk about a game actually in this case. Let's talk about something like Diablo. Diablo basically is a sprite-like based game. They have a background, they have orthographic or flat sprites that move around on top of this.
OpenGL can be used for this kind of thing or any other 2D kind of sprite game where there may be a depth, there might be a stacking of things. Look at PowerPoint. PowerPoint is really a 2D thing, but there's a sorting order to it. So you could do PowerPoint in OpenGL, rendering the text, putting the things up there, and you would get all the fades, transitions, rotations, those kinds for free.
Because it's all accelerated, all in hardware. Once you get your image in there, you can manipulate it however you want. So that's good for 2.5D, good for 2D, good for some things like this. Some things it may not be good for is vector drawing. For example, we have Quartz, which is exceedingly good at handling PDFs, handling text, handling vectors, scaling. OpenGL does scaling, but the scaling is for a bitmap. So if you send a texture in, it's going to scale based on that texture.
So if your texture is 32 by 32, a little icon there, and you blow it up to full screen size, you're going to get a 32 by 32 icon blown up to full screen size. It doesn't do anything special with it. Whereas Quartz may have a bit of a texture. So you can have a text and a description of the text in a vector format, which will then, when you blow it up, keeps the vector format, keeps the smooth curves and lines.
So OpenGL, if one thing it doesn't really do well, is handle images in a vector format, though the bases, the polygons, the points are all vectors, and those scale infinitely, basically. And it's easy to add to applications and code. You don't have to go take a linear algebra class and go find some dark corner of some library that has some dusty old volume of how to code in OpenGL. it's not that difficult.
So let's kind of jump right into things here and move ahead. What I'm going to do in this session is I'm going to go through, I don't have a lot of slides. I have a lot of code I'm kind of going to show you. We're going to go through some demos.
You're going to see kind of one ubiquitous spinning square demo a lot, but basically I'm going to use it to show you how to manipulate kind of different constructs of OpenGL. One of the keys in this is I'm going to be talking about AGL in a lot of ways and how to manipulate your buffers, how to set it up so if you had text in one half of your window, you could put OpenGL in another half, how to do some texture mapping and stuff. A lot of this is basically, I'm going to demo in an OS 9 for simplicity, but basically it's all carbon-based.
Almost everything you see here is carbonized, runs on 10, AGL works on 10, and the stuff you see here almost all works on 10. Now I'm trying to think, some of the full-screen stuff is not quite supported, but that will be supported shortly. So what are we going to do? We're first going to talk about pixel formats. I think I mentioned pixel formats in some of my other OpenGL talks.
We're going to talk about some of the, I think I mentioned some of the caveats there, especially when you're dealing with windows and multiple monitors and multiple renders. We're going to talk about using multiple windows. If I want to draw OpenGL, this window, this window, and this window, maybe I want a CAD application. Do the perspective view and then four orthographic views from the front, a side, and a back, or a front, a side, and a top actually. In that case, you can use multiple windows OpenGL.
Just update them, mine update events, real simple. It works right in an event loop, works great for good stuff. Subwindows, combining 2D and 3D. How actually do you get 2D and 3D on a window? If you've played with OpenGL at all, one thing you'll see is if you draw 2D and then you draw some OpenGL and call swap buffers, it's going to blow away that 2D.
It does not know anything about that drawing. It does not know about how you, that you had something there. It does not know about compositing to your background. So you have to consider that, that the area you're updating in OpenGL is going to take over basically that area that you set up. Talk about some basic font handling.
Then we'll talk about physical simulation. I have a couple of things I want to talk about. I want to talk about a couple of demos and talk about just using OpenGL for physical simulation. Nothing really, really big. There's no real good answer. I can't give an answer for the world on physical simulation. There's a lot of text on that.
But how to use OpenGL, some of the considerations. Orthographic projections. Orthographic projection is kind of a big word. And you say it real quick and someone says, "What the heck are you talking about?" Real simple is that for like an orthographic projection might be something that puts the text on the screen while behind that you have a perspective view of orthography. So, you're going to have a walk through. So, as you're walking through your virtual library, you want to identify what room you're in. And you want to do that in text.
Well, you would switch to an orthographic projection which allows you to nicely place your text on the screen. It wouldn't, you know, do your-- do any manipulation or any perspective on that text. And then we'll talk about texture mapping at the end. Real simple. I'm going to show you how to get textures into the Macintosh, how to texture map them. So, you can get some of the basic stuff going, which is kind of the crux of doing 2D and 3D.
Okay, pixel format considerations. AGL Choose Pixel Format is how you set up basically your kind of drawing, I want to say context, but that's not the right word. AGL Choose Pixel Format sets up the basics of pixel format. And what it handles is it handles things like RGBA displays, so red, green, blue. It handles whether you want a depth buffer or not. It handles whether you require an accelerated context or not.
And also handles if you want to choose a specific renderer. And let me pull up an example of some of these and we'll look at it. Some of the caveats here are handling multiple displays, handling movable windows, and I'll talk about some special considerations. Actually, let me move forward a slide. Okay, we've got all kinds of things on the computer here, and this is going to be a real simple one. I need to set my font size here.
What we have here is talking about some pixel formats. I brought this up so it's easier to talk about to see some examples of them. Basically, in almost all cases, if you want to do hardware acceleration, you want to do red, green, blue, and alpha. 8-bit is supported in software, but almost no hardware right now supports 8-bit context. So if you want to do hardware acceleration, do high-speed graphics, you want to do red, green, blue, and alpha. Then we're talking about AGL render ID. AGL render ID says, I want a specific render.
And you can ask for any render that we enumerate. If you want a 3D effects render, you can ask for a 3D effects render. In this case, I want to say generic render ID. But this says, I want the software render. The reason I put this up here is so you know that one good test you can do if you're working OpenGL and you're not getting things right, and this driver is really bad, it's buggy, it's not drawing my stuff right, go to the software render.
Check out and see what the software render says. If the software render renders it correctly, then it may be a bug on our part. If the software render renders it incorrectly, then it probably is something in your code. So I have a challenge, and this challenge is for, I put it out pretty much every time I talk about OpenGL.
I have tremendous confidence in John Stauffer and his ability to code outstanding implementation of the software render. So if the software render you find is not compliant with the OpenGL specification, send what you think is not compliant. We'll look at it. If you're correct, I'll send you a t-shirt.
Right now I'm doing the OpenGL t-shirts, but I'll find something if I run out of those. But I haven't given out a t-shirt yet. So that's the challenge. So if you think you have a non-compliance in the software render, send me a note, and if you're right, I'll send you a t-shirt. .
The AGL render ID should be followed by a render ID, and that's in the AGL header files. The AGL render generic ID says software render, basically. So AGL generic render ID says I want the software render. That generic render is the software render. There's another trick to do this.
If you want to go to software render on a pre-compiled code, you just want to check out, see how it runs, pull out the ATI or 3DFX actual OpenGL accelerators or OpenGL components out of your system folder, and the only renderer left will be the software renderer. That's another test you can do. If I want to get any render, basically I'm going to say RGBA, AGL all renders, AGL double buffer, and none. The double buffer means I actually want a double buffer surface, which in almost all cases you do.
AGL all renders, what does that really tell you? Why is that different than just leaving it out? AGL all renders will accept non-compliant renders. For example, the Rage 2 shipped on the original Rev AI Max is a non-OpenGL component. The AGL render ID should be followed by a render ID saying AGL generic render ID says I want the software render. The AGL render ID says I want the software render.
The Rage 2 shipped on the original Rev AI Max is a non-OpenGL compliant renderer. In that case, this will select that. Star Wars Racer wanted to run on iMac Rev A's, so they would use a string such as this to make sure they would be able to select that. Otherwise, it will refuse to select any hardware accelerator render in that case.
OpenGL hardware compliant renderer, same as the above string basically, but I got rid of the HL_AllRenderers, hardware accelerators, and added HL_Accelerated. Here's a key that I talked about moving windows. If you have a window that you're using OpenGL, and you're moving the window around, and you ask for an accelerated renderer, it's going to fail.
Why does it fail? It fails on multiple screen displays because we coded a fallback case, so when the window spans two screens, you go to the software renderer. So it gives you rendering while you span two screens. So if I have a 3D effects card in one of my slots, and I have an ATI card in the other slot, and I drag across, if you use a string such as this, I'm sorry, in most cases you'll get ATI rendering. You'll get a lot of rendering.
So if I have a 3D effect card in one of my slots, it moves across. When it's between screens, you get the software renderer. When it's back on the 3D effects display, it'll get 3D effects renderer. And one interesting thing is you can easily check if you use GL_GitString, check for the manufacturer and the version string, you'll get either the ATI, the Apple software, or the 3D effects. So you can identify what renderer you're actually on.
If I use this string right here with the AVL_Accelerated, and I had a movable window, so I basically say I want it for all my monitors, and it'll fail because it says I can't handle the case where you're between screens. It's pretty logical, but it may be a little caveat.
Say, hey, why is this never working? I have AGL_Accelerated, and I can never get an accelerated context with my multiple screens. So that's something I've run into with people. So that's a little note on that. ATI compliant renderer, if I want an ATI, again, AGL_Renderer_ID, add an AGL_Renderer_ATI_ID. And if I wanted to get an OpenGL compliant, ATI Render Hardware Accelerated Render I would add the AGL Accelerated.
So what does this look like? Real simple, one of the simple ways to do it, you can do it like above or you can just add the attributes in order, RGBA, double buffered accelerated. In here, in this case, I'm indicating that I want a depth buffer. One key here is with the depth buffer, if you actually do something that has depth, if you're going to have back-to-front sorting, you need to set a depth buffer up.
You can turn on depth sorting, you can do everything you want, you can turn on Z value, you can do everything you possibly can in your GL code. If you don't set up a depth size and actually have that depth buffer there, it will never do any depth sorting. You'll be like, why is this not working? Because you don't have a depth buffer. GL is like that in a lot of ways.
You get down a road and you're sure you got it right and you forgot to enable a mode or set up a depth buffer. So that's a little caveat there. So you definitely want to do these two lines if you want to use depth sorting. And then AGL none terminates all these. So.
Let's talk about multiple windows, which is really, for me, the main difference between a full-screen immersive and a non-immersive application is handling windowed cases and combining 2D and 3D together and combining text. For multiple windows, we'll talk about-- oh, I'm sorry. There we go. We'll talk about handling multiple pixel formats, what that means. We'll handle multiple contexts, multiple drawables, and considerations such as AGL up-fate context. That should be update context, my bad. I'm sorry. And resizing.
Multiple pixel formats, we've talked about that. Pixel formats set up whether you have a depth buffer, whether you're accelerated or not, what render you may be using, those kind of things. Multiple context encapsulates an OpenGL state. A drawable is actually a window. And when we talk about considerations, we'll talk about how that relates to all the above. Let me actually go through this.
I have my little cheat sheet here. So what are we going to do? I'm going to go through a lot of coding examples here. I talked about this as non-gaming. So what do we have? I want to run the ubiquitous game app. So this is the app. You'll see this kind of thing all over the demos and hopefully the projector will think awesome. So this is our game app.
[Transcript missing]
There's really no, since a pixel format is your basically lowest level, everything needs to set up to that, there's really no reason to have more than one pixel format per context. Because when you create a context, you're required to supply a pixel format. You'd have to destroy the context and pass in a new pixel format. So there's no changing a pixel format. You can't maintain a depth buffered one and a non-depth buffered one.
If you want to make that kind of change, you're going to have to recreate the context, create a second context, or recreate the pixel format. You can't go through and have pixel formats swapping to multiple contexts. So that's the thought, that if you do have something you want depth buffered and not depth buffered, you're going to have to work on multiple contexts, not multiple pixel formats. Okay, so now let's talk about multiple contexts.
So, multiple contexts, we'll pull up on my little application here. I'm going to have to, again, I'm going to resize this to make sure that everyone can see this once I bring up the file. Basically, multiple contexts handle different GL settings, basically the state of GL. You can say, hey, I want to draw with shaded, I want to draw with line version. So what we're going to look at is just this little section right here of code. Let me make this bigger for you.
So this works. What we're going to look at here is setting up a couple contexts. I have two rectangles at the top here. They're So, in this case, they're both different sized rectangles. They have a window. I go through here. I set up my pixel formats, and I use my setup GL sample code and call build GL from window. So, I build a GL context there. You can see that's context number one. Then, basically, do a similar thing on this side. Create a window up here.
Set up some similar settings. You notice this line right in here. For draggable windows, as I said before, we can't use AGL accelerated because what happens with draggable windows, you're saying you want them across multiple monitors. If it ends up between two monitors, you're going to have that fail because it can't fall back to the software render.
In most cases, you will actually get an accelerated context. There's very few cases you won't if you have an acceleration card. So usually I just eliminate AGEL Accelerate and I should get an accelerated context with that. Then I build the Gelfand window. And what I did here was, on the second context, I said, really I want this one to be flat shaded. So if we run this, Very simple. I have two contexts. One's a draggable window.
One's a normal window. And this window over here, exactly the same drawing routine. Calling the exact same thing, so the code paths for each is exactly the same. All I'm doing in this case is that this context here is set up for flat shading. If I take it out, then it would just look exactly the same as the first one. We can see that. Normally the shade model is smooth shaded, so I'll go back to the default version of that.
This session covers handling multiple contexts, combining 2D and 3D graphics, and using the orthographic projection to create accelerated 2D graphics in any window. : In the next slide, we will see how the fixed window is projected into the hallway, and the actual line view of the fixed window. The fixed window is the working copy of the actual line view of the actual building. The fixed window is the one that shows the actual rendered image. This is the kind of thing you would use multiple contexts for.
Okay, so what else do I got? Okay, so I did flatline drawing that one, so we're done there. Let's move on. Let's look at multiple drawables. So in this case, multiple drawables is the same context with multiple windows. Maybe you want the exact same thing to show, but you want it to show in different windows, in a different view, you just want to rotate a camera, and you want all basically the GL settings will always be linked. So if you set one to a different setting, both of them will link together, but it gives you different views or viewpoints on the same scene, we'll say. So, let's get back to another simple spinning square application here.
And in this one, let's just run this and see what we get. So I have two windows, same size, exactly the same view, same view of the world, everything's the same. So let me prove that to you. So let's go back into our...
[Transcript missing]
Let's say, let's make the drawables actually a different size instead of making them the same size, showing that they're actually drawing exactly the same thing unless I do something to affect that.
There you go. So in this case, they're actually, since they're drawable setup on the back window first, the front window thinks it's basically the same size as the back window, and just draws exactly the same thing. The question is, how do we fix this? So that's basically the point of all this, is if you're handling multiple drawables with a single context, what do you need to do for that? And what you need to do is you need to make sure that you set the viewport correctly. And to set the viewport, we'll scroll down a little bit in the code, into our drawing routine. This is a simple drawing routine.
Basically what I'm doing here is I'll use these two lines of code here. What this does As you notice, this is the due update. So I used to have a standard Mac program. Every time I update, I'm drawing GL again. In this case, I use GL viewport. GeoViewport takes the X and Y offset and then also takes the width and height. And in this case I did screw up again. Wow, I'm not doing very well here. It should be bottom.
and if we compile this, you'll see that we should now have the drawable size to the appropriate window sizes. Okay, there we go. So we're back at something we kind of expected. And if we do like a window size here, it actually keeps the viewport to the window.
So another thought here is one thing you'll notice when you're working with OpenGL is that If you're doing something that's supposed to look realistic, and someone does that to your window, you realize that just by setting the viewport, OpenGL is going to scale everything. OpenGL thinks of everything as basically zero to one. Zero to one.
And so what happens is, when you scale things in a very small space, if I scale things like that, you get definitely... OpenGL says, "Hey, I can do this. I can fill this rectangle, whatever you provide me, with that right size of your drawing." And at that point, it'll just scale. So there's ways to avoid that. You can set your aspect ratios and you can check that you're so instead of really smishing your viewing up, you're actually just taking a piece of it out.
So, and one more thing is to, let me prove that actually this is actually a separate, you know, draw, and this is using the same context. So if it's using the same context, you would think any OpenGL state change to that contact, anything affecting the state would change both of these. So let's make a state change here.
So, same lines of code I used in the last one. In the last one, I had two contexts, I changed lines of code, they operate independently. In this case, again, this is something you may want to have two views with the same context, two views of the same scene. So we should get basically two views of the same scene. So the scene when it's drawn should look the same.
Oh, there we go. Flat shaded lines. And so, as we saw, when we change the context variables, when we change the OpenGL state variables, what happens is we get the flat shaded kind of thing, we get the line outline instead of the little spinning square. So, again, let me recap a little bit.
If you're doing an OpenGL application, you want to have multiple views and you want to have multiple views that have different kind of OpenGL large scale states, such as orthographic versus a projection, such as a flat shaded versus a smooth shaded. You may want to set it up so you're having the two are separate contexts. If you're doing something that just has different views of the same world, you want to set it up so you're using two different drawables, same context. Let's move on a little bit.
So, multiple contexts and multiple windows. This one is a real simple one. I already showed the viewport from the last one, and this just shows a couple other things on it. And probably I don't really even need to show this. Other than we'll talk about some specifics I didn't talk on the last one. Same kind of thing. Okay. What you see here is basically the same thing, but you notice there's a black border around one of the things. And the black border is from basically a sub-window, which I'll talk about in a second.
The key here is that when I move this, when I drag this, when I do that, what do I need to do to OpenGL to tell it that actually I did something to the window? And it's pretty important. Because if you don't, the window won't change. Nothing will happen. The window will draw the same size, thinking that it's drawable is the right size.
You need to call updateContext. And let me, this code may be a little bit convoluted because of the Carbon stuff in it, but we'll show it to you and we can We can look at exactly when you need to call update contexts and how you actually grow and drag windows.
And so, just so you know that if you're not taking notes on this and don't have the right feverishly, I'm going to put all the code up on the website. There will be examples of this on the sample code website, so you can pull it down and play around with it and look at it. So for example, let's look at the ingrow.
standard thing for growing a window. This is kind of code. You set REC to a big REC, you grow the window, but then what do I need to do to open GL for the grow? Basically I set the port here, I inval the REC to make sure it redraws all, and then the key here is down in this area. We'll look at the window 2, this window first. This is a window I actually can drag. I call I'll call updateContext here.
Oops, sorry. Call updateContext, and I call glSephViewport. What that does is update context tells OpenGL that you've changed something as far as that drawable in the window. For example, if you look at the anatomy of how OpenGL works with a window, what happens is there's a front buffer, which may be a certain size, and we talked about the back buffer before. The back buffer actually is built behind that front buffer, pixel for pixel. So if you resize the window, the front buffer changes and the back buffer must be reallocated.
So what we have is we have a lot of reallocation in that video card. I mean, for example, if you had loaded it up with your 200x300 little context and you had all your textures loaded in there and then you go to 1024x768, definitely there's a lot more things that are being used, a lot more resources that are being allocated on your video card for the back buffer and that front context. Thus, it's going to have to do some shuffling. It may purge some textures. There are a lot of things going on in the background just by resizing that window. I'm going to kill myself up here. But that's okay. I think I'll get through this before I die.
GL update context tells AGL, which tells GL that you've changed the context. Show and hide, definitely should call it. Move window and resize window are cases where you definitely should call AGL update context to let GL know that something has changed for that context. GL viewport then was a thing I showed you before. The GL viewport basically sets the size of your viewport to that window and scales anything you're drawing to that size of the window.
Subwindows. Let's look at subwindows now. Subwindows are pretty cool. Subwindows is probably one of the most useful things for people who are using OpenGL in a non-fullscreen app. Any kind of thing where you have a part of your window you want to draw a chart, a graph, or anything like that, it's really simple to do.
You can use any of the line constructs, you can just draw squares, you can draw filled squares, shaded squares. That's really simple. Any OpenGL book can tell you how to do that. But the question is how do you constrain your drawing, how do you make your drawing work, draw in the areas you want it to draw in.
So let's run this first, actually, and then I'll show you how it works. Again, this is a spinning square, and you notice now we have two spinning squares. This is pretty simple. It just puts a square. We have the one window, which is exactly the same as before.
And what I've done in the second window, when I go to draw it, it's the same context, same drawable, but I draw it twice setting up what's called a buffer rec. And the buffer rec is nice because in this case, when I click the mouse, it adjusts the vertical part of it to where I click the mouse.
So you can see that I can adjust the buffer recs there. So you can see that you can put a divider bar and have your OpenGL rendering, whatever you're doing, a visualization of a 3D visualization of a website layout with a list on the other side of all the web pages. And as you drag the slider bar, you can adjust the size of your display. This is an easy way to do it. This works great. This is called AGL buffer rec.
And again, someone may wonder, if they're new to GL, why is it AGL and GL? AGL is the Apple's implementation-dependent sections of OpenGL. Anything that has to do with windowing, anything that has to do with the display is all through AGL. Thus, a buffer rec, which is adjusting inside a window, inside a drawable, will set up in AGL. And that basically sets up kind of a clipping area. It sets up a small area that it clips to that area and just draws GL in that area.
One note here, if I didn't set the viewport as we saw before, Buffer Rec works great. It'll draw a little window that you can move around or whatever. They don't just draw a little cutout. So if you set your viewport, your viewport will stay in the bottom corner as normal of your drawable.
And you can set a Buffer Rec without setting the viewport and it'll just move around on your screen. I may not even show you that. Let's quit this and I'll show you a very simple setup here to do here. This is the Buffer Rec code. I'll bring it bigger for you.
Okay, on my do update again, what I do is these are, I have two, - Section of the drawing, one here and one here. And if you notice that I use the buffer rack, and the buffer rack is, in this case, x, y, width, height. And what I'm doing here is setting the height of it to my mouse location.
When I get a mouse down, I find the location in window coordinates, and I find out what it is, and so I set that. So that gives me the height of that mouse location there. I call glSetInteger, Set my context, set call AGL buffer rack, and pass in the buffer rack. So that tells it how I want to constrain my drawing.
And I called GLViewport for the actual viewport. I'm trying to think of how I can do this to make it actually interesting. Okay, I know what I can do. We'll get rid of that and we'll do this in a second, and we'll get rid of that. But basically the other one's the same way. So I'm basically drawing two pieces, just adjusting the interior. I talked about how you can use Buffer Rack to actually take a little window on the world kind of thing, and this should do that.
Yes? If you notice, it's in my infinite wisdom, I have some update context in there, but basically the update is out of sync as I move down. You see that unless you call viewport synced with your buffer rack, that you have cases where you're looking at either old data that's not being updated or you're looking at the viewports remaining the same because it's not updating.
This section up here has never been updated again because a swap is just calling it on the small buffer rack. We can also see that... Let me show you another case of that. That's something to worry about, or not to worry about, but to understand. I'm going to go back one example, the multi-context one.
Just run it for you. If you notice I talked about that black border. The black border is inset with a buffer rack, real simple. But notice OpenGL doesn't update any of that area. OpenGL is not responsible for that. So if you have areas that you change your buffer rack or areas that you take over and you want to update, you need to make sure that you're updating that area because OpenGL is not going to do it.
You saw it in this Right here you can see it, whereas when I change my drawable, here, it doesn't update that top part. Buffer Rack resets it to set that area of draws. It just constrains the drawing, but doesn't say, "Oh, erase the background." OpenGL works the same. It's very linear thinking, so if you don't think it's going to do something, it probably won't do it. So let's go back and see where we are.
Okay, actually good, good. I'm about where I want to be. You notice on almost all of those, you may have noticed on the screen that you saw fonts on there. How do you do fonts? Fonts are a great thing to put out because you can just put text on the screen. Works pretty well.
It's something that you definitely want to probably do, and if you don't have a way to do that, then We talked about OpenGL Buffer by combining 2D and 3D. One thing, actually, there is another caveat here. Let me go back to this for a second. 2D and 3D.
Keep them separate. Don't try and draw OpenGL on an area that you're also writing 2D on an area. Make sure that your 2D and 3D are separate areas. If you want to put a frame around a 3D area, that's fine. Just set the buffer record for whatever you want. And then put the frame around it with your 2D drawing conventions. Just don't try and overlap them. Because what's going to happen is, the OpenGL is going to draw right over whatever you have there.
The OpenGL doesn't know any better. It doesn't know that you're constraining it. OpenGL does abide by clip regions. You notice if I switch the windows in the background, it didn't draw over top the other window. It does handle those things in windowed areas. So it's just fine. So there's no problem with the app switching to the back. You don't have to worry about, oh my god, I have to set this weird area to draw. And it'll take care of that.
But don't try and draw OpenGL, like for example, in that spinning square. Don't try and do a swap buffer, and then immediately try and draw text to the screen. OpenGL is a pipelined and asynchronous method of drawing. So what happens in it is that swap buffer basically says, sometime in the future, I'll get to it.
The engine usually is running much faster than you can sync commands to it. So usually, you saw 400, 600 frames per second. So if you draw in text every frame, it's probably synchronized. But what's going to happen is that in reality, you're lucky if it's synced. It doesn't really synchronize together.
By calling swap buffers, it doesn't sync to anything. There are ways in glFlush and glFinish to do some synchronization. But I recommend staying away from that. I recommend using the buffer recs. I recommend drawing parts of the screen. I recommend you could even do a clipping region and clip out the area that you don't don't want to draw into and then using 2D specifically in those areas.
So we taught you full window updates. If you're doing a full window update, make sure you draw the 2D pieces also. If you change your buffer rack, OpenGL is going to forget about the piece it doesn't want to draw anymore, how you moved it, and you have to take care of drawing that.
Basic font handling. As you saw in my demos, there's some fonts on there. If you want to put fonts on top of 2D, don't draw text on top of 2D, or on top of 3D, sorry, on top of 3D, because it won't work. So you have to use some of the font handling that we have in OpenGL.
John's been very nice to work and put AGL font handling in that works like a champ. It's super easy. Once you figure it out, it's like you can put any text you want on it. What you can do, though, is you can't rotate it and you can't blend it. It's just bitmap raster fonts.
It'll work great to draw strings, work great to color it, whatever you want, but you can't blend it with the background, and you can't. I'm sorry, when I say blend, I mean blend the actual text, not the background. You don't get blocks of black letters. The text is transparent in the areas that it aren't actually drawn to, but you can't blend the white text into gray into the background kind of thing. So let's look at the code to do that, and it is super simple, and I think I have it on the web right now. And. And if it's not, I'll put this up. I like this stuff. Because it's really, really easy.
Okay, I have this big draw frame rate, but that's not the real key. The real key is the build font GL. Okay. We call GL unsigned integer list base, and the GL gen list, and basically that makes 256 slots for basically display lists, so it's holders for the font letters. Then I'm going to call AGL use font right here.
I give it the context it was passed in, a font ID, so I use like Monaco, whatever I call, get font FNUM. A face, a size, so a face may be normal, size would be 9.0256, this sets the parameters for how many it's going to generate, and the list base, which is what I got right here. Basically what that does, it generates all your fonts. It takes all the fonts, stuffs it into the list, and you get a font that you wanted, whatever raster font you wanted.
This is something, because of the way AGL use font sets up a GL pixel map handling, you want to reset your pixel store, called GL pixel store I to zero. This is kind of a little bit of a trick, but it's a little bit of a trick. This is kind of a key thing to do to make sure that you don't have problems later on trying to draw textures. And real simple, to delete it you call delete list, the font list you created that's passed in, and 256 elements in that list. Pretty simple. And what's even better is my drawl C string GL.
All you do is go, you take GL int, set an I equal to one. Wall, you have letters in your out string, you want to use call list, font list plus the base in your, and increment I. And there's other ways to do this with for loops and some other loops.
But basically what that does, it's going to walk through your list, and the list basically says, it's a display list in GL, which basically says draw this character at the raster position and advance the raster position with the character. So you get the proportionally spaced fonts. So it works fine. It's not like they're all blocked out.
And you get all the letters, and the letters will draw nicely on the screen. You get nice letters. You can put information out. You can put your frame rates out. You can put version numbers out. You can put debugging information out. You can put information what your states are. Works great.
Simple. So you then have a, basically you're kind of print F for GL. You can, in that way. Or I'll say print F. You can use print F to, print F to format, and then you can just spit it out to here. So that works well. And in this case, this is just some code that I'm using here to do frame rate timing inside of it. So I call that once per frame. So that's more of a, there's no real complication there.
So, I won't show you anything with fonts. I'm just going to show you something else with fonts right now. And in a second, I'll show you that when you want to draw fonts, you want to make sure you're in a context or in a, you can set your raster position to the 2D position on the screen you want your fonts in.
If you're drawing a complicated 3D screen, you may not be able to. It may be your X0, Y0, maybe back face of this polygon over here until your font comes out backwards and over here or whatever. So that's not, you want to be careful about that because that won't work very well. So, we'll look at, remind me about that, and I'll tell you how to set up an orthographic context in a minute. And so, I think I'll go back here. How are we doing? We've got about 20 minutes, I think.
And let's go back to... I don't believe I... No one told me? I did? I did? Okay, awesome. Thank you. Save me. Okay, so basic font handling is good. Physical simulation. Really simple physical simulation is... I'm going to talk about some examples of it, of how to do physical simulation, or examples of doing physical simulation in OpenGL.
You can do something from very simple, just to get the idea of what's going on, just to get, hey, you know, sometimes visualization of something is a lot easier than describing it or writing a text-based program. So with a little bit of hooks in there, you have your physics simulator working out at a university, and you want to simulate some effect, and you want to just map it out.
It's pretty simple to just toss it into OpenGL. A couple of recommendations here. If you don't want to do any kind of Mac handling, you want to make the thing run on Unix, you want to make it run on Windows, you want to make it run on the Macintosh, what you can do is you can use GLUT. GLUT is GLUT Utility Toolkit. It's a cross-platform, on the code level, based kind of... UI handler. So it gives you the keyboard events, it gives you the Windows and that kind of stuff. You write the same code.
I wouldn't recommend it for shipping code, but I would recommend it for if you're doing any kind of a profile, any kind of research areas, any kind of... you want to look at some algorithms, you want to do a quick one-off application for in-house to demonstrate something, GLUT works like a champ. So what am I going to show? I'm going to show you some fun examples.
So you've now pretty much finished with my spinning square. I appreciate you kind of hanging in there for the spinning square demos. Now we'll look at some more interesting things. Thank you very much. Skyrocket. Skyrocket is a program that was written by Terry Welsh. And the reason I bring this up is because I ported this from Windows to Macintosh. It took me about two hours to do this.
It's a screen saver. It took me about two hours to do the port. And it runs very well. It's not... it wasn't a big deal. It's OpenGL and based on a standard template library. It was really easy. Came over, no problem. No changes in the GL code at all.
Didn't change anything. I mean, not a line. I did a little bit of AGL setup and that was about it. And so then we go to the GL code, leave it the same. And okay, so I'm going to run this in 1024, 768 and I think it will all fit on the screen. It should work just fine. And I ask you to bear with me for a minute because it takes a little while to get started to see it.
And I'm not sure how well you can see that. You probably can. And the camera will take maybe a few minutes to rotate. But what he has done here is basically set up a texture for the clouds. He set up a texture for the sky, for the stars. And then he's going to rotate the stars. He also has some terrain below you.
You can't really see that real well, can you? That's not bad. We'll wait for it to rotate and then I'll show you. And then you'll actually see it's 3D. So it's your standard fireworks kind of demo. If you notice that the fireworks light the smoke up with the other fireworks.
So he has a lot of lighting effects going on. The clouds get lit up. And of course, it wouldn't... since I don't have mouse keys to rotate it, it wouldn't start... want to start rotating for me because it kind of randomly picks the times to rotate. So we'll let it... we'll sit here and be patient.
Let it fool around anymore. So what else can I tell you about it? Each... the way this works, if you notice, each piece of smoke is a texture and he scales the texture and colors the polygon color. So he takes a texture of the cloud with some alpha values.
And then what he does, he puts the polygon in the right place and he turns on... he sets the polygon color to let the... now here we go. If you notice, it really is full 3D application rotating at that little matrix like that. So you can see the effect of the camera rotating. And what's really nice about this application is... I mean, it just... it looks good.
It gives some power on OpenGL and it's pretty small, pretty compact. One guy did it. I mean, if you want to simulate something, if you want to do some simulation, you can see that you get kind of... you can do some really nice things in OpenGL. You get a nice moon glow and some lighting effects there.
And So you can see that he did a real nice job with this. And what's nice about some OpenGL stuff is that I can turn it off full screen, go to 400 by 300. Remember, I haven't done anything to the OpenGL itself. All I'm doing is changing it to a window, calling the same thing I've talked about before, the GL viewport, making sure the window is set up the right way. And there I am. I got it in a window. Same stuff, running exactly the same, no changes, same code, works like a champ.
So OpenGL works in a window. I could have something that says, hey, this is my rocket simulation. I'm going to show you. This is the fireworks effect that my fireworks company is going to set up for your 4th of July celebration. Here's an example of what it's going to look like, and have some cost or some presentation on it, using OpenGL to demo it. So that could be a use for someone who wanted to use OpenGL without having to do too much extra work.
I actually want to whiz through these ones a little bit quickly so I can get to the end. New Wave, this is an example that comes with This is a really cool feature. It comes with the GLUT, the code comes with this. You have source code for that with our SDK and you can find it and this is something that's really easy to do. That's kind of nice. That's kind of cool. What's nice about it actually is that you can do this.
And let's do something else here. Let's set size to... Let's use the environment mapping. The reason I say this is a physical simulation other than just a cool piece of eye candy is because... Let's display the wireframe. The wireframe is actually just a set of springs. And they used to use a simple physical simulation of springs to set this up and to do the propagating waves on it. So this is another physical simulation. That's kind of cool. You could talk about it.
You can do it. But then you put it in OpenGL, throw the environment mapping on, and you've got something that really catches your eye and says, "Hey, that's something great." You notice it runs in a window just fine. It's not like you have to go full screen to demo this stuff.
and the last one is actually a real simple demo uh... and I apologize for this one because on this machine it kind of runs a little fast uh... so this is a demonstration of Lorenz attractors and uh... you can see that that's So it's showing, it's following the little red ball around in the little Lorenzo tractor. So that's another idea of someone using OpenGL, mapping it out to 3D and implementing it in a very simple way. So just to show that you can do simple stuff there too.
Okay, let's move on. And you're awesome, because he's going to have to switch it over by the time I get here. I'm just going to beat him. Oh, damn. I couldn't beat him. Orthographic projections. We talked about that a little bit. Orthographic projection is basically a parallel projection.
It's one-to-one for the screen and the context. And one good use for this is a sprite engine. I wanted to have a sprite engine example here, honestly, but I did not have the time to put it together with all the other things we're doing. So what I have is I have my font. My font's at the end.
I'll show you how that works with the font display, how it actually is an orthographic projection. And you can set them up. And it's really fairly simple. It's just a little three lines of code. But you had to hold on for that, so I'll move on into a minute.
Texture Mapping. In my last 10 minutes here, I'm going to tell you all how to do texture mapping on the Macintosh. Because it seems that first people go, "Ooh, I don't know how to do texture mapping. I don't know how to put cool stuff on the screen. All I know how to do is actually draw boxes like I did." That's easy. Texture mapping is hard. Texture mapping is easy.
Basically, you've got to read the image. You've got to swizzle the pixels. What I mean by swizzle the pixels, that's an official industry term now, swizzling pixels. Swizzling pixels is you've got an ARGB, so it's 8 bits alpha, 8 bits red, 8 bits green, 8 bits blue in the Macintosh 32-bit off screen. That's what you get. That's when you read it in using QuickTime, read it in using your Read Picture or whatever. That's what you're going to have.
OpenGL doesn't like that. It doesn't like taking that format in and doesn't like handling that format. So what you need to do with that is you need to swizzle the pixels. So what you're going to do is read it into a different buffer. And you're going to take the RGB values out of it and put it in the other buffer, set it up as just 3 byte values or tuples of RGB values and use that for your texture, send that into OpenGL. That's swizzling pixels. You have to do that. But we've just announced that we're going to have packed pixel format support in our next version of GL, which will be seeded to developers very shortly.
And that will allow you to take standard Macintosh pixel formats, like something you get in an off screen, and apply it directly to a texture. So you can get that. You can do 1555 16 bit. If you want to fill the alpha channel in, you can. Or you can blank the alpha channel.
Or you can fill it in with ones and you can send it directly into a texture. And there you go, your texture mapping without having to do any pixel swizzling. Then we'll talk about handling GL textures, just the basics of how to texture, and we'll combine that with QuickTime.
Magic. Let me show you the texture mapping demo. I just want to make sure I don't run over and have time for the last demo. That's the only one I'm really, really proud of. So this is just my-- I stole the Maya face that the guys worked on and basically did texture mapping demo.
Can you see that real well, or is that really hard to see? I think it's probably hard to see. and I can see it. You can see the reflection better than anything else. But basically, you have the texture map floor, you have a reflection in the cube, and the reflection in the cube is really simple. It's just the same thing.
Did anyone notice something's wrong with that? Exactly. The reflection is totally wrong. The reflection should be, in this case, should be going, as it dives down the top, the point should come up in the bottom. So that's all screwed up. But that's not the point of this, so I can do that.
And by the way, I mean, this is a big thing for anyone who's using OpenGL. There's so many resources on the web. It's one of the-- the people who work with OpenGL love to write about it, I guess, too, or have seen their names in print. Because you go to www.opengl.org and you follow the links there, and there is tons of stuff about doing reflections correctly, what mistakes not to make. All that stuff is out on the web. It's great stuff. So what was I going to show you? Oh, texture mapping. Okay, load texture res. So what we're going to do is we're going to again go to the bigger size so you can actually see this.
I'm not going to worry about the setup here, but I'm going to basically do getPictureResID. We all know that. If you actually get the picture, getPicInfo. The reason I want to get the PicInfo is because I want to get the size of the picture. I want to create a new GWorld the size of that picture if it's 32 bits. I want to actually lock the pixels. Remember to check to make sure that the lock doesn't fail, because if it fails, you're in trouble.
That's something you should always be doing. The reason I say that actually is partly because that's something you should do and partly because as we move forward to 10 and there are actual locks and unlocks that matter, locking pixels, if it doesn't let you lock it, there may be a reason you can't draw with it. There really is something you should pay attention to. I draw a picture into the off-screen GWorld. I set the GWorld. Now here's the magic, really simple magic here.
Width, height, stride, base address. Width is the width of the picture. Height is the height of the picture. Stride is the actual row-to-row distance in bytes. which is the row bytes, the base address is the first address of the buffer. I create a new pointer, so I create a buffer for my texture that's the size of the width and height, and three for one byte for each RGB component.
And then really simple, what I do is, in this section, I pull out I pull out the... This is offset by 4 because it's ARGB, so it's 4 bytes for each component. This is by 3, so it's RGB, 3 bytes, and I pull out the 0 and I pull out the red, the green, the blue, in order, and then the simple call for OpenGL... Now I have a buffer that's width by height that's set up for the texture and I call glTextImage2D, Chuckster2D, 3 components, width, height, RGB, unsigned bytes for the components, and the pointer to its buffer. Dispose pointer, dispose usual, release resource. Boom. Done. That's all you need to do to texture. OK. But you have to get it to the screen. I think I have that somewhere.
Okay. Little trickery here that I'm not going to show you today but you can look up, this bind texture thing. I call it bind texture because I only want to load the texture once. So actually I load the texture right here, that call. I call bind texture to say I want to pay attention to the marble, lightning or the face or the mask.
So I say pay attention to this, set these parameters for the texture, load the texture. And then every time I call bind texture I'm saying I want to use that texture. So I go back down here and I say bind texture. So I want to use the mask. I then go to draw cube and draw cube says here's a texture coordinate, 0, 0, 0. Here's a vertex coordinate which is a position for that face vertex.
Texture coordinate 0, 1, 1, 1, 0. If you notice I'm just walking around the polygon. I'm just boom, boom, boom all four corners. Textures are going to be mapped 0 to 1. So what happens when you map a 0 to 1, it makes it really easy for you to map a full texture to a polygon because you just map the corners to the right places 0 to 1 and it's simple. It's gone. Done. Perfect. Awesome.
We're done. We're not done, but we're done with that. So texture mapping is not hard. I think that's all I need to show you on that one. So we saw that. And then I have-- Swizzling texture updates. Oh, texture updates. Oh, I'll show you this one. This is actually the demo I'm kind of actually thinking is actually reasonable.
Do you want to see what Steve and those guys showed, or do you want to see a different trailer? This is the large size of the dinosaur trailer. Basically what it is, is QuickTime playing it into an off-screen and then playing it, and you should see it in a second because it's actually dark to start with. Oh, I'm sorry, one thing, that is really dark. Can you turn down, is there a way to turn down some of these lights? Yeah, turn them off. They don't need to see me.
I'm ugly. Thank you, I appreciate it. Okay. OpenGL Texture, and you can't see the cool part is, which I'm, you can't really see, is this is running at 99, 89, this is running at about 100 frames per second. So it's texture mapping from a QuickTime movie onto the texture at 100 frames per second.
So it's blowing away the QuickTime movie, it easily can update at the rate the QuickTime movie is. And one thing I have here also is, I have a fog set up, so as it fades out it actually is fading. That's why you get the fade as it goes away. So it actually, that's not a mistake, I set it up on purpose with a black fog to go out, fade in, fade out. And there actually is sound playing with it and all that stuff, we don't have that hooked up. So, but that's not a big deal.
So this is a real simple, it's playing the trailer, and if I click it ends. And if you notice that all I can do is just pick, I'll play the same one they always play. You can leave the lights down still. It plays any movie. No big deal, no tricks, no. No, nothing.
So, can we do that at home, right? Is that easy? Actually, if someone wants to bring the lights up or leave them down, I don't care, and I have a couple minutes here to actually show you how I did it. The texturing routine is almost exactly the same.
I basically use some of the utilities that we have out there. I sat around for about a couple hours and figured out, hey, this is not that hard to do. And the key here is, I have a movie, and let me show you the section that I want to show you.
This is a very simple task fog setup. By the way, this is all that was required to set any fog. Start, an end, linear fog, and a fog color. Real simple. I enable textures at the top. I set a clear color, which is really pretty simple. I empty my texture buffer, which is pretty simple. Here's the quick time to texture conversion.
Basically what I'm doing is I have a texture size and I want to set offsets into subsample the movie. I set these up ahead of time right here. I'm going to put the code on the web so people can really look at exactly how I do it. The key is to find all your offsets into an array so I can do an array offset into the actual texture.
When I come down to make texture, If you notice, basically I'm taking the offsets into, I get my Pixmap, which is the Pixmap for the movie, base address, I got that, I set up my array for my texture, and I work through this by offsetting from my Gmovie, which is the texture, into my, using my base address for the 4, the RGB, and I offset correctly into the array. The array is already offset to avoid the alpha value. It's a little trickery there, but not too complicated. I mean, that's all the code that does to handle the texture.
If you notice, in this code, that's all the stuff to do the window moving, and all I call is Make Texture, and then the key other line is glTexSubImage2D. Once I call texture once, I can call glTexSubImage2D, doesn't recreate the texture, all it's going to do is update the contents of the texture on the card, so it keeps filling stuff more to the card, a little faster than the normal texture.
And then I can make the glTexture text image 2D. And I really don't want to go through the text image 2D, I mean, I guess I can run it. I'll do that to let people know that I don't think there's going to be much of a degradation at all in this, but that's another way to do it if you have textures that are changing all the time.
And I'll tell you what the frame rate is. 87 instead of 100. So I lost 10 frames per second by doing it this way. So, and if anyone's interested, they really want to see, I know you can't see the frame rate counter up there. Absolutely, you're welcome to come up and look at it. And again, the code will be up so you can look at the code and figure it out. So, if we can switch back to this one.
Roadmap. Okay, what are we doing next with QuickTime? In this room, right after this, is the advanced optimization. John Stauffer has graciously given his time to put some cool demos together, talked about optimization with QuickTime, or sorry, QuickTime OpenGL, and talked about how to do that. He's going to talk about how to do that. And then we have the feedback form right after lunch.
Please, if you have anything to say about OpenGL, about 3D direction on Apple, come to the feedback form. We'd love to hear any of your opinions, comments, goods, others, whatever you have. We'll take them down and we'll take them in and try and get you the best 3D implementation out there anywhere.