Category Archives: 3D Gamedev

Kodev – Updating the UIQuestMenu

I recently made a video explaining how the quest menu GUI works for the game Knight Online. This is the first part in a two part series where we look at why these GUI components work the way they do and how we can use the GUI editor to help us to implement functionality for later versions. Part two for this series has already been recorded I just need to upload it! I hope to do that in the next week or so. If you have any questions or would like to suggest future topics for videos please email me at “info@stephenmeier.net”. Thanks!

EDIT: I was recently able to upload part 2! In the future I will try not to make the videos so long because they quickly become hard to upload.

 

Knight Online, Repent Online, and the N3Base

Just wanted to share an interesting tidbit of information I stumbled upon. MGame, a Korean MMO developer, worked on two major projects around 2002/2004. One was Knight Online and the other was Repent Online. You can still play Knight Online today and it is possible to play Repent Online but you need a Korean social security number. Anyways, I played Knight Online (KO) a TON when I was 12 or so years old. Today I commonly work on Knight Online private server development in my free time. So while poking through the content files for both KO and Repent Online (RO) I noticed a common trend in the binary formatted content data. A while back I write a tool to help visualize KO game objects using OpenGL, and lo and behold (with minor tweaks) I could get the RO objects to render using my KO tool! Pretty cool!

This might not be too terribly useful but then again it might be neat to add some of the weapons and objects to the KO content as a throwback to Repent Online (a.k.a Legend of Ares).

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!