Category Archives: Tutorial

Duktape to automate Ogre3D scenes

I have been playing around with the Ogre3D graphics engine. Ogre3D has been around for a while but only recently, after getting burnt out on OpenGL, have I started to learn more about how Ogre works. After making my way through their set of beginner tutorials (here) I was ready to start making my own scenes and start building a little prototype. I wanted an easy way to editor my scene without having to recompile everything every time I slightly move something within the scene. Typically this is where most people go out and learn 3Ds Max or Blender or even any of the custom Ogre scene editors. But after spending a day working through the Ogre tutorials I felt like I had experienced enough tutorials for one day.

So, if you are like me, you would like to have another option for manipulating a scene quickly without having to recompile. This is an excellent excuse to embed a scripting language within your C/C++ program! Here is an example of building Ogre scenes using a JSON file and Duktape. Duktape can do more than just parse JSON files, so it is a great addition to your codebase – especially if things like Google’s V8 Engine is overkill for what you are looking to do.

Let’s start by viewing a little test scene I’ve been playing around with.

So this is a basic scene I built using the sample meshes which come with the Ogre SDK. Below is the JSON file which builds this particular scene.

{

"SceneNodes": [
	{
		"Name": "FloorNode",
		"Position": [0.0, -5.0, 0.0]
	},
	{
		"Name": "TestNode",
		"Position": [0.0, 25.0, 0.0]
	}
],

"Entities": [
	{
		"Name": "FloorEntity",
		"Mesh": "floor00.mesh",
		"SceneNode": "FloorNode",
		"CastShadow": false
	},
	{
		"Name": "TestEntity",
		"Mesh": "ogrehead.mesh",
		"SceneNode": "TestNode",
		"CastShadow": true
	}
],

"Lights": [
	{
		"Name": "TestLight",
		"Type": "Point",
		"Position": [250.0, 100.0, 250.0]
	}
]

}

Ogre doesn’t know how to parse this file at all. All the conventions here I define within my code using the Duktape API. Once Duktape parses the JSON file I retrieve the relevant information I need to build my scene using Ogre. First we read in the JSON file as a string so we can later hand it over to Duktape, which will then use it to create a Javascript object.

int strLen = 0;
char* fileStr = NULL;

FILE* file = fopen("../../data/scene/test.json", "r");
if(file == NULL) exit(-1);

char c;
do {
	fread(&c, sizeof(char), 0x01, file);
	fileStr = (char*) realloc(fileStr, ++strLen*sizeof(char));
	fileStr[strLen-1] = c;
} while(!feof(file));

fileStr = (char*) realloc(fileStr, ++strLen*sizeof(char));
fileStr[--strLen] = '';

fclose(file);

So now we have a string pointer, char* fileStr; , which points to the contents of our JSON file. Here you could do any “pre-processing” work that may be needed. Next we need to send this string over to Duktape. It is very simple to get Duktape up and running.

duk_context* mContextJS;
mContextJS = duk_create_heap_default();

duk_push_global_object(mContextJS);

duk_push_string(mContextJS, fileStr);
duk_json_decode(mContextJS, -1);

free(fileStr);

Here we decode the JSON object and free our previously allocated string. From here you can literally do anything with the JSON object. Below is one example but there is really no limit to how complex these scenes could get!

duk_get_prop_string(mContextJS, -1, "SceneNodes");

size_t i;
for(i=0; i<duk_get_length(mContextJS, -1); i++) {
	duk_get_prop_index(mContextJS, -1, i);

	duk_get_prop_string(mContextJS, -1, "Name");
	const char* nodeName = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "Position");
		
	duk_get_prop_index(mContextJS, -1, 0);
	Ogre::Real x = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);
	duk_get_prop_index(mContextJS, -1, 1);
	Ogre::Real y = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);
	duk_get_prop_index(mContextJS, -1, 2);
	Ogre::Real z = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);

	duk_pop(mContextJS);

	/* === */

	Ogre::SceneNode* node;
	node = mSceneMgr->getRootSceneNode()->createChildSceneNode(nodeName);

	node->setPosition(x, y, z);

	/* === */

	duk_pop(mContextJS);
}

duk_pop(mContextJS);

/* === */

duk_get_prop_string(mContextJS, -1, "Entities");

for(i=0; i<duk_get_length(mContextJS, -1); i++) {
	duk_get_prop_index(mContextJS, -1, i);

	duk_get_prop_string(mContextJS, -1, "Name");
	const char* entName = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "Mesh");
	const char* meshFN = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "SceneNode");
	const char* nodeName = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "CastShadow");
	bool castShadow = duk_to_boolean(mContextJS, -1) & 0x01;
	duk_pop(mContextJS);

	/* === */

	Ogre::SceneNode* node = mSceneMgr->getSceneNode(nodeName);
	Ogre::Entity* entity  = mSceneMgr->createEntity(entName, meshFN);

	entity->setCastShadows(castShadow);

	node->attachObject(entity);

	/* === */

	duk_pop(mContextJS);
}

duk_pop(mContextJS);

/* === */

duk_get_prop_string(mContextJS, -1, "Lights");

for(i=0; i<duk_get_length(mContextJS, -1); i++) {
	duk_get_prop_index(mContextJS, -1, i);

	duk_get_prop_string(mContextJS, -1, "Name");
	const char* lightName = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "Type");
	const char* lightType = duk_to_string(mContextJS, -1);
	duk_pop(mContextJS);

	duk_get_prop_string(mContextJS, -1, "Position");
		
	duk_get_prop_index(mContextJS, -1, 0);
	Ogre::Real x = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);
	duk_get_prop_index(mContextJS, -1, 1);
	Ogre::Real y = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);
	duk_get_prop_index(mContextJS, -1, 2);
	Ogre::Real z = (Ogre::Real) duk_to_number(mContextJS, -1);
	duk_pop(mContextJS);

	duk_pop(mContextJS);

	/* === */

	Ogre::Light* light = mSceneMgr->createLight(lightName);

	if(!strcmp(lightType, "Spot"))
		light->setType(Ogre::Light::LT_SPOTLIGHT);
	if(!strcmp(lightType, "Point"))
		light->setType(Ogre::Light::LT_POINT);
	if(!strcmp(lightType, "Directional"))
		light->setType(Ogre::Light::LT_DIRECTIONAL);
	light->setDiffuseColour(1.0, 1.0, 1.0);
	light->setSpecularColour(1.0, 1.0, 1.0);
	light->setPosition(x, y, z);

	/* === */

	duk_pop(mContextJS);
}

duk_pop(mContextJS);

The one line that probably deserves explaining here is duk_to_boolean(mContextJS, -1) & 0x01; , which I did simply to avoid const warnings VC++ was giving about converting from duk_bool_t to bool .

As an added bonus, if we put this code within a void createScene(void); function we can hotload the scene with a single button press (i.e. without having restarting the program). Just add the following to your bool OIS::KeyListener::keyPressed(const OIS::KeyEvent& ke); function’s switch statement.

case OIS::KC_R: {
	mSceneMgr->clearScene();
	createScene();
} break;

Here I’m using the “R”-key to reload my scene.

I much prefer this method to learning a bloated program like 3Ds Max, Blender, or any of the other programs out there. These programs can’t avoid the bloat because they need to be able to perform everything a user needs (and then some). But if you only need the basics they come with way too much overhead to warrant their use.

Hopefully I’ll have more 3D stuff in the future! I’ve never made a 3D game before so this is new territory for me – quite excited!

SDL 2.0 Tutorial-03: Metasprites

So last week we were able to generate a collection of textures and draw them to the screen. In terms of memory this isn’t very efficient. This week we will be creating one texture from a meta SDL_Surface* . This will be my last part of this basic tutorial, if there are any topics you would like discussed please email me at “info@stephenmeier.net”. If you do occasionally read these posts please let me know which posts you find interesting!


/* Stephen's Tutorials (https://stephenmeier.net/)
gcc main.c -o game.exe -I./include -L./lib -lSDL2main -lSDL2
running on gcc 4.8.1, SDL 2.0.1
*/
//----------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>

#include "game.h"

