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 may have transcription errors.
Good morning. I'm Jeff 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 the things that get wrong later.
But John Stauffer is the engineering manager for OpenGL Group over here. And also we have a number of guests from ATI who do the OpenGL drivers for us. Chris Bentley sitting on the side, Brian Raderman, and Jeremy Sammel working on the OS X work with ATI. So we appreciate them being here. And 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, it's 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 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. Whether it may be a depth or it 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 things like that. 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, like, Quartz may 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, 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. There's not-- you don't have to, you know, go take a linear algebra class and go find some dark corner of some library that has some dusty old volume about how to put-- 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 the 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 can 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 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 to 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 on update events. Real simple. Works right in an event loop. Works great for good stuff. Sub windows, 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 had something there. It does not know about compositing to your background.
So you have to consider that, that the area you're updating OpenGL is going to take over basically that area that you've set up. Talk about some basic font handling. Then we'll talk about physical simulation. I have a couple demos and talk about just using OpenGL for physical simulation. Nothing 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 you talking about? Real simple is that for an orthographic projection might be something that puts the text on the screen, while behind that you have a perspective view of a walkthrough. 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 would allow you to nicely place your text on the screen. It wouldn't do any manipulations 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 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 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 3DFX render, you can ask for a 3DFX 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 noncompliance seen 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 precompiled 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 render. render, thus it'll switch to it. So 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, Rage 2 shipped on the original Rev-A IMAX is a non-OpenGL compliant renderer.
So in that case, this will select that. Star Wars Racer wanted to run on IMAX Rev-As, 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 renderer in that case. Thank you. OpenGL hardware compliant render, same as the above string basically, but I got rid of the HL all renders, hardware accelerators, and added HL accelerated.
Here's a key that I talk 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 render, it's going to fail. Fail on multiple screen displays. 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 3DFX 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, moves across. When it's between screens, you get the software renderer. When it's back on the 3DFX display, it'll get 3DFX renderer. And one interesting thing is you can easily check if you use GL get string, check for the manufacturer in the version string, you'll get either the ATI, the Apple software, or the 3D effects. So you can identify what render you're actually on. If I use this string right here with the AGL 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. I 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 wanted ATI, again, AGL renderer ID, add an AGL renderer ATI ID. And if I wanted to get an OpenGL compliant AGL renderer, I would have to get an OpenGL 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. 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 context, multiple drawables, and considerations such as AGL upfade 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. The 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. Okay, 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.
Full screen, has the whole screen, doesn't care about anything else, doesn't care about updates or not updates, it just draws. Works great and you'll see the spinning square a lot. So I just wanted to give you a little preview of that one. And let me give you a little update on my spinning square demos.
None of the demos you'll see except for maybe the last two are really exciting as far as content. I didn't spend a lot of time putting textures and fun little cool stuff in it. Keynotes do that, I don't. I'm not an artist. The point here was just to get across the coding, the actual techniques involved, rather than the content. So I hope you can bear with my spinning squares for a while. So where are we? We're talking about multiple context... Oh, let's talk about multiple pixel formats. Multiple pixel formats are pretty... 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 format 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 context, not multiple pixel formats. OK, so now let's talk about multiple context.
So multiple contacts, 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 contacts 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.
this works. Okay, what we're going to look at here is setting up a couple contexts. I have two rectangles at the top here. They're flat. both in this case are different size 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 AGL accelerate and I should get an accelerated context with that. Then I build the gel from 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 has set up for flat shading. If I take it out, then I could-- 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.
real simple. So it's just--this would be used for cases where you have things that--for example, you have four windows that you may want to do in a CAD program and one to be smooth shaded and the rest to be line drawings. So if you want it to be line drawings, you can just put the polygon mode into line and we'll assume from my simple demo that the fixed window is your projection of your hallway or your building your building. The other one is your actual line view of it that you're working with. So this one would be like your working copy of it and this one over here would be the one that shows the actual what it's going to look like at the end, the rendered kind of image. So that's the kind of thing you would use actually multiple context for.
Okay, so what else do I got? Okay, so I did flat line drawing that one, so we're done there. That's moving on. Thank you. Let's look at multiple drawables. So in this case, multiple drawables is the same context with multiple windows. You may 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-- So, scroll down here and we will get to the setup. So-- 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 actually since the draw will set up on the on the back window first the front window thinks it's basically the same size of 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, If you notice, this is the do 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. Thank you. 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 into 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. So instead of really smishing your viewing up, you're actually just taking a piece of it out. And one more thing is to, well, 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 contacts. 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 changed the context variables, when we changed 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 and you want to have multiple views, and you want to have multiple views that have different kind of OpenGL large 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 using two different drawables, same context. Let's move on a little bit. Thank you. So it moves in multiple contacts 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? That's pretty important. So if you don't, the window won't change. Nothing will happen. The window will-- I mean, it'll draw the same size, thinking that it's drawable's right size. You need to call update context. 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 context 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 here-- up on the website. There'll all 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 two, this window first. This is a window I actually can drag. I call I'll update context here. Oops, sorry. I'll update context and I call GLSET viewport.
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. So, I'm going to go ahead and AGL 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's 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.
Okay, 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. If you're 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 look at-- 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 rack. And the buffer rack's 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 racks 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 Rack. 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 rack, 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 will 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 will just move around on your screen. I may not even show you that. But let's quit this, and I'll show you a very simple setup here to do here. This is the Buffer Rec code. It will bring it bigger for you. Thank you.
okay i might do update again what i do is these are i have to section of the drawing, one here and one here. And what-- 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. And 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-- 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 is 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, and 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 the swap is just calling it on the small buffer rack. And we can also see that. Let me show you another case of that. So 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 didn't-- doesn't update that top part. Buffer Rec resets it to set that area of draws. It's just, you know, constrains the drawing but doesn't draw-- I mean, it doesn't say, "Oh, erase the background." OpenGL works the same. It's very kind of linear thinking so if it-- 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. So, you notice on almost all of those, you may have noticed on the screen that you saw fonts on there, and how do you do fonts. Because fonts are a great thing to put out, because you can just put text on the screen, works pretty well, and, you know, you can do that.
is something that you definitely want to probably do and on it, and if you don't have a way to do that, then, So we talked about OpenGL buffer 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 the 3D area, that's fine. Just set the buffer record whatever you want. 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. and 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, eh, 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 GL flush and GL finish 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 can even do a clipping region and clip out the area that you 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 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 way 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. Because 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 Monaco, whatever I call, git font fnum. A face, a size. So face may be normal. Size would be 9.0256.
That's 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 0. 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 1. While you have letters in your out string, you want to use call list, font list plus the base and increment I. And there's other ways to do this with for loops and some other loops. But basically what that does is it's going to walk through your list and the list basically says, it's a display list in Geo, it 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, what your states are. Works great. Simple. Basically, you're kind of printf for GL in that way. Or I'll say printf. You can use printf 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 else with fonts right now. And 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 and so 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, 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 a, 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 on 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 GL 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 wanna look at some algorithms, you wanna do a quick one-off application for in-house to demonstrate something, Glut works like a champ. So, what am I gonna show? I'm gonna 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. So 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. 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's done here is basically set up a texture for the clouds, he's set up a texture for the sky, for 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 of the other fireworks. So he has a lot of lighting effects going on. The clouds get lit up. And of course, since I don't have mouse keys to rotate it, it wouldn't want to start rotating for me because it kind of randomly picks the times to rotate. So we'll sit there and be patient and let it fool around anymore. So what else can I tell you about it? 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 a full 3D application, rotating at that little matrix-like 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, so if you want to simulate something, If you want to show some simulation, you can see that you can do some really nice things in OpenGL. You get a nice moon glow and some lighting effects there. Yeah. 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. Hey, 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, you know, this is the fireworks effect that my fireworks company is going to set up for your Fourth 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-- 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 do 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 an OpenGL, throw the environment mapping on, and you 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 so this is a demonstration of lorenzo tractors and uh... you can see that that's So it's showing-- it's following little red ball around in the little Lorenzo tractor. So that's another idea of someone using OpenGL mapping and out to 3D and implementing 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. 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 fonts at the end, I'll show you how that works with a 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've got to hold on for that, because I'll move on into a minute. Thank you.
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. It 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 three 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 do 1555 16-bit. If you want to fill the alpha channel in, you can, 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 pixels whistling. counseling. 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.
Eh, you kind of 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. Who noticed? 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 on 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. This is 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.
Oh good, I have a clock written on my screen here. Someone yell if I get to the 55 mark without finishing. So I'm not going to worry about the setup here, but I'm going to basically do get picture res ID. We all know that. If you actually get the picture, get pic info. The reason I want to get the pic info is because I want to get the size of the picture. I want to create a new G world the size of that picture that'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. So that's something you should always be doing. And 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. So there really is something you should pay attention to. I draw a picture into the off-screen G world. I set the G world. 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 3 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, see this is offset by four because it's ARGB, so it's four bytes for each component. This is by three, so it's RGB, three bytes, and I pull out the zero 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 glTextImage 2D, Chalkster 2D, three 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. Okay, but you have to get it to the screen. I think I have that somewhere. Okay. A little trickery here that I'm not going to show you today, but you can look up, this bind texture thing. I call 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, zero to one, 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? Do you want to see a different trailer?
This is a large size of the dinosaur trailer. Basically, what it is, is QuickTime playing 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 I can't-- God, that is really dark. Yeah, can you turn down-- is there a way to turn down some of these lights? Yeah, turn them off. I don't-- 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 you can't really see, is this is running at 99, 89. This is running to 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 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. But that's not a big deal. So this is real simple. It's playing the trailer. And if I click, it ends. And if you notice, what 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 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, the movie and let me show you the section that I want to show you.
fog setup. Oh, by the way, this is all that was required to set any fog. Start, an end, linear fog, and a fog color. Real simple. But 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. And I set these up ahead of time right here. And I'm going to put the code on the web so people can really look at exactly how I do it. But the key is to find all your offsets into an array so I can do an array offset into the actual texture.
So 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. 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. All I call is make texture, and then the key other line is glTexSubImage2D. Once I call texture once, I can call glTexSubImage2D.
It 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 and then she'll text her text image 2D. And I really don't want to go through 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, you know, if anyone's interested, they really want to see that-- 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, and I'll-- no, no, fine, got it.
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, talk 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.