Landscape Creator

It’s been too long since I last posted about something fun programming, so today I’m going to post about something fun with programming again. [As you may recall][lastpost], I’ve recently posted about starting development on a level editor for my new railroad app. Over the past few weeks, it has gained a lot of traction, which can be best shown in a screenshot.

![Of course, a screenshot is not always an option.][screenshot]

What can you see here? Three basic tools (raise landscape, lower landscape and paint solid color) are already working. The two geometry tools are not working as flawlessly as they should, but they do work. Solid color painting happens completely on the GPU[^1] which is not a huge advantage but sure sounds cool. There is full undo and redo, unlimited other than by RAM. There is full saving and loading. Finally, texture painting is coming along, at least the screen for selecting a texture works.

Over this time, I’ve learnt some very interesting things, which I thought I might share. Note that this is going to be very technical.

Do not Garbage Collect

I think it cannot be stressed enough: If you use any OpenGL code, then do not use the Garbage collector you can use in Mac OS X 10.5 in your application. It actually works perfectly as long as your code is correct, but you can’t use the OpenGL Profiler that way. OpenGL Profiler is probably the best way to figure out what the hell your drawing code is doing and why, and it’s too valuable to give up for decadent luxury like automatic memory management.

Core Data can be tough

This is the first time I’m using Core Data at all. It’s a technology in Mac OS X to make saving and loading of documents both easier and more powerful and I hoped it would save me some work. As it turns out, there are things that Core Data really likes doing, and there are things that it take some work and there are things that are just not possible. Storing an OpenGL texture in Core Data while editing this on the GPU is admittedly a niche task, but it’s just something that will absolutely not work with it. Now, I’m using a package format (i.e. a folder that appears as a file to the user), storing some Core Data data as well as plain height field and texture data. Again, something that took some work, but Apple provides [some very helpful sample code][NSPersistentDocumentFileWrappers] here.

OpenGL actually kind of sucks

OpenGL is great for a lot of work, but it’s easy to get it out of it’s comfort zone as well. A fun thing are OpenGL contexts, which can be thought of as the areas you draw to plus associated resources. It’s very important to have one, but it tends to float in the background, harldy ever seen or interacted with. That works great until the very moment you have two. I have two per document (the image browser for the textures takes one too. Not sure why, but it does) and potentially unlimited documents. Inside the designated drawing routine, you can count on your context being the right one. Outside, not so much, leading to much fun when you draw something or set some state at a completely different point than you thought.

Of course, you can manually switch the context. You just have to make sure that every piece of code that wants to make OpenGL calls knows that context and sets it before doing anything — and, of course, sets the one previously used again after that. There is, however, an easier way.

Apple offers [OpenGL macro headers][oglmacros], which basically means normal OpenGL functions, except the context is now a variable you provide. The idea is that this saves some speed because it is no longer required to look up the correct context, but you can also use it to keep from switching to a different context completely. You simply add the required cgl_ctx variable to your class like this:

@interface SomeGLUsingCode : NSObject
{
    CGLContextObj cgl_ctx;
    /* ... */
}
- (id)initWithContext:(CGLContextObj)context;
@end

And in the implementation, the first thing you do is set it:

- (id)initWithContext:(CGLContextObj)context;
{
    if (![super init]) return nil;
    cgl_ctx = context;
    ...
    return self;
}

Now all OpenGL calls made from that object will go to the right context. One point, though: This only works if you call the OpenGL functions yourself, not if you use some library that calls OpenGL for you - notably, it already fails with GLU. However, it does seem that it’s legal to use this as a replacement for context switches, [Apple sample code uses it this way][oglmacrosample]. I just think it’s sad that there’s no more direct way to pass the context to an OpenGL function.

The Future

Now, next up is getting texture drawing in, then having an exporter for iPhone, then drawing it on the device. After that, rails and static objects, possibly special cases for trees and the like. Water might come later if I’m really bored.

By the way, I do not currently plan to make this level editor available to anyone but myself, although I won’t completely rule it out either, especially if I ever decide to make it work with maps for [Hubschrauber][hubi] as well. I do guarantee that there will not be a Windows version ever, unless one of the Cooca-To-Windows-Projects starts becoming truly amazing over night.

[^1]: Actually, old versions needed for undo are stored in the normal program. It makes a few things easier if I do it that way.

Written on June 10th, 2009 at 10:23 am

3 Comments

  1. Posted 15 June 2009

    Björn

    Wird es auch eine Windows-Version geben?
    xD Schertz bei Seite...

    Aber wie sieht es mit Linux aus ?!?

    Das Programm sieht durchaus interessant aus und erinnert ein wenig an den "RollerCoaster Tycoon"-Level-Editor.
    Wofür ist das A in der Menüleiste zuständig? Kann man damit Text auf die Map schreiben?
    Was genau für ein Sinn hat es einen Leveleditor auf einem Mobiltelefon aufzurufen?

    Durch schütteln kann das iPhone doch Zufallszahlen erzeugen, oder? Die kann man dann nutzen um das Gelände zufällig erstellen zu lassen. Das würde ich lustig finden ^^

    Kann Hubschrauber denn die erstellten Maps auch auslesen und wenn ja, funktioniert das auch unter Windows (oder stirbt die komplette Hubschrauberreihe für Windows)?

    Alle Map-relevanten Dateien befinden sich dann in dem "Ordner"? Das wäre besser als bei Half-Life wo man Texturen und Objekte extra beisteuern muss...

    Und wo wir gerade bei Valve sind, das Steinpflaster und die Wiesen sehen verdächtig nach Half-Life Source aus?!?

  2. Posted 16 June 2009

    Torsten (admin)

    Linux sieht schlecht aus. GNUStep läuft unter Linux vielleicht besser, aber ich verwende gerne und viel modernere Features von Cocoa, die GNUStep eventuell noch nicht hat. Abgesehen davon ist das Userinterface von GNUStep grauenhaft.

    Das A öffnet die Schriftarten-Palette, die ich hier nicht verwende (ist nur ein Standardfeature, ich war zu faul es zu entfernen). Analog tut der Drucker auch nichts.

    Der Leveleditor wird nicht auf dem iPhone aufgerufen, ich erstelle damit auf dem Mac meine Level, die ich dann in ein optimiertes Format konvertiere und dann auf dem Gerät nur anzeige.

    Hubschrauber kann die Maps derzeit nicht auslesen. Ich müsste dafür idealerweise sowohl das Programm als auch Hubschrauber umschreiben (was ich aber gut machen könnte). Das würde ich aber dann auch auf Windows portieren.

    Ja, alles in einem Ordner, so lange es im Editor bleibt. Die Steine und Wiesen sind von http://mayang.com/textures/ (hm, Links in den Kommentaren wären eine interessante Idee...), so weit ich weiß hat das nichts mit Valve zu tun.

New comments can no longer be posted because it got to annoying to fight all the spam.