We’ve covered drawing basic meshes, and we’ve covered the all-important transformation functions. We’ve also covered lighting. Now that we have all these basics down, it’s time to start on some of the nifty tricks that you can pull using what we’ve learned.
Billboards and Texturing
Up until now, we’ve been drawing pretty plain meshes. Sure, you can add some visual diversity by changing the draw colour of objects, and you can throw a little more dynamism in by using lighting, but nothing adds detail and visual pizzazz to your 3D world like textures.
To show off how texturing works, and to show you a bit of applied transformation, we’ll be putting together a billboard in Game Maker. Simply put, a billboard is a textured plane that is always rotated to face the camera. These were used a lot back in the days of software 3D engines (and are still used today!) as a way of drawing objects inexpensively. They’re especially useful when the object you’re drawing looks the same from all angles, like an energy ball or a plume of smoke.
The energy balls in the above screenshots are done using billboards. Observe how they keep facing the camera regardless of where it’s pointing.
In this screenshot, the smoke trail particles are done using billboards. The soldier firing them is also a textured plane that is rotated to face the camera.
Firstly, I’m going to assume that we have a fully-functional camera that the player can pitch and rotate. The actual implementation of this isn’t something that I’ll go into, since it’s a programming exercise rather than an explicit 3D function. If you have a static camera at a fixed position, you can easily insert constant values instead of these variables. For now, however, let’s assume that the camera has variables for its direction (side-to-side) and pitch (up and down) in degrees. [Yaw! Roll, pitch and yaw! Use the proper terms; it makes you sound fancy. 😛 – Ed]
We’ll start by importing a texture that we can place on the billboard. Textures for 3D objects are imported into GM as standard sprites or backgrounds, just as you’d normally do for 2D games. For now, create or import any sprite, and call it BillboardTex.
Next, we need to create our Billboard object. Put the following in its Create event:
myTex=sprite_get_texture(BillboardTex,0); z=32;
This will give us a handle to the texture to use in our 3D draw functions. The first argument specifies which sprite we’re pulling the texture from, and the second argument tells GM from which frame in the sprite to pull the texture from. As you can imagine, this allows you to have animated textures (just use -1 as your frame argument, just like in your draw_sprite() command). Note that you can also use the background_get_texture() function to pull textures from background images.
We’ll also add a variable called z, which will specify the height that we want the billboard to be drawn at. It isn’t essential, but it helps to have a specific variable defined to control this. You can either leave it as-is, or you can add or subtract from the variable as your needs require.
Next, slap the following in its Draw event:
d3d_transform_set_identity(); d3d_set_culling(false); d3d_transform_add_rotation_y(Camera.direction); d3d_transform_add_rotation_z(Camera.Pitch); d3d_transform_add_translation(x,y,z); d3d_draw_wall(-16,-16,0,16,16,0,myTex,1,1); d3d_set_culling(true); d3d_transform_set_identity();
Most of the above code should look familiar now that you know about transfomations. What I’d like to draw your attention to is the d3d_draw_wall() command. In this case, I’ve drawn it to be 32 units wide and tall, but it can be any size you want. More importantly, you’ll see that the last three arguments have been populated with the myTex handle (the texture we defined earlier) and the texture hrepeat and vrepeat arguments have been set to 1. As I mentioned last time, these two arguments control how many times the texture is tiled across the mesh. Try playing around with different values and you’ll soon see how it works. All mesh functions in GM take these last three arguments, allowing you to specify textures for all of them.
And that’s all there is to it! The textured plane will orient itself to match the camera’s direction and pitch, meaning that it looks the same from all angles.
Heads-up-display (HUD)
So, we have a fantastic 3D world, brimming with lit, textured meshes, awesome billboarded special effects, and (hopefully) great gameplay. But how to convey information to the player? As novel as it would be to incorporate in-world health indicators and target reticles a-la Dead Space, sometimes you need to be able to slap information up on the screen in 2D as well.
Let’s say you want to place a targeting reticle in the centre of the screen to help with aiming, and an indicator in the corner of the screen that shows how many frames per second your game is running at. You would place the following code in the HUD object’s Draw event:
//disable lighting to prevent HUD from being drawn in black d3d_set_lighting(false); //disable hidden surface removal, to stop the HUD from being obscured by 3D geometry d3d_set_hidden(false); //Set ortho plane d3d_set_projection_ortho(0,0,view_wport,view_hport,0); //Draw FPS text in top left corner of screen draw_text(0,0,string(fps)); //Draw reticle sprite in center of screen draw_sprite(TargetReticle,-1,window_get_width()/2,window_get_height()/2) //reenable hidden surface removal d3d_set_hidden(true); //Reenable lighting d3d_set_lighting(true);
Firstly, we need to disable lighting. If we don’t, GM will attempt to light the things we draw to the HUD, which will result in solid black icons and text. Secondly, we disable hidden surface removal. This prevents our HUD from being hidden by 3D objects in the world, as in these screenshots:
Next, we tell GM that we’re drawing to an orthographically projected (2D) plane, and set its x and y position (in screen coordinates, not room coordinates!) and its width and height. For width and height, I’ve set it to fill the screen, running from 0,0 to the edges of the viewport. Finally, you can specify the rotation of the projection plane. With our orthographic plane defined, we then use the same 2D drawing functions that we always do to place text and the reticle sprite on the plane. Finally, we re-enable lighting.
Warning! Remember how the Camera object must always be drawn first? Well, the HUD object must always be drawn last. If the HUD is drawn before any of your other 3D objects, the 3D objects drawn after the HUD will be rendered orthographically as well. Obviously we don’t want this, unless you’re deliberately planning on putting an orthographic 3D model on the HUD as well.
Conclusion
In this instalment, you’ve learned a practical application of transformation by creating billboards, and learned a bit about texture mapping in the process. We’ve also covered how to build a heads up display to convey information to the player. As always, play around with these concepts to get a feel for them – there’s a lot more that can be done with them.
Next time, we’ll finally learn about building, importing and texturing 3D models in GM, and go through some answers to a few of the more common niggles people experience.
It didn’t work for me, there is no z size to the wall. Am i missing something here?