Archive for July, 2009

First Experiments with OpenGL

July 31, 2009

I got tired of how slow some drawing operations were recently and started to learn OpenGL. I’m rewriting large parts of the game now–about half of the C++ code will be replaced with Scheme, and the other half will be broken out and made a separate part.The game logic itself is fine and does not need to be rewritten. I’m keeping the SVG parser, but making it a separate utility that exports the data into my own format.

The rewrite will solve lots of problems and reduce dependencies on other software: I’m getting rid of Gtk and won’t require Libxml at enduser’s machines anymore. SDL is a new dependency, though.

cairo_vs_opengl

The output quality doesn’t come close to what I got with Cairo, at least not with the naive approach I tried. Maybe I did something wrong. I know it can be solved because Qt, Amanith and various other libraries can render pretty SVG graphics using OpenGL. Maybe they use fragment shaders or some other technique that’s too advanced for someone who started with OpenGL just a few days ago. I’ll get there eventually, but right now I plan to use Cairo to render textures that I can map to rectangles with OpenGL. That should give me both quality and speed, at the cost of texture memory.

The partial rewrite means I won’t have a prototype to show at the end of the summer as I had hoped, but once it’s done I should have a much better foundation and be able to work faster than before. Working with the new code base is already more fun, which may be enough to increase my output after the rewrite, since it could mean I end up putting more hours into the project.

Multiple Dispatch and Open Classes

July 13, 2009

In my last post I wrote that Scheme lets you extend the base language in powerful ways. In this post I’ll give you a concrete example of such an extension: TinyClos.

TinyClos is a minimal implementation in Scheme of Common Lisp’s object system CLOS. CLOS is different in many ways from the object systems of C++ and Java that most of us are familiar with. Two big differences are multiple dispatch and open classes.

Before I explain what multiple dispatch is I must explain what dispatch refers to. When you make a function call in any language, the compiler or language runtime must figure out which function the call refers to. This is called binding the function to an implementation. In some languages binding always occurs at compile time, in others always at runtime, and in still others sometimes at runtime and sometimes at compile time.

Some languages, such as C++, allow you to declare many functions with the same name as long as they have different parameter lists. Not only the name of the function but also the number of arguments and their types are used when binding. This is called overloading in C++ terminology. However, overloading is a compile-time mechanism, and as such only looks at the static types of the arguments. If you want to bind the call based on the run-time type of an object you have to make the function virtual.

However, virtual functions in C++ (and most other mainstream languages) only take the run-time type of the first parameter into account–the “this” pointer. This is called single dispatch, because it dispatches the call based on a single argument. As you can probably guess by now, multiple dispatch is when the run-time types of multiple arguments is used for binding. In other words, multiple dispatch is like overloading but with run-time types.

This is immensely useful in some situations, because you can decide what to do based on combinations of types, without having to sprinkle your code with instance-of tests or resorting to ugly work-arounds like the visitor pattern. For instance, to express that anti-aircraft units can attack air units but not tanks or other units types, I just specify a method can-fire-at? for the type combination (AirDefenseUnit, AirUnit) and another one for (AirDefenseUnit, Unit). The method with the most specific matching argument list will be selected, at run time.

Here’s another simple example in the syntax Bjarne Stroustrup wants for C++ (not included in the coming update of the standard):

bool canShareHex(virtual Unit u1, virtual Unit u2) {
  return true;
}
bool canShareHex(virtual GroundUnit u1, virtual GroundUnit u2) {
  return false;
}
bool canShareHex(virtual AirUnit u1, virtual AirUnit u2) {
  return false;
}
bool canShareHex(virtual SeaUnit u1, virtual SeaUnit u2) {
  return false;
}

In other words, GroundUnits can share hex with all units but other GroundUnits. This example also demonstrates the other feature I wanted to write about: open class extensions. As you can see above, the methods were not part of a class but added separately. This too is very useful, because you can’t always anticipate what people may want to do with your classes, and sometimes it’s impractical to cram everything into one class.

