239. WoT – Building a tank
So I’ve started working on World of Tanks.
I’ve built a tank model, as shown in this video.
It’s all built in Codea, even the textures for camouflage and tracks and I thought I’d share a couple of techniques I used.
Tank design
I could find a tank model on the internet, but they tend to have too many vertices (eg 20,000 versus 350 in my final model), and they don’t separate the different parts of the tank so you can rotate them separately. So I decided to build my own, very cheap (ie few vertices) tank, completely in Codea.
I want a tank with a turret that rotates left and right, and a barrel that rotates up and down. I’d also like tracks and wheels that look fairly realistic – but I don’t have to be too fussy, because most of the time, I’ll be looking at tanks fairly far away, and not close up.
I decided to go with 4 separate meshes, for the body, tracks, turret and barrel, ie one for the body and then one for each moving part.
Template
I looked for some tank images on the internet, and settled on a tiger tank, because it is fairly rectangular but still looks nice. (Rectangular is good because our meshes are built from rectangles, and curves are difficult). I copied and pasted it into a spreadsheet so I could get a grid pattern behind it like this, to help with measuring.
Then I had to measure the positions and sizes of all the different parts, and start creating vertices. And eventually, I got this. Clearly, I suck at graphic design….
I realised that if I dropped the height of the tank, it would look better, so I removed the chunk in the middle, just below the turret, and I ended up with the design below, which I think looks good.
Vertices
It took me several hours to set up all the vertices, doing one part at a time. Many of them have to be mirrored, ie I need them both on the left and right (or front and back) of the tank, and where that happens, I need to be careful that the vertices for each triangle are always in anti clockwise order, so if I have a triangle with points a,b,c, and the anti clockwise order is a,b,c when the triangle is on the front of the tank, then the matching triangle on the back of the tank, facing in the opposite direction, needs to be have the order a,c,b.
Why anti clockwise? OpenGL uses the ordering to decide whether a triangle faces outward or inward. This isn’t important until you start using lighting, because until you do, triangles look the same from both sides, but once you have lighting, one side is light and the other dark. So you need to make sure all the triangles facing outwards have their vertices in anti clockwise order.
Tracks
I wanted the tracks and wheels to look realistic, and to move if possible. The problem with the tracks is that they have all these treads that stick out from the track. If you model these with narrow 3D blocks, it will take hundreds if not thousands of vertices, for something you will not even see unless you are close.
I thought about this for a long time, and finally decided to fake most of it. I created a mesh for the track with just four 2D rectangles on each side, as drawn below.
I didn’t bother doing the top of the track because that is hidden by the side of the tank, and I didn’t bother making a smooth curve at the front and back, because most of the time, you are too far away to see details. (If you’re wondering why I bothered to do the bottom track, which is almost invisible normally, it’s because if a tank comes over a hill toward you, you can see its underside).
Those tracks are smooth, but tank tracks have treads that stick out, and they look great as they move. I wondered how to do this, and eventually decided to use a scrolling image. As part of the setting up, I got Codea to draw a little image in memory which was wide, and had a lot of vertical lines for the treads. Then I set this image as the texture for the track, and set the texture positions for each of the track rectangles.
This just leaves me with the problem of how to animate the treads so they look as though they are moving. This is where shaders come to the rescue, because they let you mess with vertices and texture positions very efficiently. What I did was to adjust all the texture positions by a small amount each frame, so the track image seemed to move along. I could do this without a shader, by adjusting all the texture positions individually, but this is very inefficient, and also, what happens when you get to the end of the image and need to start over? A shader can do it much faster, and also easily deal with the end of image problem by simply going back to the start again. (I’ve written about tiling shaders here, and if you’re interested, I’ve written an ebook on shaders which you’ll find in a link at the top of the index page to this blog).
That just left me with the wheels. First I created a flat mesh the same size as the gap inside the track, then drew an image in memory with some coloured circles to look like wheels, and made this the texture for the mesh. So the wheels are just a picture.
Camouflage – noise
The only tricky texture was the camouflage green I’ve used on the tank. Again, I created an image for this, using the noise function, and this code.
function MakeBodyTexture() local w,h=200,200 local img=image(w,h) local c1=color(122, 184, 121, 255) local c2=color(70, 122, 73, 255) for i=1,w do for j=1,h do local m=noise(i/w*5,j/h*5) img:set(i,j,c1:mix(c2,m)) end end return img end
We choose two green colours to mix, and then loop through all the pixels in the image, using the noise function to set the proportions between the two colours. The two parameters for the noise function are fractions of the width and height, and I divided by 5 after playing around to get the right sort of look.
Noise is a great way of creating patterns. I used it for the sea in my World of Warships project.
Lighting and barrels
In most 3D work, you need lighting, because unless you have a very distinctive pattern, it can be hard to see the edges of 3D objects. You will have noticed that in bright sunlight, it can be hard to make out objects, whereas late in the day, all the shadows make this much easier. So lighting simply makes one side brighter than the other.
I won’t explain how it works, because that takes too long, and I’ve done this in several posts and an ebook. I just want to focus on one neat lighting trick.
A problem with using triangles for meshes is that they tend to look blocky, and curves can be hard to draw. But there is one neat trick, which I’ve used for the barrel and the side of the turret, which both look quite round, although they aren’t. In fact, my barrel is made up of 8 long thin rectangles, arranged in a circle.
The picture shows the the barrel mesh on the left, clearly showing the different rectangles. But the image on the right shows a rounded barrel using exactly the same mesh – how?
The answer is that lighting depends on how each point is angled – the closer it is to the light direction, the brighter it is, and vice versa. Each triangle is a flat face, and every point on that triangle points in the same direction, so normally, lighting will make each triangle (and each rectangle) equally bright. That’s what’s happening in the left hand image.
However, you can use a shader to calculate the lighting for not just each triangle, but for each point inside each triangle. And if you tell the shader that the point is facing outward from the centre of the barrel, then every point will have a different angle, and the lighting will be different for each point, giving a smooth effect.
There is one limitation to this technique – you can’t change the edges of the mesh – and if you look closely at the end of the barrel on the right, you can still see the outline of the rectangles.
Anyway, now I have a model.
And many challenges ahead.
Wow that tank is awesome! I just got Codea a few days ago! Maybe you could make some tutorials or something for me!
This blog is packed with tutorials, just look around.
The index page also has links to ebooks…
Oh, cool! I’ll check them out!