Skip to content

182. Drawing 3D characters (made easy)

November 8, 2014

I’ve worked on my 3D text code some more, and this is the result – 4 separate demos, any of which you can adapt if you want.

There is also now an easy to use class you can drop into your own projects.

Details (and explanations of the video) in the rest of this post.

1. 3D text on a 2D screen

First of all, I show you you can fake 3D text on a 2D screen by simply drawing the image over and over, moving it slightly each time. This is really simple, and doesn’t need the class I wrote. I won’t explain the code, you can look at it yourself (link at bottom).

2. 3D text on a 3D screen

I mainly provided this demo to show how little code you need to use the ThreeD class.

This to set up some 3D text

txt=ThreeD{text="Hi",size=72,width=10,textColor=color(55,148,117)}

This to draw it on the screen (you still need to translate and rotate to the right place first, of course).

txt:draw()  

 

The ThreeD class

This is a good time to introduce the ThreeD class, which you can use to set up as many pieces of 3D text (or images) as you like. As you can see above, you pass it the information it needs, which is

  • text
  • font = any font you like (optional)
  • size = size of font
  • width = thickness of 3D text in pixels
  • textColor = colour of the text

Note that you are passing a table – see the squiggly brackets I used, ie { }, not ( ). I could also write this as

txt=ThreeD({text="Hi",size=72,width=10,textColor=color(55,148,117)})

ie with round brackets, and a table inside, but Lua says that if you are only passing a table to a function, then you can omit the usual round brackets. So you can do it either way.

The reason I am passing a table with named items is so that you only need to include the items you want, and in any order. If instead I wrote the function the usual way, with a fixed list of items, then you would have to include values in the right sequence, for every parameter, even if you didn’t want to use it. So this is more flexible.

So the bottom line is that to simply create and draw some 3D text takes only two lines of code.

3. Rotating the 3D text

In my last post, I explained how I made 3D text by cheating and simply drawing lots of copies of a 2D image. There was a lot of flicker if the images were side on, so I prevented the rotation from ever becoming sideon, or from going further and being drawn backwards.

However, I’ve altered my code so that when you get close to side on, it temporarily uses more copies of the image and stacks them more tightly. This greatly reduces flicker (unless you make the 3D thickness quite large), so now you can rotate as much as you like in any direction, and it looks good.

4. Multiple 3D objects

In the final demo, I add a second piece of (yellow) text, and a spaceship image. Yes, it can make images 3D as well (sort of).

Creating a 3D image is simpler than text, because less information is required

ship=ThreeD{image=readImage("Space Art:Red Ship"),width=5}

Again, width refers to the thickness of 3D text in pixels.

The transparency problem

There is an OpenGL “feature” I have discussed several times before, most recently here, where if you draw anything with transparent pixels, OpenGL treats them as solid and won’t draw anything behind them. This is a problem where we are using 2D images, because they are rectangular, and almost always have transparent pixels around a picture in the middle.

If, for example, we draw the green text at the back first, then the yellow text and spaceship in front, everything is fine. But if we draw the green text last, then OpenGL would not draw any part of it that was behind the rectangles for the yellow text and spaceship, even where the pixels were transparent and you should be able to see through them. There would just be a blank.

This is a bit of a drawback of using 2D images in a 3D scene.

The way round this is to draw transparent objects in order, from furthest to nearest. So in this case, I should draw the green text first, then the yellow text and spaceship. But if we rotate the whole scene around more than 90 degrees sideways, the green text will be in front of the other objects (and backwards), and we will have the transparency problem again. We need to draw the green text last in this case.

So we need to know which objects are in front before we decide which to draw first. It is actually as simple as figuring out if we have rotated more than 90 degrees sideways, past the sideon position, to where we are drawing the text backwards. And in my last post, I explained a way of figuring out if you have done this, by using modelMatrix().

And I have provided a special function in my ThreeD class, IsReversing(), which is true if we are drawing backwards. So our code needs to look something like this

if txt:IsReversing() then --test any one of the objects
    --draw in reverse - yellow, spaceship, then green
else
    --draw normally - green, yellow, then spaceship
end

IsReversing can not only tell you if you have reversed, but if you are close to doing so. You can give it a parameter, eg 0.1, and it will return true when it gets close (the smaller the parameter value, the closer to sideon that will be).

The code is here, but I need to explain a couple of things for people who haven’t seen a multi-tab demo before.

First, copy the code, and then go to Codea, hold down the Add New Project button until you see “Paste into project”, then touch that. This will copy all the code into separate tabs for you.

The first tab, Main, controls all the demos. Unless you are a software guru, leave this alone, because it is complex. Just trust me that it works.

The second tab contains the ThreeD class library.

The remaining four tabs contain the four demos. When you run the program, you can pick one of them, with the slider and press the button to load it. All the code for that demo is in its own tab, and you can copy it to your own project (along with the ThreeD tab of course).

Note – the first line of each demo tab (“if localise then …”)  can be deleted if you make a copy – it is used by the Main tab to control the demos.

 

 

 

 

Advertisement
One Comment

Trackbacks & Pingbacks

  1. Index of posts | coolcodea

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: