ClanLib Tutorial – Part 2: Visuals

In this part of the ClanLib Tutorial, we will learn about ClanDisplay, and how it handles visuals. We will cover drawing using primitives, fonts and animated sprites. The ClanLib resource system will also be covered in some detail.

About ClanDisplay and Display Targets

ClanDisplay is a module that handles a lot concerning getting visuals on the screen. Here you will find functionality like fonts, images, sprites, framebuffers, blending, primitives, shaders, textures, but also stuff like input and collision. ClanDisplay is only an abstraction, and you will also need to use one or more Display Targets as renderers as well. ClanLib supports OpenGL 2/3, OpenGL1 and a multi-core software renderer.

For the examples in this tutorial we’ll use the ClanGL target, which requires an OpenGL 2 or higher graphic card to function. If this does not work for you, you will only need to change a few code lines to switch to a more compatible display target. In the official ClanLib examples, you can also find an example on how to support all display targets in your application, and let the user choose which one to use.

To initialize the display module, you add an include to display.h and instantiating a setup object.

#include <ClanLib/display.h>
...
CL_SetupDisplay setup_display;

To select the display target, you follow the same scheme. In our case, we’ll use the OpenGL target:

#include <ClanLib/gl.h>
...
CL_SetupGL setup_opengl;

But if you want to use the OpenGL 1 target, or the software renderer instead one of the following setup functions:

#include <ClanLib/gl1.h>
...
CL_SetupGL1 setup_opengl1;

#include <ClanLib/swrender.h>
...
CL_SetupSWRender setup_swrender;

Creating a window

To show anything on the screen, we will need a window. In ClanLib, this is called CL_DisplayWindow. You can create as many as you like, but in games it is more common to have only one.

The following code line will create a 640×480 window with the window caption Hello ClanLib!

CL_DisplayWindow window("Hello ClanLib!", 640, 480);

The CL_DisplayWindow shown above assumes a lot of defaults for the window created, but if you need something else than those defaults, you need to first create a display window description. In the following example we describe the window characteristics first, then create the window.

CL_DisplayWindowDescription description;
description.set_size(CL_Size(640,480), true);
description.set_title("Hello ClanLib!");
description.set_allow_resize(false);
CL_DisplayWindow window(description);

If you need more details, read more in the reference about CL_DisplayWindowDescription.

Window Messages

The OS windowing system communicates with the application and its window by sending it messages via a message queue. In order to process these messages, a ClanLib application needs to call CL_KeepAlive::process(). This function then reads and handles the messages, updating the input contexts and emiting signals when the window is resized and so on.

A simple main loop of a ClanLib application may look like this:

CL_SetupCore setup_core;
CL_SetupDisplay setup_display;
CL_SetupGL setup_gl;

CL_DisplayWindow window("Hello ClanLib!", 640, 480);
CL_GraphicContext gc = window.get_gc();
CL_InputContext ic = window.get_ic();

while (ic.get_keyboard().get_keycode(CL_KEY_ESCAPE) == false)
{
    draw_gfx(gc);
    handle_input(ic);
    window.flip();

    CL_KeepAlive::process();
    CL_System::sleep(10);
}

Here we also call CL_System::sleep to avoid busy looping the CPU.

Primitives drawing

Lets summarize all the code so far into one example that initialize a display, a window, a main loop and draws some primitives; a typical Programmers Art example! Notice that we have split it into two classes – one static class Program that initializes ClanLib and handles any exceptions. If an exception occurs, we make sure to create a console window to display the error and full code stack trace in.

The rest of the example is in a separate non-static class called PrimitivesExample, where we draw little scene using only primitives. ClanLib has a static class called CL_Draw, which is a convenience class that lets you easily draw points, lines, boxes, filled boxes, circles and gradients.

Notice we use the ClanLib signal and slot system to hook into the window close event, to make sure we quit the application when the user clicks the window close button. The signal and slot system is a very powerful way to get callbacks from ClanLib. The syntax might seem weird, but you will get used to it later. There is one very important thing to remember when using signals – keep the CL_Slot returned from the connect function. If you forget this, the slot will disconnect right away, and your callback will never be called!

#include <ClanLib/core.h>
#include <ClanLib/display.h>
#include <ClanLib/gl.h>
#include <ClanLib/application.h>

class PrimitivesExample
{
private:
   bool quit;

public:
   void run()
   {
       quit = false;

       CL_DisplayWindow window("Sunset", 640, 480);

       CL_Slot slot_quit = window.sig_window_close().connect(this, &PrimitivesExample::on_window_close);

       CL_GraphicContext gc = window.get_gc();
       CL_InputDevice keyboard = window.get_ic().get_keyboard();

       while (!quit)
       {
           if(keyboard.get_keycode(CL_KEY_ESCAPE) == true)
               quit = true;

           draw_sunset(gc);

           window.flip();
           CL_KeepAlive::process();

           CL_System::sleep(10);
       }
   }

   void draw_sunset(CL_GraphicContext &gc)
   {
       CL_Colorf red(155/255.0f, 60/255.0f, 68/255.0f);
       CL_Colorf yellow(255/255.0f, 234/255.0f, 117/255.0f);
       CL_Colorf blue(13/255.0f, 75/255.0f, 74/255.0f);
       CL_Colorf lightblue(16/255.0f, 91/255.0f, 90/255.0f);

       // Draw top of sunset
       CL_Gradient gradient1(CL_Colorf::black, red);
       CL_Draw::gradient_fill(gc, CL_Rectf(0,0,640,160), gradient1);

       // Draw second part of sunset
       CL_Gradient gradient2(red, yellow);
       CL_Draw::gradient_fill(gc, CL_Rectf(0,160,640,240), gradient2);

       // Draw the sun
       CL_Draw::circle(gc, CL_Pointf(320, 240), 15, CL_Colorf::white);

       // Draw the ground
       CL_Draw::fill(gc, CL_Rectf(0, 240, 640, 470), blue);

       // Draw some distance lines
       for(int y = 241, ydelta = 2; y < 480; y += ydelta, ydelta += ydelta)
       {
           CL_Draw::line(gc, 0, y, 640, y, lightblue);
       }
   }

   void on_window_close()
   {
       quit = true;
   }
};

class Program
{
public:
   static int main(const std::vector<CL_String> &args)
   {
       CL_SetupCore setup_core;
       CL_SetupDisplay setup_display;
       CL_SetupGL setup_gl;

       try
       {
           PrimitivesExample example;
           example.run();
       }
       catch(CL_Exception &exception)
       {
           // Create a console window for text-output if not available
           CL_ConsoleWindow console("Console", 80, 160);
           CL_Console::write_line("Error: " + exception.get_message_and_stack_trace());

           console.display_close_message();

           return -1;
       }

       return 0;
   }
};
CL_ClanApplication app(&Program::main);

Download source code and project files for this part

Fonts and text

ClanLib has a number of font providers – System fonts, FreeType fonts, Vector fonts and Sprite Fonts. They all have their strengths and weaknesses, but the easiest to use are System fonts. You create a font description describing the font you want created using CL_FontDescription, and then instantiate the font type using it.

Here is a quick code example how to initialize and draw some text:

// Initialize a system font
CL_FontDescription system_font_desc;
system_font_desc.set_typeface_name("tahoma");
CL_Font_System system_font(gc, system_font_desc);

// Initialize a freetype font
CL_FontDescription freetype_font_desc;
freetype_font_desc.set_typeface_name("myfont.ttf");
CL_Font_Freetype freetype_font(gc, freetype_font_desc);

...

// Draw some text using the fonts
system_font.draw_text(gc, 100, 100, "Hello World using Tahoma");
freetype_font.draw_text(gc, 100, 200, "Hello World using a freetype font");

Note, you can experiment with the various providers through the ClanLib example called Font.

Resources

To load assets in ClanLib, it is common to use a Resource file. This is an XML file with a syntax for defining various assets, like images, sprites, sound, and custom data.

An example resource file:

<?xml version="1.0" encoding="iso-8859-1"?>
<resources>
<sprite name="WalkingMan" base_angle="180">
	<image file="Gfx/walkingman1.png" />
	<image file="Gfx/walkingman2.png" />
	<image file="Gfx/walkingman3.png" />

	<translation origin="center"/>
	<rotation origin="center"/>
	<animation pingpong="no" speed="100"/>
</sprite>
<section name="sounds">
	<sample name="MissileHit" file="Sound/missilehit.wav"/>
</section>
<section name="stuff">
	<my-type name="my-resource1">
		<text>Hello World!</text>
	</my-type>
</section>
</resources>

To load resources, you first need a CL_ResourceManager. Each resource type in ClanLib has a constructor to initialize itself from a resource manager.

