Recently I've been playing around with shaders. I implemented a little palettized renderer, i.e. a shader that takes the palette and color indices and looks up the appropriate color for each pixel in the fragment stage. Right now it's being used to draw units_32.pcx as a test. I also enabled it to recolor the sprites according to a civColor parameter, right now the recoloring is pretty primitive since I don't know exactly how the original game does it (does anyone?) but it looks okay. Take a look on the ExperimentsWithMapView2 branch.
I found shaders in Godot easy to use, except for one problem. As far as I can tell, integer texture samplers are unusable. Even though Godot will let you declare (u/i)sampler2D variables in the shaders and attach textures to them, there's no way to create a compatible texture. Sampling from an incompatible texture is undefined behavior and in practice, at least on my system, returned a garbage result. References:
Some miscellaneous thoughts:
I'm tempted to charge ahead and start working on unit animation but I figured I'd create this thread first to see if you guys have any thoughts on this stuff. We never did decide exactly how animations would be implemented. Puppeteer's initial work builds off of Godot's built-in features, but I'd rather do it manually, i.e. put all the frames in a large texture and have the drawing functions pick an appropriate slice. I want to do this since it gives us fine-grained control over the animations, means MapView can remain independent of the game state, and I'm just the kind of person who likes doing everything myself. Animations need their state stored somewhere, of course, and the place for that is the engine. That makes sense since changes to the game state will often need to wait for animations to play out, for example imagine a unit attacking into a city, winning, and capturing it. The engine might know the outcome of the battle as soon as it begins but it can't process the city capture until the battle animations are finished.
So next I'll either start work on unit animations or rework terrain rendering to use MultiMeshInstance2D. Once the map is drawn with instanced rendering, it should be fast enough that we can redraw everything from scratch every frame (like a typical modern video game) and then would no longer need to call mapView.onVisibleAreaChanged whenever anything in the game world changes. Calling that function is not a problem right now but it will become one as we start to implement real gameplay.
I found shaders in Godot easy to use, except for one problem. As far as I can tell, integer texture samplers are unusable. Even though Godot will let you declare (u/i)sampler2D variables in the shaders and attach textures to them, there's no way to create a compatible texture. Sampling from an incompatible texture is undefined behavior and in practice, at least on my system, returned a garbage result. References:
- https://www.khronos.org/opengl/wiki/Sampler_(GLSL)#Sampler_types -- Says it's undefined behavior to read from a usampler2d if the attached texture format is not GL_R8UI.
- https://docs.godotengine.org/en/stable/classes/class_image.html#enum-image-format -- None of the Godot texture formats correspond to GL_R8UI. The closest is FORMAT_R8 which corresponds to GL_RED except that won't work since it's a floating point format.
Some miscellaneous thoughts:
- Looking up colors through a palette means it's necessary to turn off texture filtering. When rendering at 1.0 scale this isn't a problem, in fact it's an advantage since it prevents filtering from blurring the sprites as they're drawn, but at other scales it looks worse. It's probably feasible to implement filtering manually in the shader but probably not worth the effort compared to de-palettizing the textures as they're loaded.
- I mentioned wanting to draw the terrain with a shader attached to a mesh. I was thinking the mesh would be a large square grid holding all tiles in view, since then I've found about MultiMeshInstance2D, which I expect would be a better tool for the job. Instead of having one large mesh it does instanced rendering of many small meshes. Though I wonder if it would produce seams.
- One advantage of doing palettized rendering might be reduced load times since it means PCX files would require less processing and there's less data to move around overall.
I'm tempted to charge ahead and start working on unit animation but I figured I'd create this thread first to see if you guys have any thoughts on this stuff. We never did decide exactly how animations would be implemented. Puppeteer's initial work builds off of Godot's built-in features, but I'd rather do it manually, i.e. put all the frames in a large texture and have the drawing functions pick an appropriate slice. I want to do this since it gives us fine-grained control over the animations, means MapView can remain independent of the game state, and I'm just the kind of person who likes doing everything myself. Animations need their state stored somewhere, of course, and the place for that is the engine. That makes sense since changes to the game state will often need to wait for animations to play out, for example imagine a unit attacking into a city, winning, and capturing it. The engine might know the outcome of the battle as soon as it begins but it can't process the city capture until the battle animations are finished.
So next I'll either start work on unit animations or rework terrain rendering to use MultiMeshInstance2D. Once the map is drawn with instanced rendering, it should be fast enough that we can redraw everything from scratch every frame (like a typical modern video game) and then would no longer need to call mapView.onVisibleAreaChanged whenever anything in the game world changes. Calling that function is not a problem right now but it will become one as we start to implement real gameplay.