Tag Archives: opengl

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).

Arx Libertatis

I have been digging around lately for feature-length games with readily available source code. I really enjoy looking back over great source code and trying understand the underlying structure. Most recently I learned about Arx Fatalis, which is now being developed further by passionate hobby developers under the project name Arx Libertatis. I never even knew this game existed!

Arx Fatalis was originally developed by Arkane Studios, the developers for the more recently popular game Dishonored. I have been making my way through this game in amazement. I am a huge fan of the King’s Field series of game and Arx Fatalis seems to capture a lot of the feelings of isolation that are synonymous with the King’s Field series. The fact that this entire project is open source and under the GNU Version 3 license is just icing on the cake. If you have time, follow this video tutorial and get a version of the source code up and running for yourself! At the very least, get the game off Steam or GOG and enjoy this wondrous experience!

Obj Mesh Importer

Today I am sharing a bit of code I use to read very basic vertex and texture coordinate information from an obj file. The .obj format is explained in detail here. It is pretty well supported across most 3D-modeling software and is currently my go-to format for 3D mesh information.

In order to keep the vertex information file format agnostic (in case you decide to add other file formats later), I designed an intermediate layer of abstraction between the .obj file and OpenGL.

typedef struct {
	GLfloat* pos;   // (x,y,z)
	GLfloat* tex;   // (u,v)
	GLfloat* norm;  // (x,y,z)
	GLsizeiptr num; // total num of verts
} VertInfo;

typedef struct {
	GLuint* pos;    // 3 pos inds per
	GLuint* tex;    // 3 tex inds per
	GLuint* norm;   // 3 norm inds per
	GLsizeiptr num; // num of triangles
} IndInfo;

This works well for my current needs but there are a few simplifications I have made about the obj. An obj file may contain an optional coordinate (x, y, z [,w]) but I am ignoring that it might exist. Another simplification is that the texture coordinates have two variables, not three. These are reasonable assumptions to make since they basically ignore optional arguments that I won’t use anyways. The bigger assumption is that all the face definitions are triangles. This isn’t the case, even with the files I’ll be using, but OpenGL is really fast with triangles to I’m converting the quadrilaterals to triangles. This current iteration only accepts quadrilaterals but triangle faces would be trivial to add.

Here is the actually importer.

void loadObjFile(VertInfo** verts, IndInfo** inds, const char* filename) {
	if(*verts) {
		free((*verts)->pos);
		(*verts)->pos = NULL;
		free((*verts)->tex);
		(*verts)->tex = NULL;
		free((*verts)->norm);
		(*verts)->norm = NULL;

		(*verts)->num = 0;
	} else *verts = (VertInfo*) calloc(0x01, sizeof(VertInfo));

	if(*inds) {
		free((*inds)->pos);
		(*inds)->pos = NULL;
		free((*inds)->tex);
		(*inds)->tex = NULL;
		free((*inds)->norm);
		(*inds)->norm = NULL;
		
		(*inds)->num = 0;
	} else *inds = (IndInfo*) calloc(0x01, sizeof(IndInfo));

	size_t n = 0;
	char** lines = NULL;

	if(filename==NULL) return;
	FILE* fp = fopen(filename, "r");

	do {
		char c;
		int colInd = 0;

		lines = (char**) realloc(lines, ++n*sizeof(char*));
		lines[n-1] = NULL;

		do {
			fread(&c, sizeof(char), 1, fp);

			// NOTE: strip newlines
			if(c!='n') {
				if(colInd>0) {
					// NOTE: strip extra spaces
					if(!(lines[n-1][colInd-1]==' ' && c==' ')) {
						lines[n-1] = (char*) realloc(lines[n-1], ++colInd*sizeof(char));
						lines[n-1][colInd-1] = c;
					}
				} else {
					lines[n-1] = (char*) realloc(lines[n-1], ++colInd*sizeof(char));
					lines[n-1][colInd-1] = c;
				}
			}
		} while(c!='n');

		// NOTE: end with a null terminator
		lines[n-1] = (char*) realloc(lines[n-1], ++colInd*sizeof(char));
		lines[n-1][colInd-1] = '';

		if(lines[n-1][0]=='#') {
			// NOTE: ignore comments

			free(lines[n-1]);
			lines[n-1] = NULL;

			lines = (char**) realloc(lines, --n*sizeof(char*));
		} else if(lines[n-1][0]=='') {
			// NOTE: ignore blank lines

			free(lines[n-1]);
			lines[n-1] = NULL;

			lines = (char**) realloc(lines, --n*sizeof(char*));
		}
	} while(!feof(fp));

	fclose(fp);
	fp = NULL;

	size_t numPos = 0;
	size_t numTex = 0;

	int i;
	for(i=0; i<n; i++) {
		// NOTE: parse the file

		printf("%sn", lines[i]);

		if(lines[i][0]=='v' && lines[i][1]==' ') {
			// NOTE: parse the vertex and add it to the array

			(*verts)->num++;

			numPos += 3;
			(*verts)->pos = (GLfloat*) realloc((*verts)->pos, numPos*sizeof(GLfloat));

			GLfloat x = 0, y = 0, z = 0;
			sscanf(lines[i], "v %f %f %f", &x, &y, &z);
			printf("[%f, %f, %f]n", x, y, z);

			(*verts)->pos[numPos-3] = x;
			(*verts)->pos[numPos-2] = y;
			(*verts)->pos[numPos-1] = z;

		} else if(lines[i][0]=='v' && lines[i][1]=='t' && lines[i][2]==' ') {
			// NOTE: parse the texture coordinates

			numTex += 2;
			(*verts)->tex = (GLfloat*) realloc((*verts)->tex, numTex*sizeof(GLfloat));

			GLfloat u = 0, v = 0;
			sscanf(lines[i], "vt %f %f", &u, &v);
			printf("[%f, %f]n", u, v);

			(*verts)->tex[numTex-2] = u;
			(*verts)->tex[numTex-1] = v;
			
		} else if(lines[i][0] == 'f' && lines[i][1] == ' ') {
			// NOTE: parse the face elements

			int c, num = 0;
			GLuint* indices = NULL;

			for(c=1; c<strlen(lines[i]); c++) {

				// TODO: will have to handle the case when normals are left out
				// or when texture indices are left out

				// TODO: currently I am assuming 4 indices per face which makes
				// two triangles (i.e. 6 elements)

				if(lines[i][c]==' ') {
					num += 3;
					indices = (GLuint*) realloc(indices, num*sizeof(GLuint));

					GLuint vi = 0, vti = 0, vni = 0;
					sscanf(&lines[i][c], " %d/%d/%d", &vi, &vti, &vni);
					printf("[%d, %d, %d]n", vi, vti, vni);

					indices[num-3] = vi;
					indices[num-2] = vti;
					indices[num-1] = vni;
				}
			}

			if(num==4*3) {

				// TODO: currently only setting the position index but I need
				// to set them all eventually

				(*inds)->num += 6;

				(*inds)->pos = (GLuint*) realloc((*inds)->pos, (*inds)->num*sizeof(GLuint));
				(*inds)->tex = (GLuint*) realloc((*inds)->tex, (*inds)->num*sizeof(GLuint));
				(*inds)->norm = (GLuint*) realloc((*inds)->norm, (*inds)->num*sizeof(GLuint));

				int pos0 = indices[0];
				int pos1 = indices[3];
				int pos2 = indices[6];
				int pos3 = indices[9];

				(*inds)->pos[(*inds)->num-6] = pos0-1;
				(*inds)->pos[(*inds)->num-5] = pos1-1;
				(*inds)->pos[(*inds)->num-4] = pos2-1;
				(*inds)->pos[(*inds)->num-3] = pos2-1;
				(*inds)->pos[(*inds)->num-2] = pos3-1;
				(*inds)->pos[(*inds)->num-1] = pos0-1;
			}

			free(indices);
			indices = NULL;
		}

		printf("n");
	}

	for(i=0; i<n; i++) {
		free(lines[i]);
		lines[i] = NULL;
	}

	free(lines);
	lines = NULL;
}