CL_ResourceManager resources("resources.xml");
CL_SoundBuffer sample("sounds/MissileHit”, &resources);
CL_Sprite sprite(gc, “WalkingMan”, &resources);

You can also define custom tags, and load them using a XML DOM API:

CL_String load_my_type(const CL_String &res_id, CL_ResourceManager &resources)
{
    CL_Resource resource = resources.get_resource(res_id);
    if (resource.get_type() != "my-type")

        throw CL_Exception("Resource not of expected type!");
    CL_DomElement text_element = resource.get_element().named_item("text").to_element();

    return text_element.get_text();
}

CL_ResourceManager resources("resources.xml");
CL_Console::write_line(load_my_type("stuff/my-resource1", resources);

Sprites and animation

As probably most people know, a sprite is a collection of 2D images (called frames), shown in sequence with a delay between each frame. Sprites are used for a lot of game objects, i.e. moving people, spaceships, chairs, powerups, missiles, animated mouse cursors, etc.

CL_Sprite does all this for you in a very easy, yet flexible way. The simplest sprites are just a collection of frames, all shown after eachother, drawn on the screen somewhere. If you need more advanced features, sprites can be rotated and made semi-translucent, you can tweak the animation speed for individual frames, set frame offsets, set drawing and rotation alignment, you can play them in a backward looping pattern, a ping-pong loop, forward just-once, etc.

Let us use the new knowledge to add some more juice to the sunset application. After a quick search on the internet, I found some sprites we can play around with. It is a 4 framed animation; which has been arranged into a sprite sheet. Each frame is 159×91 pixels.

Configuring sprites in resources is very flexible, and spritesheets are supported through a <grid> tag. Note that there also exists a utility called Texture Packer in the ClanLib distribution that packs a bunch of images into spritesheets and generates resource files automatically. Also check out the full range of possibilities using sprites and sprite resources in the Clanlib documentation.

Defining the boat in a resource file called resources.xml:

<?xml version="1.0" encoding="iso-8859-1"?>
<resources>
<sprite name="Boat">
	<image file="boat.png">
		<grid size="159,91" array="4,1" />
	</image>
	<animation speed="200" loop="yes" pingpong="no" />
</sprite>
</resources>

And adding some code to load, show and animate it:

void run()
{
	quit = false;

	CL_DisplayWindow window("Sunset", 640, 480);

	CL_Slot slot_quit = window.sig_window_close().connect(this, &SpritesExample::on_window_close);

	CL_GraphicContext gc = window.get_gc();
	CL_InputDevice keyboard = window.get_ic().get_keyboard();

	CL_ResourceManager resources("resources.xml");

	CL_Sprite boat_sprite(gc, "Boat", &resources);

	CL_FontDescription font_desc;
	font_desc.set_typeface_name("tahoma");
	font_desc.set_height(30);
	CL_Font_System font(gc, font_desc);

	while (!quit)
	{
		if(keyboard.get_keycode(CL_KEY_ESCAPE) == true)
			quit = true;

		draw_sunset(gc);
		boat_sprite.draw(gc, 70, 252);
		font.draw_text(gc, 146, 50, "A quiet evening in the pacific...");

		boat_sprite.update();

		window.flip();

		CL_KeepAlive::process();
		CL_System::sleep(10);
	}
}

Download source code and project files for this part

Conclusion

This concludes the basic visuals in ClanLib part. There is a lot more to cover in this area, but that is not the main focus on this tutorial. You should be able to get started doing 2D visuals, and feel free to experiment more. There are a bunch of examples in ClanLib that are worth looking at, and also read through the ClanLib documentation for more details.

In the next part, we will learn about network basics – creating a server and a client and sending events back and forth. Read Part 3!

About these ads
  1. #1 by Jose Jorge Enriquez on October 12, 2010 - 16:59

    In your pictures ground extends to the bottom of the window, but on your code we get a 10 pixel black space at the bottom. It is because the fill instruction for the ground only extends to 470, but it should be to 480 in order to fill the screen.

    I changed this: (lines 55-56 in primitives.cpp and 71-72 in sprites.cpp)

    // Draw the ground

    CL_Draw::fill(gc, CL_Rectf(0, 240, 640, 470), blue);


    to this:

    // Draw the ground

    CL_Draw::fill(gc, CL_Rectf(0, 240, 640, 480), blue);

    Again, thanks a lot for your effort on writting these tutorials, I’m looking forward to reading next releases :).

    • #2 by sphair on October 12, 2010 - 19:48

      Thanks for pointing this out. The reason I did not notice is that apparently the default DisplayWindow constructor does not specify the size as the client area – that is, the window size without the borders. If you are using Linux (or maybe something else than Windows 7), apparently the behavior is different, making the black area appear.

      The solution is to do as you write – fix the 480 size in the draw call, but also change the way the DisplayWindow is created. If we use a CL_DisplayWindowDescription, we can specify the size of the window should be in client area size. I’m going to update the code and text above to reflect this later.

      Part 4 is coming along nicely, but it takes some time to write these long articles. ETA is two weeks :)

  2. #3 by Rca ieftin on January 5, 2012 - 11:17

    Thank you for the great tutorial it was really good ,you really helped me a lot .

  3. #4 by D.C. Ramirez on March 20, 2012 - 08:21

    Thanks for the nice tutorial, but I’ve been wondering on how do you move those images like when you do a mouse click or something.. anyways, I’ll head on to ClanLib and search for that =P

  4. #5 by D.C. Ramirez on March 21, 2012 - 06:06

    void draw_sunset(CL_GraphicContext &gc)
    {
    ….
    }

    I don’t seem to understand this line, does this change the address of an undeclared variable? Why add a “&”? The only parameters that I know is either an ordinary variable or a pointer (*).

    • #6 by sphair on March 21, 2012 - 08:13

      It is called pass-by-reference. Look it up in any book on c++, it is quite a central and common thing in C++.

      http://www.learncpp.com/cpp-tutorial/73-passing-arguments-by-reference/

      • #7 by D.C. Ramirez on March 21, 2012 - 12:15

        Oh ok, so it’s practically the same with PHP. So does this work the same way how pointers works? Ok, why the heck am I asking basic stuffs here ei? anyways, thanks for the clarification.

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 )

Google+ photo

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

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: