Monday, May 6, 2013

3D Graphics with Game Maker Part 4

In this, the final part of the series, we’ll cover the long-awaited subject of 3D models, as well as highlighting some of the possible problems you may have as you attempt your own 3D games.

Importing models

Sadly, we’re not talking about shipping beautiful women into the country, but rather the process that you’ll go through to get more elaborate 3D geometry into your games.
By this point, you’ll probably be feeling a bit constrained by the limited number of 3D primitives offered by Game Maker. This is understandable – you can’t very well build your next 3D shooter using nothing but spheres and cubes (although it could be interesting to try). Still, being able to create 3D models to represent game objects is preferable.
If you peruse the GM help, you’ll notice that there are a large range of commands to define models. Sadly, these require you to go about building the model point-by-point, manually inserting vertices, normals and all the other data you need with line after tedious line of GML scripting. As much fun as manually defining models is (not), an enterprising Hungarian named Zoltan Percsich has come to your rescue with a neat little freeware script for GM called Mosaic Light. This script allows you to import 3D model files in .OBJ format, allowing you to build and export them from a more user-friendly 3D modelling program like Blenderand pull them into your game for easy use. As a result, we won’t be going into all those model commands, although you might want to take a look at them on your own later. Rather, we’ll be looking at using Mosaic Light to import and draw a 3D model in our game.
In my case, I’ll be importing the building model that you see here as an example. I whipped it up in Blender, but you can use whatever 3D program suits you:
Blender
Blender
Firstly, I’ll need to export the model from whatever 3D program I’m using. This naturally differs depending on the package, but most should have the option to export to .OBJ format. Ensure that you select the option to export normal data if you want to do lighting.
Also ensure that the origin point of the model is where you want it. Some 3D programs export models with their positions relative to the world origin, meaning that you’ll need to reposition your model before you export. Fail to do this and your model will be off-centre when you draw it in GM.
Secondly, we need to put the Mosaic script in our game. The file that you download from the link above will contain a sample Game Maker file that contains a basic importer and the scripts. You can merge this file with your project, but all you really need is the “createFromObj” script. This is the script we’ll run to pull our models in.
Next, I’ll create an object called Building. In my case, I place the following in its Create Event:
myModel=createFromObj(working_directory+"\Models\Buildings\Bulding.obj",false);
Here, “myModel” is the handle that I’ll use to refer to the model that I import. I’ll use this in any functions that require the model as an argument. The importer script that we’ve inserted, createFromObj, takes the .OBJ model file you specify and translates it into GM’s model format, returning the handle once it’s complete. This script takes two arguments – the path to the model I want to import (as a string), and an argument that tells it whether to flip the face normals on import. Generally we don’t want this, so I’ve set it to false.
Now that the model is imported, we can draw it. We do this through a single command in our Draw function, after we’ve done all our translations as usual:
d3d_transform_set_identity();
d3d_transform_add_translation(x,y,z);
d3d_model_draw(myModel,0,0,0,-1);
d3d_transform_set_identity();
d3d_model_draw() is a standard Game Maker draw command. The first argument tells GM which model to draw – in this case, “myModel”, which I declared in the Create event. The next three arguments are the x, y, and z coordinates where the model must be drawn. Here they’re set to 0, since I’m translating using my transformations. The final argument allows you to specify a texture. In this case, we don’t want one, so we pass -1.
And voila! When we run the game with our building in it, we see it drawn in all its glory!
Building in the game
Building in the game
Let’s go back a bit. You’ll have noticed that when we specified a texture for this model, we didn’t have the usual hrepeat and vrepeat arguments that we’ve been using for the primitives. This is because texturing for models works a little differently. You will need to texture your model externally, in your 3D program, by UV mapping it. The process is, once again, different depending on the package you use, but once the UV mapping is done, all you need to do is export the .OBJ with UV data, and import the texture you mapped into GM as well. For example, I’ve UV mapped the following texture to my buildings using my 3D program, then exported my model in .OBJ with the UV data included:
Someone flattened my building!
Someone flattened my building!
Next, I’ve imported this same texture into GM as a sprite, which I’ve called BuildingTex, and assigned it a handle in the Create event, just like we did in the last instalment:
myTex=sprite_get_texture(BuildingTex,0);
Now when I do my draw code, it looks like this:
d3d_transform_set_identity();
d3d_transform_add_translation(x,y,z);
d3d_model_draw(myModel,0,0,0,myTex);
d3d_transform_set_identity();
And when I run the game, it looks a little something like this:
Textured building
Textured building