//----------------------------------------------------------------------
int main(int argc, char* argv[]) {

	Game.init();

	int x = Game.screen.w/2-8, y = Game.screen.h/2-8;
	SDL_Rect rect = {0, 0, 8*2, 8*2};

	int i, j;
	int** grid = (int**) malloc(sizeof(int*)*2);
	for(j=0; j<2; j++) {
		grid[j] = (int*) malloc(sizeof(int)*2);
		for(i=0; i<2; i++) {
			grid[j][i] = 2;
		}
	}

	SDL_Texture* spr = Game.gfx.buildSpr(2, 2, grid);

	SDL_Event event;
	while(Game.running) {
		while(SDL_PollEvent(&event)) {
			switch(event.type) {
				case SDL_QUIT: {
					Game.running = SDL_FALSE;
				} break;
			}
		}

		SDL_RenderClear(Game.screen.renderer);

		rect.x = 0+x, rect.y = 0+y;
		Game.screen.drawSpr(spr, &rect);

		SDL_RenderPresent(Game.screen.renderer);
	}

	Game.gfx.destroySpr(spr);

	Game.quit();

	return 0;
}

// Stephen's Tutorials (https://stephenmeier.net/)
//----------------------------------------------------------------------
#ifndef _GAME_H_
#define _GAME_H_

//----------------------------------------------------------------------
#define SCREEN_w 640
#define SCREEN_H 480
#define SCREEN_NAME "Prototype"
#define SCREEN_SCALE 1
#define SDL_MAIN_HANDLED

//----------------------------------------------------------------------
#include "SDL2/SDL.h"

//----------------------------------------------------------------------
void game_init(void);
void game_quit(void);

SDL_Texture* gfx_buildSpr(int w, int h, int** grid);
void gfx_destroySpr(SDL_Texture* spr);

void screen_drawSpr(SDL_Texture* spr, SDL_Rect* rect);

struct {
	// define "attributes"
	SDL_bool running;

	struct {
		unsigned int w;
		unsigned int h;
		const char* name;
		SDL_Window* window;
		SDL_Renderer* renderer;
		void (*drawSpr)(SDL_Texture* spr, SDL_Rect* rect);
	} screen;

	struct {
		unsigned int n;
		SDL_Surface** spritesheet;
		SDL_Texture* (*buildSpr)(int w, int h, int** grid);
		void (*destroySpr)(SDL_Texture* spr);
	} gfx;

	// define "methods"
	void (*init)(void);
	void (*quit)(void);
} Game = {
	SDL_FALSE,
	{
		SCREEN_SCALE*SCREEN_w,
		SCREEN_SCALE*SCREEN_H,
		SCREEN_NAME,
		NULL, NULL,
		screen_drawSpr
	},
	{
		0,
		NULL,
		gfx_buildSpr,
		gfx_destroySpr
	},
	game_init,
	game_quit
};

//----------------------------------------------------------------------
void game_init(void) {
	if(SDL_Init(SDL_INIT_EVERYTHING)!=0) {
		printf("SDL error -> %sn", SDL_GetError());
		exit(1);
	}

	unsigned int w = Game.screen.w;
	unsigned int h = Game.screen.h;
	const char* name = Game.screen.name;

	Game.screen.window = SDL_CreateWindow(
		name,
		SDL_WINDOWPOS_CENTERED,
		SDL_WINDOWPOS_CENTERED,
		w, h, 0
	);

	Game.screen.renderer = SDL_CreateRenderer(
		Game.screen.window, -1,
		SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC
	);

	SDL_Surface* surface = SDL_LoadBMP("spritesheet.bmp");
	int n = ((surface->w/8)*(surface->h/8)+1);

	Game.gfx.n = n;
	Game.gfx.spritesheet = (SDL_Surface**) malloc(sizeof(SDL_Surface*)*n);

	int i, x, y;
	SDL_Rect rect = {0, 0, 8, 8};
	for(i=0; i<n; i++) {
		Game.gfx.spritesheet[i] = SDL_CreateRGBSurface(0, 8, 8, 24, 0x00, 0x00, 0x00, 0x00);
		SDL_SetColorKey(Game.gfx.spritesheet[i], 1, 0xFF00FF);
		SDL_FillRect(Game.gfx.spritesheet[i], 0, 0xFF00FF);
		if(i!=0) {
			x = (i-1)%(surface->w/8);
			y = (i-x)/(surface->w/8);
			rect.x = x*8;
			rect.y = y*8;
			SDL_BlitSurface(surface, &rect, Game.gfx.spritesheet[i], NULL);
		}
	}

	SDL_FreeSurface(surface);

	Game.running = SDL_TRUE;
}

//----------------------------------------------------------------------
void game_quit(void) {
	int i;
	for(i=0; i<Game.gfx.n; i++)
		SDL_FreeSurface(Game.gfx.spritesheet[i]);
	free(Game.gfx.spritesheet);
	Game.gfx.spritesheet = NULL;

	SDL_DestroyRenderer(Game.screen.renderer);
	Game.screen.renderer = NULL;

	SDL_DestroyWindow(Game.screen.window);
	Game.screen.window = NULL;

	SDL_Quit();
}

//----------------------------------------------------------------------
SDL_Texture* gfx_buildSpr(int w, int h, int** grid) {
	SDL_Surface* surface = SDL_CreateRGBSurface(0, 8*w, 8*h, 24, 0x00, 0x00, 0x00, 0x00);
	SDL_SetColorKey(surface, 1, 0xFF00FF);
	SDL_FillRect(surface, 0, 0xFF00FF);

	int i, j;
	SDL_Rect rect = {0, 0, 8, 8};
	for(j=0; j<h; j++) {
		for(i=0; i<w; i++) {
			rect.x = i*8;
			rect.y = j*8;
			SDL_BlitSurface(Game.gfx.spritesheet[grid[j][i]], NULL, surface, &rect);
		}
	}

	SDL_Texture* tex = SDL_CreateTextureFromSurface(Game.screen.renderer, surface);
	SDL_FreeSurface(surface);

	return tex;
}

//----------------------------------------------------------------------
void gfx_destroySpr(SDL_Texture* spr) {
	SDL_DestroyTexture(spr);
}

//----------------------------------------------------------------------
void screen_drawSpr(SDL_Texture* spr, SDL_Rect* rect) {
	SDL_RenderCopy(Game.screen.renderer, spr, NULL, rect);
}

#endif

There are many ways to structure game logic, and for a simple 2D game being written in C it comes down to mostly personal preference.

SDL 2.0 Tutorial-02: Almighty spritesheet

Last week we were able to create a window and renderer. This week we are going to read in a spritesheet image, separate it into individual 8×8 sprites and draw them to the screen.

First thing we need to do is read in the spritesheet. Here I’m using strict SDL2 so we’ll use the .bmp format with an alpha color key. Setting an alpha color key allows us to use an RGB value which acts as an alpha channel. When SDL looks over the image pixels and finds our RGB key it will replace the color with a transparent pixel. This saves on memory because instead of writing pixels in a 32-bit RGBA value we can use a 24-bit RGB value. This is only necessary if you are restricted to just SDL, ideally you would use the SDL2_image package. You can find out more about SDL2_image here.

Alright, let’s start by allocating memory for our “gfx” (graphics) module.

struct {
	// define "attributes"
	SDL_bool running;

	struct {
		unsigned int w;
		unsigned int h;
		const char* name;
		SDL_Window* window;
		SDL_Renderer* renderer;
	} screen;

	struct {
		unsigned int n;
		SDL_Surface** spritesheet;
	} gfx;

	// define "methods"
	void (*init)(void);
	void (*quit)(void);
} Game = {
	SDL_FALSE,
	{
		SCREEN_SCALE*SCREEN_w,
		SCREEN_SCALE*SCREEN_H,
		SCREEN_NAME,
		NULL, NULL
	},
	{0, NULL},
	game_init,
	game_quit
};


This takes our original Game “soliton” and adds a “gfx” module. The graphics module contains unsigned int n;  which is the number of 8×8 sprites found within our .bmp spritesheet, and SDL_Surface** spritesheet; which is an array of SDL surfaces. Each 8×8 sprite will receive its own SDL surface. Later we will arrange these surfaces into textures to be drawn with out renderer.

Next we will read in “spritesheet.bmp”, set the color key to 0xFF00FF and populate the spritesheet array with surfaces. This code should be placed within the void game_init(void); function.

SDL_Surface* surface = SDL_LoadBMP("spritesheet.bmp");
int n = ((surface->w/8)*(surface->h/8)+1);

Game.gfx.n = n;
Game.gfx.spritesheet = (SDL_Surface**) malloc(sizeof(SDL_Surface*)*n);

int i, x, y;
SDL_Rect rect = {0, 0, 8, 8};
for(i=0; i<n; i++) {
	Game.gfx.spritesheet[i] = SDL_CreateRGBSurface(0, 8, 8, 24, 0x00, 0x00, 0x00, 0x00);
	SDL_SetColorKey(Game.gfx.spritesheet[i], 1, 0xFF00FF);
	SDL_FillRect(Game.gfx.spritesheet[i], 0, 0xFF00FF);
	if(i!=0) {
		x = (i-1)%(surface->w/8);
		y = (i-x)/(surface->w/8);
		rect.x = x*8;
		rect.y = y*8;
		SDL_BlitSurface(surface, &rect, Game.gfx.spritesheet[i], NULL);
	}
}

SDL_FreeSurface(surface);

This will give us an array of sprite surfaces with the first sprite (spritesheet[0] ) being a completely transparent surface. This is done on purpose so that the surface array an easily be integrated with the Tiled map editor, which we will discuss in future tutorials.

Next we will move to our int main(int argc, char* argv[]); function where we will create textures for the sprites we wish to render. Ideally, the textures should be allocated for each room/level during the loading screen. This works well for a 2D game but isn’t required as most computers can handle the dynamic allocation without much problem. After modifying the code we are left with the following. If you would like to use the same spritesheet as I’m using here you can get it from here.

int main(int argc, char* argv[]) {

	Game.init();

	int x = Game.screen.w/2-8, y = Game.screen.h/2-8;
	
	SDL_Rect rect = {0, 0, 8*2, 8*2};
	SDL_Texture* texture1 = SDL_CreateTextureFromSurface(Game.screen.renderer, Game.gfx.spritesheet[17]);
	SDL_Texture* texture2 = SDL_CreateTextureFromSurface(Game.screen.renderer, Game.gfx.spritesheet[18]);
	SDL_Texture* texture3 = SDL_CreateTextureFromSurface(Game.screen.renderer, Game.gfx.spritesheet[29]);
	SDL_Texture* texture4 = SDL_CreateTextureFromSurface(Game.screen.renderer, Game.gfx.spritesheet[30]);

	SDL_Event event;
	while(Game.running) {
		while(SDL_PollEvent(&event)) {
			switch(event.type) {
				case SDL_QUIT: {
					Game.running = SDL_FALSE;
				} break;
			}
		}

		SDL_RenderClear(Game.screen.renderer);

		rect.x = 0+x, rect.y = 0+y;
		SDL_RenderCopy(Game.screen.renderer, texture1, NULL, &rect);

		rect.x = 8*2+x, rect.y = 0+y;
		SDL_RenderCopy(Game.screen.renderer, texture2, NULL, &rect);

		rect.x = 0+x, rect.y = 8*2+y;
		SDL_RenderCopy(Game.screen.renderer, texture3, NULL, &rect);

		rect.x = 8*2+x, rect.y = 8*2+y;
		SDL_RenderCopy(Game.screen.renderer, texture4, NULL, &rect);

		SDL_RenderPresent(Game.screen.renderer);
	}

	SDL_DestroyTexture(texture1);
	SDL_DestroyTexture(texture2);
	SDL_DestroyTexture(texture3);
	SDL_DestroyTexture(texture4);

	Game.quit();

	return 0;
}


This will draw a chest in the middle of the screen. Here we are creating a texture for each 8×8 sprite. This is highly inefficient, what we will learn to do later is create a surface with all the 8×8 sprites and then create a texture from this bigger surface. This will help a lot but for now we are fine to do this. The last step here is to clean up the sprite array.

void game_quit(void) {
	int i;
	for(i=0; i<Game.gfx.n; i++)
		SDL_FreeSurface(Game.gfx.spritesheet[i]);
	free(Game.gfx.spritesheet);
	Game.gfx.spritesheet = NULL;

	SDL_DestroyRenderer(Game.screen.renderer);
	Game.screen.renderer = NULL;

	SDL_DestroyWindow(Game.screen.window);
	Game.screen.window = NULL;

	SDL_Quit();
}

That’s it for now, at this point you should be able to play and experiment on your own, till next week. Good luck!