I might've lost the interest in pursuing my own lisp... for a while. I'll return to it by the autumn though. For now I've resumed my work on the memapper's descendant that I'm hacking with Qt.
Like I have written previously, scripting is hard. I was to write my own lisp, with its own repl socket server and emacs mode resembling cider or slime. But achieving that would've taken more work than I had interest for. I still needed a way to script my engine though. Back in March's status update I said I'd embed a common lisp (Embeddable Common-Lisp to be precise) once I'd lost interest in rolling my own. I lied. I embedded a scheme instead.
I used the infamous Guile Scheme. I realise, with libguile being a gnu project, all work done in or with this project must be GPL from this day to the heat death of the universe. I doesn't matter. I get a lisp interpreter with an excellent C foreign function interface, a high quality repl server that's ran by invoking my binaries with the "--listen" parameter, and an excellent emacs mode (scheme-mode + geiser) for free. Being required to publish the source code is a small price to pay for that, especially since I think the free version of Qt5 requires the same too.
But now that I've got windowing mostly on qt (C++), rendering the final game on opengl (C), and scripting on a real, interactive language (scheme), I have a problem. How the hell am I to make the windowing and rendering scriptable? In theory, writing a libguile wrapper for opengl shouldn't be too hard, but if I want to let the scheme repl edit the windowing system, I have to be prepared for a lot of pain. Making C functions visible to the scheme is trivial, making C++ functions and methods visible to the scheme requires wrapping everything behind a C api that the programmer is supposed to make visible to scheme
Well, I don't really have those implemented. I've only hacked enough OpenGL, Qt and guile to know that this conglomeration might work and be a bit more performant than the old count-everything-on-jvm-on-cpu engine was. But now, let me write a spec on datastructures, apis and stuff
Datastructures, apis and stuff
Maps and tiles
Disclaimer: None of the following is certain to be implemented. If I deem so, I might go back to the "registry" approach where every object is in a global std::unordered_list<SCM /*symbol */, game_object> and every game_object has a :parent link
In the heart of this epic project is to be able to make a 2D scene, with a tilemap behind everything. This tilemap must support easy appending and prepending of tiles, both horizontally and vertically. This might one of the few problems that are actually easier to solve in the imperative C++ land than it used to be in the clojure land, considering that last time I was solving the resizing of the tilemap I was EVERYTHING MUST BE STATELESS - noobing.
Anyway, the maps consist of layers and the layers consist of these:
class tile { public: int x = 0; int y = 0; int tileset = 0; int rotation = 0; };
Layers are an abstraction that make stacking tiles on top of each other easier. Layers have a name (that matches the guile's keyword-regex) and an opacity (an unsigned char, where 0 = completely transparent, 255 = completely opaque). They also have a writeable fragment shader references, in case simple alpha blending isn't enough for the user
Tiles are stored in a 2D std::list, and the index pair to this list equals to the tile's world coordinates. Tile::x, Tile::y and Tile::tileset point to a tileset's tile. Tileset-property might be a guile keyword instead of an int. Depends on whether the tilesets need human-readable ids. Rotation means how many times the tile will be rotated 90°
Maps have a parameter that defines the supported tile texture width and height. They aren't hard coded 50x50 like they used to be, mostly because that's a really shitty texture size to use from the opengl, and it can't hurt to make that parametrizable.
Tilesets consist of a bit of metadata, like human readable id, dimensions of the tiles, width, height etc., and a 2D array of opengl textures. These arrays will be made visible to the fragment shaders in a way I can't say to understand yet. Fragment shaders used to render a tile will also get two parameters (or uniforms? I guess the shader parameters are simpler and more performant to use) that can be used as index to this 2d tileset uniform.
Sprites and animations
Sprites know their location in world, in tile coordinates. They have their OpenGL-textures, and a reference to both fragment- and vertex-shaders. They know also their angle in degrees. Animations are a collection of sprites. As metadata they have the index to the current frame, boolean property playing?, last-updated thet gets system's currentMilliseconds on the frame change and the age of a frame.
Sprites and animations have a dynamically defined event system (which is to say, I have no idea which kind of event's they'll need so I'll avoid specifying it too much yet) operable from the guile repl
Other resources
The clojure-mapper had sprites, animations, tilesets, maps, and scripts as resource types it operated on. Scripts will be similar on the new Qt/Guile - world: I'll try to extend the geiser-server so that the developer can request a certain script to emacs, evaluate forms in the context of the game and the editor and save the contents of the script buffer back to the editor's script resource.
I'll be introducing 2 new resource types: :fragment and :vertex shaders. They are glsl - scripts you can request from the server the same way as you'd request regular guile scripts. Then, on the property editor of any drawable resource there will be two dropdowns that list ids of every loaded glsl script. Changing their values will change the way the object is rendered.
There will be play/pause/stop - buttons, but you're free to change the guile bindings runtime if & when you need to.
And the apis?
I hoped you'd never ask, for I'm certain I'll screw these up a couple of times before the release.
Every map- and layer-operation has to be callable from guile. Setting arbitrary object's arbitrary property (those that will be editable with the property editor in the editor gui + events) should be easy. Rendering should be customizable enough by editing object's shaders. Most of the game object manipulation should be easy to write on top of the object's properties.
Editor tools should be written in guile.
I don't think I'll be able bring the whole of Qt through the CFFI bridge. Thus editing the window definitions has to be done with C++, at least until I get arsed enough to write some sort of S-expr based UI language.
Postscript
When should you expect results? Based on the experiences with the multitude of jvm based *mappers, probably by 2022. I'll keep you posted though, and unless I lose my sanity and continue pursuing higher education in a real university, I have a bit more time to put on programming now. Work stress has stayed in the office in a way that school stress never did in polytechnic during 2013-2016.