Niggles and other details

We’re nearly done with this introduction to 3D graphics in Game Maker, but before we end, it’s important to run through one or two small things that may cause you hassles later on as you continue to build your 3D games.
  1. Transparency
    If you think back to the first tutorial, you’ll recall that we used the draw_set_color() command to change the colour of the primitives we drew. It’s important for you to realise that the other draw_set commands also work in 3D. For instance, if you use the draw_set_alpha() command, you can have 3D objects drawn transparently, like so:
    Something
    There’s something wrong with this picture, though. Note how the buildings still obscure each other, even though you should be able to see them through each other (since they’re transparent and all). The problem here is draw order. You need to ensure that the objects at the back are drawn before the objects at the front. In this next screenshot, I’ve implemented some code that increases the object depth based on how far away it is from the camera. Since larger depths are drawn first, this ensures that faraway objects are drawn before nearby ones. Note that the buildings are now visible through each other, the way you’d expect them to be.
    Rule of thumb – if you have transparent objects and they aren’t drawing the way you want them to, check the draw order.
    Ah, fixed it!
    Ah, fixed it!
  2. Lighting woes
    So you have a fantastic level built, riddled with lights, but when you run the game, things don’t seem to light properly. Maybe the giant plane you’re using for a floor stays black when your walls light up fine, or maybe everything is black. Remember the following:
    • Remember that 3D objects will draw in black by default. You’ll need to specify a draw colour before they’ll become visible.
    • A common complaint is that somebody is using a giant plane as a floor and/or ceiling for a level, but it isn’t lighting according to the point lights that are scattered around the level. The reason is simple. Game Maker uses Vertex Lighting. This means that it calculates lighting based on the individual vertices that make up a 3D object. If you’re using a giant plane as one big floor tile, you only have four vertices at the far corners of that great big plane. Chances are that your point lights aren’t placed anywhere near those corners, so the plane won’t light up, and if it does, the entire floor illuminates. If you do want point lighting on your floor, use smaller planes as tiles (which can get really slow), or implement some form of light-based tile-colouring system. Naturally, a directional light will work just fine, since it has no position or range and illuminates the entire level equally.
  3. Clipping planes and view aspectSometimes the range of the default d3d_set_projection() camera just isn’t good enough. Objects vanish behind a giant invisible plane at the far side of the camera, and you don’t want that. Or perhaps you want a slightly wider view of your rendered scene. Well, you need the extended camera command.
    d3d_set_projection_ext(
        xfrom,yfrom,zfrom,xto,yto,zto,xup,yup,zup,angle,aspect,znear,zfar)
    This command is used in place of your standard d3d_set_projection() command in your camera’s Draw event. You’ll notice that it’s very similar, but that there are a few more arguments tacked on at the end. First is the “angle” argument. This allows you to tweak the camera “lens” to give you a wider or narrower view of the scene. This takes input in degrees. Next is the “aspect” argument. This allows you to specify an aspect ratio (4/3, or 16/9) to allow for widescreen display. Finally, there are the two clipping plane arguments, znear and zfar. znear lets you specify how far away from the camera objects must be before they get drawn (it’s 0 by default), while zfar determines how far away the clipping plane is (that flat plane beyond which nothing is drawn). By tweaking these, you can increase your view distance, but beware! If the distance between them is too great, you’ll see triangular artifacts on models far away, where nearby surfaces “blend” into one another. It doesn’t look good at all, so watch your planes.

Conclusion

This concludes our little sojourn into the world of 3D Game Maker. Hopefully these tutorials have given you a better idea of how to go about drawing 3D graphics in GM, and also provided the tools you need to get going on your own 3D masterpieces. Feel free to drop comments or ask questions – we’re here to help!

    No comments:

    Post a Comment