Marmalade SDK Tutorial – Loading bitmaps and rendering sprites with Iw2D

This tutorial is part of the Marmalade SDK tutorials collection. To see the tutorials index click here

Carrying on from my previous Marmalade SDK tutorial that covered creating a basic game loop, I promised that I would move onto something a little more interesting. Well I was going to write this some other day, but I wanted to get it out of the way. As usual if your here just for the code then you can download it from here

Ok, in this tutorial we are going to cover drawing a sprite using Marmalade’s Iw2D module. if you do not know what Iw2D is then its Marmalade’s simple 2D drawing API that you can use for drawing a whole host of simple 2D objects, including:

  • Lines
  • Arcs
  • Rectangles
  • Polygons
  • Images
  • Text

Primitives can be rotated, translated, scaled and flipped. You can also set the colour and opacity.

Its worth noting at this point that Iw2D is context (or state) based (like Open GL). which means that if you modify a parameter within the context, such as a transform or a colour then it will be applied to all rendered primitives until you change the parameter again. So for example, if you set the current colour to red using Iw2DSetColour(0xff0000ff) then all primitives will have a red tint until you change the colour.

Ok, so lets get to some code. I put together some quick sprite rendering code that renders two sprites using rotation, scaling and translation:

// Marmalade headers #include "s3e.h" #include "Iw2D.h" #include "IwGx.h" void DrawSprite(CIw2DImage* image, int at_pos_x, int at_pos_y, iwangle angle = 0, iwfixed scale = IW_GEOM_ONE) { // Set the rotation transform CIwMat2D m; m.SetRot(angle); // Scale the transform m.ScaleRot(scale); // Translate the transform m.SetTrans(CIwSVec2(at_pos_x, at_pos_y)); // Set this transform as the active transform for Iw2D Iw2DSetTransformMatrix(m); // Render the sprite int x = -(image->GetWidth() / 2); int y = -(image->GetHeight() / 2); Iw2DDrawImage(image, CIwSVec2(x, y)); } int main() { // Initialise Marmalade graphics system and Iw2D module IwGxInit(); Iw2DInit(); // Set the default background clear colour IwGxSetColClear(0x40, 0x40, 0x40, 0); // Create two images from PNG files CIw2DImage* image1 = Iw2DCreateImage("test1.png"); CIw2DImage* image2 = Iw2DCreateImage("test2.png"); // Get the display surface width and height int surface_width = Iw2DGetSurfaceWidth(); int surface_height = Iw2DGetSurfaceHeight(); // Dynamic variables iwangle angle = 0; // Main Game Loop while (!s3eDeviceCheckQuitRequest()) { // Update keyboard system s3eKeyboardUpdate(); if (s3eKeyboardGetState(s3eKeyAbsBSK) & S3E_KEY_STATE_DOWN) // Back key is used to exit on some platforms break; // Update pointer system s3ePointerUpdate(); // Clear the screen IwGxClear(IW_GX_COLOUR_BUFFER_F | IW_GX_DEPTH_BUFFER_F); // Draw two sprites DrawSprite(image1, surface_width / 2, surface_height / 2, -angle, (iwfixed)(IW_GEOM_ONE * 2)); DrawSprite(image2, surface_width / 2, surface_height / 2, angle, IW_GEOM_ONE); // Spin our awesome sprite angle += IW_ANGLE_2PI / 60; // Show the surface Iw2DSurfaceShow(); // Yield to the opearting system s3eDeviceYield(0); } // Safely cleanup images if (image2 != NULL) delete image2; if (image1 != NULL) delete image1; // Shut down Marmalade graphics system and the Iw2D module Iw2DTerminate(); IwGxTerminate(); return 0; }

The main() function code looks pretty much the same as in our BasicMKB example except for a few additions (marked in bold). The additions are:

// Create two images from PNG files CIw2DImage* image1 = Iw2DCreateImage("test1.png"); CIw2DImage* image2 = Iw2DCreateImage("test2.png");

These two lines basically create two images from PNG files that I have added to our projects MKB file. if you open up the DrawSprite_Iw2D.mkb file and take a look, you will notice that the following two lines have appeared in the assets section:

test1.png
test2.png

This instructs Marmalade to include these files when we deploy our app or game to a phone, tablet or TV

