Category Archives: GameDev

A flexible PLY loader for Evolution War r71

I’ve cited Evolution War for the first time on this blog in my previous post, today I want to celebrate my return to SVN committing after a very long time. ๐Ÿ˜€

PLY Export

Revision 71 adds the support for a real Stanford PLY parser and loader, while the one I coded for my graphic class library is very primitive, expecting a hard-coded order for data, this one shouldn’t have any kind of problem with every well-formed PLY file.

For example, while the hard-coded loader can only accept a file like this:

ply
format ascii 1.0
comment Created by Blender3D 249 - www.blender.org
element vertex 4
property float x
property float y
property float z
property float nx
property float ny
property float nz
element face 3
property list uchar uint vertex_indices
end_header
1.000000 2.000000 3.000000 -4.000000 -5.000000 6.000000 
-1.000000 -2.000000 -3.000000 4.000000 5.000000 6.000000 
1.000000 2.000000 3.000000 -4.000000 -5.000000 6.000000 
-1.000000 -2.000000 -3.000000 4.000000 5.000000 6.000000  
3 0 1 2 
3 1 3 2 
3 4 2 1 

the parser loader can load even something like this:

ply
format ascii 1.0
comment Created and shuffled by hand
element face 3
property list uchar uint vertex_indices
element skipme 3
property float skipfirst
property float skipsecond
element vertex 4
property float z
property float nz
property float y
property float ny
property float nx
property float x
end_header
3 0 1 2 
3 1 3 2 
3 4 2 1 
0.0 0.0 
0.0 0.0
0.0 0.0 
3.000000 -6.000000 2.000000 -5.000000 -4.000000 1.000000 
-3.000000 6.000000 -2.000000 5.000000 4.000000 -1.000000 
3.000000 -6.000000 2.000000 -5.000000 -4.000000 1.000000 
-3.000000 6.000000 -2.000000 5.000000 4.000000 -1.000000 

But one of its most important feature resides in the ability to correctly load binary PLY files! ๐Ÿ™‚

Related to it there’s a bug I would like to share with you together with the fix:

istream& istream::read (char* s, streamsize n);
[...]
ifstream ifs;
unsigned int *uIndices;
[...]
ifs.read((char *) uIndices+(j*3), sizeof(unsigned int) * 3);

The read() method only accepts char pointers, so uIndices is casted, but the precedence goes to casting and not to native unsigned int pointer arithmetics, leading to catastrophic effects! ๐Ÿ˜ฎ

The fix was as simple as the bug was subtle:

-ifs.read((char *) uIndices+(j*3), sizeof(unsigned int) * 3);
+ifs.read((char *) (uIndices+(j*3)), sizeof(unsigned int) * 3)

PySoy, a month after

A bit more than a month of SoC work has passed now, PySoy is alive and getting better day after day, and since some days you can even get a taste of it!
After the release of the GPLv3 our SVN repository has been opened to the public, read more on Arc’s blog.

Textured Pyramid

About the point of view of my application the project is proceeding well and we can show you a textured pyramid at last (as usual I have to thank Arc for the huge help ๐Ÿ˜‰ )! ๐Ÿ™‚

I’ve left my normal mapping experiments behind for a moment (after having discovered how to create correct object space normal maps in Blender with the “six colored lights” hack ๐Ÿ˜€ ), and I’m now working on the lighting system which will represent the base for every non trivial texturing technique.

Much more is yet to come, stay tuned!

A slighty smarter setup.py for PySoy r64

While working on PySoy I was really disappointed by the policy adopted by the setup.py script, anytime I launched it the result was a recompilation of *all* the sources, this was really annoying and slow.

Python transparent logo

What came after was just that I decided to hack it a bit and make it behave more like a standard build tool, that is to control the modification time of a source file in order to choose whether it is updated or need a fresh compilation.

The new policy for the script is very simple, but useful enough to save plenty of time.
It is aware of the following cases (thank you Arc for tips on how to deal with the last one):

  • a .c file is missing, pyrex should compile the .pyx file
  • a .pyx file is newer than the corresponding .c file, an update is needed
  • a .pxd file is newer than any .pyx file, a global recompile is needed

The last point is not optimal, of course, but it’s a lot simpler than specifying all the .pxd dependecies for any .pyx file, and, anyway, quite close to being optimal, because of the thick web of cross dependencies which actually exists in PySoy.

Here is the piece of code which performs the magic:

# Convert Pyrex sources to C if using Trunk
if version == 'Trunk' :
  import os
  from stat import *
  from Pyrex.Compiler import Main

  options = Main.CompilationOptions(defaults=Main.default_options, include_path=include_path)

  newestpxd = 0
  for dir in include_path:
    for pxdsource in os.listdir(dir):
      pxdsource_path = (os.path.join(dir, pxdsource))

      if os.path.isfile(pxdsource_path) and pxdsource.rsplit('.', 1)[1] == 'pxd':
        if os.stat(pxdsource_path)[ST_MTIME] > newestpxd:
          newestpxd = os.stat(pxdsource_path)[ST_MTIME]

  for pyxsource in pyrex_sources:
    compilation_needed = False

    if os.path.isfile(pyxsource.rsplit('.', 1)[0] + '.c'):
      ctime = os.stat(pyxsource.rsplit('.', 1)[0] + '.c')[ST_MTIME]
    else:
      ctime = 0

    if newestpxd > ctime:
      compilation_needed = True
    elif os.stat(pyxsource)[ST_MTIME] > ctime:
      compilation_needed = True

    if compilation_needed:
      Main.compile(pyxsource, options)