I added printf() outputs for debugging purposes. This function will build the structs and set the pointers so that we can then work on this abstraction layer to build the information OpenGL needs to render the object.

To create the structs just place the following calls.

IndInfo* inds = NULL;
VertInfo* verts = NULL;

loadObjFile(&verts, &inds, "test.obj");

The neat thing about this current setup is that the index information can be passed directly to OpenGL as an element buffer.

// NOTE: allocate a GPU buffer for the element data
GLuint eleBuffer;
glGenBuffers(1, &eleBuffer);

// NOTE: bind to the element buffer so that we may send our data to the GPU
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eleBuffer);

// NOTE: send our element data to the GPU and set as STAIC
glBufferData(GL_ELEMENT_ARRAY_BUFFER, inds->num*sizeof(GLuint), inds->pos, GL_STATIC_DRAW);

On the other hand, the OpenGL vertex information is highly specific to the type of effects you plan to render with your objects. Here I’m just using the position and texture information but if you plan to add lighting or some other type of rendering effect you’ll have to customize the OpenGL vertex information to fit your needs.

GLfloat glVerts[5*verts->num];
memset(glVerts, 0x00, 5*verts->num*sizeof(GLfloat));

int tempInd = 0;
for(; tempInd<verts->num; tempInd++) {
	glVerts[5*tempInd+0] = verts->pos[3*tempInd+0];
	glVerts[5*tempInd+1] = verts->pos[3*tempInd+1];
	glVerts[5*tempInd+2] = verts->pos[3*tempInd+2];

	glVerts[5*tempInd+3] = verts->tex[2*tempInd+0];
	glVerts[5*tempInd+4] = verts->tex[2*tempInd+1];
}

// NOTE: allocate an array buffer on the GPU
GLuint verBuffer;
glGenBuffers(1, &verBuffer);

// NOTE: bind to the array buffer so that we may send our data to the GPU
glBindBuffer(GL_ARRAY_BUFFER, verBuffer);

// NOTE: send our vertex data to the GPU and set as STAIC
glBufferData(GL_ARRAY_BUFFER, 5*verts->num*sizeof(GLfloat), glVerts, GL_STATIC_DRAW);

And that is pretty much it! Now you can create complicated meshes using something like Anim8or or Blender and render them with your own OpenGL implementation.