Its worth noting at this point that if I had forgotten to add these two lines then my awesome sprites would not have been included in the build when I go to deploy it to an actual device, but it would still work on the emulator no problem (I’ve fallen for this many times)

We also added new code to get the width and height of the drawing surface that we are going to draw our sprites to using:

// Get the display surface width and height int surface_width = Iw2DGetSurfaceWidth(); int surface_height = Iw2DGetSurfaceHeight();

We will later use these values to centre our sprite on the display because it looks much nicer than being stuck in the top left hand corner.

Because we want our sprites to do something a little more interesting than just sitting there in the middle of the screen, we are going to spin them (spinning stuff usually impresses people). In order to do that we need track an angle and change it every game frame.

// Dynamic variables iwangle angle = 0;

We create our angle variable of type iwangle. You could just use int if you like but, then the variable would lose some of its meaning. Angles in Marmalade are integer and the range of 360 degrees is defined by the constant IW_ANGLE_2PI

In the main loop we use the following two lines of code to render our sprites:

// Draw two sprites DrawSprite(image1, surface_width / 2, surface_height / 2, -angle, (iwfixed)(IW_GEOM_ONE * 2)); DrawSprite(image2, surface_width / 2, surface_height / 2, angle, IW_GEOM_ONE);

I decided to stick all of the sprite rendering code into a nice easy to use re-usable function so that you just whip off with it and use it. It also keeps my code nice and readable.

Lastly, we update the angle of the sprites using:

// Spin our awesome sprite angle += IW_ANGLE_2PI / 60;

This spins our sprites 6 degrees every game frame (pretty fast in other words)

Ok, now those bits are out of the way, we will move onto the DrawSprite() function. Unfortunately, you do bump into matrices at this point, if you do not know what they are then do not worry as long as you know how to use them. In a nutshell, A matrix is a mathematical object that allows you to transform a set of coordinates. In our case we are using it to rotate, scale and then translate (move) the coordinates of our sprite. The important parts of code in DrawSprite() that I want to point out are:

// Set this transform as the active transform for Iw2D Iw2DSetTransformMatrix(m);

Here we tell Iw2D to set the “current” transform to use when drawing “anything”. Remember that Iw2D is context based and this transform will be remembered, so if you attempt to draw something else without again changing the transform then it will be drawn in the same position, at the same angle and at the same scale. Note that you can reset the current transform back to normal using Iw2DSetTransformMatrix(CIwMat2D::g_Identity). The identity matrix is the default transformation matrix that has no rotation, no translation and no scaling.

// Render the sprite int x = -(image->GetWidth() / 2); int y = -(image->GetHeight() / 2); Iw2DDrawImage(image, CIwSVec2(x, y));

This section of code draws our sprite on screen. Note that we offset the sprite up and to the left by half its height and width so that it is positioned using its centre point rather than its top-left hand corner. This allows out sprite to be rotated and scaled around its centre.

The final result should look something l like this:

DrawSprite example shot

DrawSprite screenshot for Marmalade SDK Iw2D Tutorial

Like anything to do with game development you will eventually come to the point where you need to squeeze every last drop of power out of your rendering engine. Each time you render a different image a new state change is sent to the underlying hardware (this is not efficient by any stretch of the imagination). To help you, Marmalade built an automated batching system into Iw2D so that multiple draw calls using the same image are collected together and batched. Batched is a term used to describe sticking lots of data together and sending it to the hardware in one go. This process is not totally automated however, you will need to draw images by group. For example, lets say you have 10 trees and 5 balls in your scene. The most optimal way would be to draw all of the trees then all of the balls, not 2 trees, 1 ball, 2 trees, 2 balls etc..The astute among you will have noticed a simple problem with batching and that is the problem of draw order. Sometimes it is not practical to draw all trees then all balls because sometimes a ball may appear behind a particular tree.

So how do we solve this problem other than using a z-buffer, which I believe is not supported by Iw2D? The solution is to arrange the ball and tree image on a single bitmap (usually called a sprite atlas or sprite sheet). When you want to draw the tree, you simply render the portion of the image that represents the tree and when you want to draw a ball, you simply render the portion of the image that represents the ball. This way, you are always rendering the same image (albeit different potions of the image) and never have to worry about batching optimisations.