Well, actually Arc commited r65 too, simplifying the conditionals of the script even more, but this is another story. ๐Ÿ™‚
Anyway, I hope to have made another little step into making the life of our team a bit simpler. ๐Ÿ˜‰

My Summer of Code begins with PySoy r44

Summer of Code has started just today (even if currently it is only a “Spring of Code” ๐Ÿ˜€ ) but a little contribution of mine has already made his way inside the SVN repository of PySoy.

But let’s start from the beginning…
After having shown to Arc an early draft of an UML class diagram for the current code I decided to come back to work on some test code I had written in the afternoon.
It was just a classical spinning cube demo to actually compare PyOpenGL versus Pyrex speed, I don’t report the results here because they are quite identical, if I haven’t done any mistake it should be the absolute minimum complexity of the code which actually determined this result.
Anyway, even without this proof I firmly believe in the power and speed of Pyrex. ๐Ÿ˜‰

Going back to my commit, my changes affected a small yet important area of the code, I think we will remember it in the future. ๐Ÿ™‚

BEFORE
In src/windows-glx/soy.windows.pyx:

gl.glClear(0x4100)

AFTER
In include/gl.pxd:

[...]
# Constants
  ctypedef enum:
    [...]
    # glPush/PopAttrib bits
    GL_DEPTH_BUFFER_BIT
    GL_COLOR_BUFFER_BIT
[...]

In src/windows-glx/soy.windows.pyx:

gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)

Was it better before or now? ๐Ÿ˜‰

Back to work, Mars r622

Yesterday my exams session finally ended, I’m really satisfied about the results achieved, but during the studying period I was eager to get back to coding…

Today I fixed bug #0000008, a fastidious one which caused program termination if the user tried to take a screenshot after having deleted the hidden directory inside his home where settings and images are saved by default. ๐Ÿ™‚

Mars 0.1.1 2nd

The first thing I thought was that I was missing a fopen() return code check, but, fortunately for my reputation, it wasn’t the case. ๐Ÿ˜‰

// Opening output file
if((fp = fopen(filename, "wb")) == NULL)
{
  throw Exception("Screen", "fopen error");
  return -1;
}

The problem, as the shell output was suggesting, was related to the exception system: the fopen() exception was never caught.

Just changing this:

case SDLK_F4:
  screen->TakeScreenshot();
  break;

into this:

case SDLK_F4:
  try
  {
    screen->TakeScreenshot();
  }
  catch(Exception e)
  {
    e.PrintError();
  }
  break;

fixed everything.

Have a look at r622 log and at mars.cpp changes and remember to catch all the exception you may throw. ๐Ÿ˜‰

Mars r594 and the vflip hack

For my first entry on this blog let me tell you a tale, it’s about OpenGL framebuffer and vertical flipping…

Mars 0.1.1 1st

Once upon a time a little fool called Encelo used to perform, in a little testing program, a vertical flip of the entire OpenGL framebuffer this way:

if(_flags & SDL_OPENGL)
{

  GLvoid * pixels;

  pixels = (GLvoid *) malloc(_width * _height * 4);
  glPushAttrib(GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_PIXEL_MODE_BIT);
  glReadBuffer(GL_FRONT);
  glReadPixels(0, 0, _width, _height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  glDrawBuffer(GL_BACK);
  glRasterPos2f(-1.0f, 1.0f);
  glPixelZoom(1.0f, -1.0f);
  glDrawPixels(_width, _height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  glReadBuffer(GL_BACK);
  glReadPixels(0, 0, _width, _height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  glPopAttrib();
  output_surf = SDL_CreateRGBSurfaceFrom(pixels, _width, _height, 32, _surface->pitch, rmask, gmask, bmask, amask);
}

It wasn’t really bad, it performed some interesting tricks with buffers, and, as a matter of fact, Encelo was really proud of this implementation. ๐Ÿ™‚
But… it didn’t work on Mars. Yes, no matter how much Encelo tested, changed, and tested again, it simply didn’t work on anything else than the original testing program.
A decision had to be taken soon, to persevere or not to persevere? That was the question.

Encelo chose not to persevere and to try a completely different approach… memcpy() flipping! ๐Ÿ˜€
Yeah, something as simple, elegant, fast and smart as this:

if(_flags & SDL_OPENGL)
{
  int row, stride;
  GLubyte * swapline;
  GLubyte * pixels;

  stride = _width * 4; // length of a line in bytes
  pixels = (GLubyte *) malloc(stride * _height);
  swapline = (GLubyte *) malloc(stride);

  glReadPixels(0, 0, _width, _height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

  // vertical flip
  for(row = 0; row < _height/2; row++)
  {
    memcpy(swapline, pixels + row * stride, stride);
    memcpy(pixels + row * stride, pixels + (_height - row - 1) * stride, stride);
    memcpy(pixels + (_height - row -1) * stride, swapline, stride);
  }

  output_surf = SDL_CreateRGBSurfaceFrom(pixels, _width, _height, 32, _surface->pitch, rmask, gmask, bmask, amask);
}

This story is true, and happened exactly a month ago, on the 30 of November 2006.
As a proof have a look at r594 log and at Screen.cpp changes. ๐Ÿ˜‰