For example, let’s say you are writing a compiler and define classes for different kinds of expressions, like addition and multiplication and so on, reflecting the tree structure of arithmetic expressions. Since the set of operations (optimizations) you may want to perform on the tree is open ended, you wouldn’t want to make them all methods of the tree classes. It would be much cleaner to define each optimization separately, while still dispatching on the actual run-time type(s) of the tree nodes.

With Scheme, I could just download TinyClos and start using these features. They can be compiled efficiently too, though I’m not sure they are in Chicken. Knowing that it can be solved should the need arise is reassuring, though.

Further reading:

Scheme is not one Language

July 13, 2009

Considering all the hassle of using multiple languages in one project, was it really a good idea to use Scheme? I would say so.

Scheme is not like other languages. In a way, Scheme is not really one language, but a foundation for defining other languages. The semantics of Scheme were designed to enable programmers to devise their own control structures and the syntax was minimized to make it easy to parse, modify and layer your own syntax on top of. The language as a whole left so many other things unspecified that you must build your own environment on top of it unless you’re working on a small project.

One example of the power Scheme gives its users is unlimited continuations. A continuation is a point in the execution of a program which has been lifted up to the language layer, where it can be passed around or saved in variables, letting you execute it again later should you want to. That was pretty abstract so I’ll give you an example:

As you may know, functions in C programs push their return address on a stack before they are invoked. When a function returns, it pops the return address from the top of the stack and jumps to that address, which is where the call came from. The return address is the continuation of the caller (I’m simplifying here), but it doesn’t exist as a first-class entitiy in the C language, so we cannot see it or refer to it. In Scheme you can. Moreover, you can call a continuation as many times as you wish, and you can ask for the current continuation at any point, not just at call sites.

This is not terribly useful in day-to-day coding, but it is essential if you want to make your own language. Using continuations, you can implement exception-handling mechanisms like try-catch without resorting to low-level hackery (and things like loops with complicated exit logic and light-weight threads, too).

What all this comes down to is that Scheme is a very good environment for language experimentation, which has resulted in a lot of people hacking on these kinds of things. Consequently, you can go to the web page of your Scheme environment and download language extensions others have written that add or alter fundamental parts of the language; They may for instance add object-oriented constructs and run-time dispatch. Mainstream languages are much less malleable.

In summary, I think the ability to download language extensions and, so to speak, assemble my own language from pieces written by others has been a huge advantage. That, and the ability to make changes to the program as it’s running has been more than enough to justify the hassle.

Extended Scheme

July 13, 2009

I have now started to use the preprocessor on a small scale. All it does currently is translate dotted field expressions into the syntax TinyClos expects. I actually found out you could type  (slot@ foo x y) instead of (slot-ref (slot-ref foo ‘x) ‘y), but foo.x.y is better still, so I don’t regret writing the preprocessor. Besides, this way I can easily add more “macros” that don’t conform to Lisp’s grammar later.

This is not really a new language, just an extended variant of Scheme, but I still need a name for it to be able to talk about it.

Extended Scheme is too long a name, so I thought about shortening it to Extreme, but that doesn’t send the right message. Instead I’m calling it Steme (pronounced like steam), which you can interpret either as Ekstended Scheme or Stefan’s Scheme. If this ever grows into something serious I need to find another name for it.

Even Bigger Map

July 5, 2009

I increased the size of the map again. I’ve included a scaled down version (15% of the normal view size) of the map as it looks right now below. As you can see it’s far from complete, but I think this will be the final extent of this scenario.

I’m not sure exactly what the scenario will be, but something like an attack on Denmark with Sweden getting drawn in. The south of Sweden was pretty much undefended (we had prepared for the war between the Soviet Union and Finland to spread to the north of Sweden, not for war with Germany), but the Swedish navy could probably have helped the Danes a bit.

sweden

I haven’t done much programming this weekend, but I experimented some more with the Antlr parser generator. I can now recognize field access expressions like foo.bar.baz and transform them into (slot-ref (slot-ref foo ‘bar) ‘baz). I detest having to type all that garbage just to access some fields, so anything that can rid me of that is welcome even if it means writing a preprocessor. More about that later. I’ll need to work more on it before I have something that can be used.