Ladies and gentlemen, friends,
it has been a while since I wrote anything in here, and what better time could there be than on April fool’s? As many of you might remember, it’s not just the 1st April, it is NUTS birthday. It has been five years since I released NUTS 0.0.1 and many things have progressed since. One of them are my other NewGRFs that took place after NUTS. The latest one – BRIX – will now share birthday with NUTS as I am releasing BRIX 0.0.1 right now. I will also talk about some more things later in the article…
NUTS Unrealistic Train Set 0.7.9
The last update happened when I was adding 32bpp/ExtraZoom graphics to it. That was probably sometime in 2014 if I remember correctly, which is insanely long time ago to me. Over time, I have realized that converting it all into 32bpp would be absolutely insane amount of work. On top of that, the already present 32bpp is of very poor quality and would require serious further attention. This brought me to the first major change of removing 32bpp except slugs (you can always turn your blitter to 8bpp if you really want them in 8bpp anyway). I might create a separate newgrf which will be NUTS-with-32bpp, with the same grf ID, just only obtainable from devzone, not from BaNaNaS.
Other than that, I added a few parameters for disabling signals as NUTS defines them by railtype, which BRIX can’t overwrite, so now you can choose which signals you want, or even disable them if you use NUTS alone.
I also fixed a couple of old bugs some looooong time ago, but I don’t even remember which.
If you reported something to me in the past for the latest version, please re-check if you can remember what it was, so I could fix it.
Future of NUTS
Mainly with the 32bpp-out decision I started to realize what NUTS should truly be and what should it’s final form look like.
I consider it feature-complete now, and the only things I would like to add/change in the future are related to cargo sprite definitions. Vast majority of NUTS sprites are wagons. Simply because there are about 70 cargoes in OpenTTD, and NUTS defines graphics for basically all of them (in any game you won’t ever have 2 same graphics for 2 different cargoes – this isn’t true anymore since some from FIRS 2.0 are missing, but it’s the aim), combine this with about 10 wagon classes, 3-10 loading stages, 8 rotations and you get insane amount of sprites.
I would like to simplify that by using sprite layering, and 8bpp mask recolouring by code. That way, I can just define recolour patterns, define empty sprite, define sprite of the cargo alone, and done. Of course this is still a really big amount of work, but if anything, I will put my energy towards this in NUTS, mainly because it is the only thing that will need upkeep as others add new cargoes, and such rewrite could both make it better manageable, and reduce the filesize of NUTS as a nice bonus.
Overall, NUTS has made huge progress over the years, and in the last 3 years of no updates, I have been quite happy with it. You probably know that I number my releases like 0.7.9 is 79th release, so I won’t declare it 1.0, but 🙂 . Thank you everyone for your support of this train set, be it discussions I had with you about it, any form of feedback, using it on servers, …
A long time ago I have made RAWR – a NewGRF aiming to replace all base set sprites. When releasing 0.0.1, I wasn’t too happy with it and my general mindset was “I can always fix it later”. I continued working on it, and I was almost done with version 0.0.2, but even after basically completely reworking everything ground-up apart from some template 3D models and the code, there were still so many issues that it just became unbearable pain and I just decided to stop. At the same time I didn’t really like how it looks, even if it didn’t try to look realistic, it used real-life-style textures and it just looked off with OpenTTD’s scale.
I just started doing some random models and I modelled a tile of straight rail, monorail, a road piece and a hole in the ground. All of them pretty much exactly the same as you can see them in v0.0.1. From there I was just like “ok, it’s cubes, let’s make a project called BRIX and go with it”.
So I re-used some of the RAWR sources and code, and continued.
Some parts like the landscape were completely redone for the better, some parts like all rails were just re-used for the system. When making these base-set-style NewGRFs, the first task I always did was investigate the base set which sprite ID is which, and sort my 3D models in the same way for consistency (because it is often an insane mess to go through). This alone takes a lot of time to do, so at least I didn’t have to do that again for BRIX.
In general my mindset was totally different. This time I didn’t want to “just make something and fix it later”. The idea was to make things right on the first try, and what is in BRIX, is there to stay. I will skip to present and now I can tell you that about every single sprite will change in terms of receiving some Photoshop postproduction, but the 3D models won’t change, I won’t even re-render them for the most part. Some things like the bulldozed land are horrible and need complete rework however.
This mindset is what lead to me postponing the release “until it’s ready”. My last big effort put into BRIX has been in July/August last summer and around Christmas a tiny little bit. Since then I have “just” written a 8bpp converter (more about that later) in the beginning of February. As you might know, I work full-time as technical artist on another game Factorio. This is taking all of my interest and time lately, and it’s only getting more and more intense. I find this day a good opportunity to face the reality, this is what BRIX is today.
I am definitely hoping to continue working on it, I am not in a state of unhappiness with it like I was with RAWR – mainly because the 3D models are made very precisely and for example the tracks are made with a LOT of thought going into them. I just don’t want to do it right now. I even have a lot of Photoshop painted masks for making it look a lot nicer, but they need applying very carefully, and some of them need finishing, so it’s still a non-trivial task for which I don’t want to delay 0.0.1 right now.
It is a very specific NewGRF in usage. You can load it into your game just like any other, but you can also use it as static NewGRF. I have already written a guide on how to do this with RAWR here. BRIX takes this even further as you can customize which sprites do you actually want to use. You can disable any part of BRIX individually so for example if you only want to use the signals all the time, even when playing on servers which don’t have BRIX, you easily can.
32bpp -> 8bpp conversion
One of the last things as I mentioned is a python script which converts 32bpp to 8bpp. With all of my NewGRFs I have observed that 32bpp isn’t necessarily better, especially when some people have obscure problems with the game running slowly with 32bpp. Even some people with high-end workstations can get into those problems which is just downright weird. Apart from that, the magic of 8bpp is strong, and often people actually just want the additional pixels that extra zoom gives.
Every 32bpp NewGRF I have looked at either ignores the 8bpp completely, or does something ugly. RAWR and YETI is basically broken with 8bpp because of ugly blue artifacts everywhere, and NUTS is saved in this regard because it existed as 8bpp only NewGRF before 32bpp was added, so there is no need to somehow generate new 8bpp because it already exists. I believe 8bpp is very important, and this always made be feel quite sad. The way I always did it was palette conversion in Photoshop, but that’s very limited and any sprites which have semi-transparent pixels look very ugly.
When making Factorio, I have learned to use some basic python for making scripts. Once I needed a special script which would filter images for specific colours, and to practice the techniques for making it work, I decided to write a 32bpp->8bpp converter for my OpenTTD things.
The idea is very simple, it just reads a 16×16 RGB image with the palette colours, and then goes through every pixel in the given spritesheet, and tries to compare it’s colour to the palette colours. Using various formulas (experimenting with that), the closest colour is declared winner and placed into the output spritesheet. Currently I am not saving it as 8bpp image just because I didn’t code it yet, but eventually…
The biggest problem of Photoshop is how it handles alpha (transparency). It just mixes everything with a colour that you specify. The best results I got with 50% dark gray were ok-ish in majority, but still very broken in so many cases. I needed something that would allow me to say how exactly should the alpha channel be handled. My script reads the alpha of the pixel, and if it is less than 50% visible, then it just throws it away. If it is more than 50%, it takes the colour of the pixel. Now, depending on the alpha value, it shifts the colour closer towads the shading spectrum in it’s palette by 1 or 2 shades.
For example, if I take a pixel with colour (ignoring alpha) closest to index 1 – pure black. Imagine the pixel is at the edge of an image where it is 40% transparent. When drawing it, it probably doesn’t look like a pure black pixel. Instead, it is most likely something around dark gray. This is why the script will add 2 to the index, and use colour of index 3. The same applies for white, but vice versa – it will make it darker.
This logic of course isn’t perfect as it assumes a 50% dark gray behind it, but it is much better than what Photoshop does.
The result is very slow since BRIX spritesheets have about 350megapixels, going through each pixel one by one by getpixel() and putpixel() function is very slow. There are probably many ways how to optimize this, and/or try using numpy or a python on CUDA, and run it on a graphics card. But I didn’t have the time to do that so for now I am happy with letting the computer crunch it for some time while I do other things. I am hoping to do a lot more experimenting with the algorithm which finds the closest colour. There is a lot of magic to be done here. For example I want parameters by which I can explicitly enable-disable target colours. So if I don’t like a specific colours, I can skip it. Also, the shade shifting is pretty bad in the middle of the shade ranges because it starts “overlapping/switching sides”. Apart from that, there is a whole bunch of ways how to determine close colours so I want to look into that eventually. For now the 8bpp is kind of broken but it isn’t exactly easy to fix it quickly.
You can find the current version (v8) of this script in my BRIX repository /scripts folder and use it for your NewGRFs as you like.
BRIX is what it is, it might get better (hopefully will), for now you can play with it to some degree. I had many more plans and ambitions for 0.0.1, like finishing trains, applying the Photoshop postproduction, making more trees, giving trees colours, and so on. But here is the time where I decide to just publish it, and push the rest of the things to 0.0.2.
I believe NUTS is heading in the right direction in the long term, even if now you might feel like “well now I finally have BRIX in 32bpp, and I can’t have 32bpp NUTS?”. Which I understand and for that reason I will probably try to release NUTS32bpp soon on devzone. If you like, please test if it is properly compatible with the main Bananas version.
Once again, thank you for reading and for your continued support through the years, it’s always been a lot of fun.