As usual you can download the source code for this tutorial from here

Happy coding and until next time, be careful crossing roads!

37 thoughts on “Marmalade SDK Tutorial – Loading bitmaps and rendering sprites with Iw2D

  1. Wes says:

    Please keep these excellent tutorials rolling. Really useful for getting started with this awesome SDK :D

  2. Doug Hayes says:

    Before I felt overwhelmed going into marmalade development. I Know that there is some learning curve going into any new development environment, but getting helpful tutorials like these are fantastic!

  3. pugmartin says:

    Brilliant so far.

    No pressure, but the next few tutorials will sway my decision on whether i fork out for Marmalade or not. Had a little trouble getting some things up and running so far (buckets etc required for an oversize image to enable simple scrolling…) and these are exactly what I’m after.

    Do you have any idea on timescales between tutorials? Cant wait to get to the meatier ones.

  4. drmop says:

    I wish I had more time to spend on the blog but I need to fit it in-between my paying work I’m afraid. I will try to cover a few more topics this coming weekend.

  5. pugmartin says:

    Thats good enough for me going by the tuts so far.
    As an aside, I have just today tried out Funky Cam – incredible work.

    I *think* i may have found a new home…

    Thanks for the work done so far, looks like its going to be an excellent resource.

  6. drmop says:

    Glad you like Funky Cam, It started out as a novel idea, which I thought others would enjoy playing around with.

    No problem, if there is something specific you need me to cover then let me know

  7. drmop says:

    Its worth noting that it is possible to clear the background an easier way than using:

    IwGxSetColClear(0×40, 0×40, 0×40, 0);
    IwGxClear(IW_GX_COLOUR_BUFFER_F | IW_GX_DEPTH_BUFFER_F);

    You can simply replace these lines with
    Iw2DSurfaceClear(0xff404040);

    Thank you to nhold for pointing that out

  8. CoCoKool says:

    What is the frame rate? How do you change it? Where is the delta calculation in case one frame takes longer to draw.

  9. drmop says:

    The frame rate is limited by the update rate of the device you are running on, usually around 30 fps.

    I will be covering scaling transients by time in a forthcoming tutorial about time and timers. This tutorial is focused on loading bitmaps and rendering them and not timing.

  10. CoCoKool says:

    Thanks for the reply. But there are loads of devices that run at 60Hz, is there a function that I can call to set the desired FPS or do I have to calculate it as usual?

    How Would I make the rotate slower or faster?

  11. drmop says:

    I do not know of any functionality that lets you change the refresh rate of the hardware.

    I’m afraid that you will have to calculate how long the last frame has taken and scale your next frames transient variables to account for any variation in frame rate.

    I will see if I can find some time tomorrow to work on the time and timers tutorial.

    You can change the rate of spin of the sprites in this tutorial by changing this line:

    angle += IW_ANGLE_2PI / 60;

  12. eyeslave says:

    great tutorial! excited to learn about the next one….

  13. Brian says:

    Thanks for these helpful tutorials. Do you know if there is an easy way to create a CIw2DImage from a PNG memory buffer pointer instead of a file?

    I have a metafile filled with images in PNG format and I’d like to create a CIw2DImage for each one. What I have is a pointer to the data that’s already in memory (it’s the same as the data in a .png file).

    I’ve browsed the API reference but haven’t figured it out yet. Just curious if you knew a way to do it. Thanks for any info. :)
    -Brian

  14. drmop says:

    Hi Brian,

    I haven’t come across such a function n the SDK. The only way that I know to do this is to load the PNG file and decompress the pixel data (using libpng) then create a CIwImage from it. You can then create a CIw2DImage from the CIwImage using Iw2DCreateImage().

    A very quick way of doing what you need is to do is save the PNG buffer out then you can create it from a file (not ideal)

    Marmalades file system is also customisable, so you could potentially re-target the file system to read from memory instead.

    Mat

  15. Brian says:

    Hi again Mat,

    I figured out a way to do it so I thought I’d share it… I stumbled on the function s3eFileOpenFromMemory() and using this, I was able to initialize a CIwImage with my png data buffer using the ReadFile() function. From there, it was easy to create the CIw2DImage.

    Best,
    -Brian

  16. drmop says:

    Hi Brian,

    That’s great news, nice find, I will make a note of that function

    Mat

  17. Robert says:

    I have been struggling with starting up with marmalade, yet it looks so powerful and useful.

    Your tutorials that i just found look like the exact thing i need to get into this SDK.

    Thank yourvery much for taking time to create these tutorials, and i loko forward to reading the next one.

    btw: i was stuck on trying to get an asset into the project. I could not find it in all that documentation i was given, bah!

    Thanks again,
    Robert

  18. drmop says:

    Marmalade is an amazing and powerful SDK. I’ve been developing gaming products for the last 20 years and I’ve never come across anything as good. Marmalade reminds me of the start of the DirectX days, where DirectX enabled high speed hardware accelerated gaming across all PC platforms. Believe it or not the PC market was once fragmented by video cards.

    Wiring the tutorials is a pleasure and I’m glad that you are benefiting from them.

    What asset are you having trouble with?

  19. Robert says:

    The assets were not the problem. Knowing to put the list of assets i wanted to load into the MBK file was my error. I just did not know. I am used to writing games from the ground up, and having SDKs soemtimes gets in my way (i have a simple view of memory and layout etc).

    Your tutorials are an excellent way to get into game coding,and i am enjoying reading them. Soon i will convert my #wp7 game over and see how that goes. Then maybe after that one of my Xbox games.

    Keep up the good work, and i am now following you on Twitter as well.

    thanks, and now i am enjoying the Maramalde experience :)

    Rob

  20. drmop says:

    Ah, I see, good to see that you sorted it. I should do a blog about adding assets to the build. There are actually three ways to get assets into a build:

    1) Using resource groups
    2) Adding the files directly to your assets section of the MKB (as you already discovered)
    3) Loading them from a web server

    I was working with WP7 when it first arrived, but not really had much chance to go back to it. I very much like Silverlight (more precisely XAML to the point that our own UI uses XAML style XML files). In addition, selling something on WP7 seems to be incredibly difficult unless its a live game.

  21. Robert says:

    Hi once again DrMop.

    I would love to to see an overview of all the ways one can get assets into the project. Also any tips on getting the windows emulator up and running faster (like textures being in better formats than just PNGs for example).

    I will be writing about your useful Blog in my own very soon, as it really has given me the jump start i needed.

    Thank you very much,
    Robert
    Da Voodoochief

  22. drmop says:

    Hi Robert,

    I will see if I can free some time up today to put a quick blog together about adding assets to the build

    Mat

  23. pugmartin says:

    Hi Mat,

    I wonder if you could help me?

    Running under the simulator in debug mode my textures appear fine, though for some reason when running on in under simulator in release mode or on iOS device (debug OR release) the textures appear as a black silhouette only.

    Any ideas as I’ve hit a wall?
    Seems strange that it happens in sim from debug to release.

  24. pugmartin says:

    Update:

    Managed to get it working correctly now.
    It was an issue with the alpha channel and rgb not being set correctly prior to drawing… though the code worked fine in the debug mode?
    This surprised me tbh.

    Thanks anyway,
    Martin

  25. drmop says:

    HI Martin,

    Sorry that I didn’t get the chance to reply, its been an incredibly busy week, so busy that I’ve been surviving on 4-5 hours sleep per night. We will have a major deadline out the way this week though so I can finally get some free time :)

    I believe the issue is with the different implementations of OpenGL on the underlying hardware.

    Glad to see you got it sorted.

    Mat

  26. Roman says:

    Is there a way to get access and modify particular pixels in image1 or image2 loaded from .png? I want do separate R, G, and B images, and set alpha to 0.33 and I can’t find any good method in documentation to do that.

  27. drmop says:

    Firstly get the material of the CIw2DImage then get the texture from the material. You can then call GetTexels() to return a pointer to the pixel data (be careful here because once the texture data has been uploaded it gets freed).

    CIwMaterial* mat = Image2D->GetMaterial();
    CIwTexture* texture = mat->GetTexture();
    uint8* pixels = texture->GetTexels();

    You will need to check the pixel format of the data using:

    CIwImage::Format format = texture->GetFormat();

  28. Max says:

    Hey man. Cool set of tutorials you have here.

    I’ve been pretty frustrated by Marmalade, I’m totally incapable of getting anything approaching a reasonable frame rate on my iPad (1). I used their simple “Hello World” (the IW) code, instrumented it with some timers, and found that the SurfaceShow() call was taking 150-200 ms, even for that simple code.

    The same goes for this tutorial; all the rest of the code runs as fast as you’d expect, but the SurfaceShow is taking 200ms or so, meaning the frame rate is atrocious.

    Do you have any idea what might be wrong?

  29. drmop says:

    Hi Max,

    Very odd issue, not experienced anything like that. have you tried deploying to a different device to ensure that its not a hardware issue?

    What development environment and version of Marmalade you using?

    Mat

  30. Max says:

    Mat,

    Thanks for the reply. I’m using VS2010 Express (perhaps I should upgrade to Pro, we’ll see), and I’m using the most recent version of Marmalade (apologies, I’m not at my PC right now so I can’t be sure exactly which, but I installed it about a week ago).

    I’ve only got the one iPad to deploy to sadly. The hardware is “good” so far as I can tell; I can run normal games from the AppStore without issue.

    I modified the deployment settings to not allow native iPad resolution, forcing it to be an iPhone app, and that ran better although still not amazingly well. I’ve not set anything interesting in my .icf files, perhaps I should?

    The fact that it’s explicitly on the ShowSurface call seems like maybe I’m not setting up frame buffers correctly or something.

    It’s infuriating; I’m actually a professional game dev by day (although I do high level stuff, AI engineering, not the low level stuff like this) and I wanted to dabble with iOS dev at home but this hurdle has really limited me. Any suggestions would be appreciated.

    Max

  31. drmop says:

    Have you tried a number of different Marmalade SDK examples? For example the Karts game demo?

    We can probably safely exclude your hardware and the dev environment from being the problem, which leaves either a) Something wrong on the Marmalade side or b) Some weird system settings that are affecting the ARM compiler in some way.

    Are you calling s3eDeviceYield(0) somewhere in your game loop? Also ensure that you are not trying to show the surface more than once per game loop.

  32. Rao says:

    Great tutorials! I love the IwGame engine as well. I am currently using it for my title screen. I’m anticipating the release of a series of 3d tutorials :D

  33. drmop says:

    Thanks. We are looking to add 3D functionality into the IwGame engine over the coming months (we already started preparing for this by decoupling the rendering code away from Iw2D and giving depth to sprites). I will write a bunch of Marmalade 3D tutorials when we begin adding support for 3D model / scene rendering to IwGame.

  34. Rao says:

    Wow, I’m looking forward to them, thanks!

    I started using virtual screen sizes, and I can’t seem to get it working. I wrote this in my icf file:

    [GL]
    VirtualWidth=1024
    VirtualHeight=640
    VirtualLetterbox=1

    I also added this into my while loop:

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrthof(0, 1024.0f, 640.0f, 0, -10.0f, 10.0f);
    glViewport(0, 0, 1024, 640);

    In your CInput class, I changed a few of the callbacks to this:

    CTouch* touch = InputSys.getTouch(0);
    touch->active = event->m_Pressed != 0;
    //Transform from device coordinates to virtual coordinates
    CIwGLPoint point(event->m_x, event->m_y);
    point = IwGLTransform(point);
    touch->x = point.x;
    touch->y = point.y;

    Yet, every coordinate is in terms of the screen resolution, not the virtual resolution. Any ideas? Thanks for your help.

  35. drmop says:

    Looking at your code it should work. Are the touch x, y values coming from the input system correct?

  36. Rao says:

    Looking back at it, I don’t think the values are being transformed. I did this:

    CTouch* touch = InputSys.getTouch(0);
    if (touch != NULL)
    {
    char dbg2[256];

    CIwGLPoint point(s3ePointerGetTouchX(0), s3ePointerGetTouchY(0));

    sprintf(dbg2, “GL: %d, %d — CInput: %d %d”, point.x, point.y, touch->x, touch->y);
    Iw2DDrawString(dbg2, CIwSVec2(0, 50), region, IW_2D_FONT_ALIGN_CENTRE, IW_2D_FONT_ALIGN_CENTRE);
    }

    Both “point” and “touch” have the same values. I don’t think IwGLTransform() is doing anything.

  37. Rao says:

    IwGame engine is also giving me non-virtual values. :\

Leave a Reply