News:

Simutrans Tools
Know our tools that can help you to create add-ons, install and customize Simutrans.

Make it possible for ways to draw stuff right in front of the vehicle (not tile)

Started by Nazalassa, August 24, 2024, 08:55:50 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Nazalassa

As of now ways can draw their images in two locations: behind the vehicle (backImage) and in front of everything on the tile. Both can find uses (be it specific or general).

However, I think there should be a third location available: right in front of the vehicles, but behind the other frontImages of the tile (i.e right after the vehicles in the drawing order). Such an "image location" could be useful in the following situations:

  • In the pak48.bitlit, the tunnels are tube-shaped, and use the way's frontImage to draw the part of the tube that is in front of the vehicles. However, since the way's frontImage is drawn above everything on the tile, including any building's frontImage, this may lead to ugly graphics. In the example below, the station does not give too bad a result, but the depot does - the tunnel's frontImage gets drawn above its frontImage whereas it would be better if it did not.

    bitlit.gif

    Thus I think in this situation it would be better if the tube's front was drawn at the new location I suggest, right in front of the vehicle, but behind any building's frontImage.
  • I have made some sort of "experimental" way which has a "wall" on both sides (to avoid the vehicles getting off the track). Thus the front wall hides part of the vehicle. This means it has (with the current system) to be a frontImage, which gives the following result when a building is added:

    box.gif

    The front wall would go better at the new location I suggest, right in front of the vehicle, but behind any building's frontImage.

Such an "image location" would thus be used e.g for whatever part of the way that is supposed to hide only (part of) the vehicles.
Making paksets since October 2023  |  pak48.bitlit | pak32.box | MLM for pak64 | Empire F7 cars

Life is like a multi-tasking OS: you know you'll eventually get back to everything, but you don't know when.

prissi

A thrid position is very difficult to realise. One could however tweak the order so than stations' and depots' forntimages are drawn before the ways' frontimages. That would apply to all ways and all graphic sets. However, I thing it should make more sense this way as it is like real stations and depots appear.

Please test it in r11402

Leartin

Quote from: prissi on September 06, 2024, 12:07:28 AMit should make more sense this way as it is like real stations and depots appear.

I don't think that can be generalized. In the given examples, the station is broader than the way, so the frontimage of the way should be behind the stations frontimage. But you could have ways that are as broad as the tile, with narrower stations, then it makes more sense for the wall to cover the station. And there are more dimensions - some frontimages are meant to be "above" the way, while stations are often "in front, but rather flat on the ground".

Same is true for backimages, to be honest. In the second example above, if you turn it to see the opening, you'd probably want the wall to continue into the depot. But if the way is all flat on the ground, but reaches the tile borders, you want the depots backimg to cover the ways backimg.

That being said, I suppose I can finally add the snowpoles to my roads that I dropped due to exactly this issue...


prissi

One could have this configurable per way or per pakset as it needed only one change in recalculating the image after construction or season changes.

Nazalassa

Quote from: prissi on September 06, 2024, 12:06:03 PMOne could have this configurable per way or per pakset as it needed only one change in recalculating the image after construction or season changes.

Or configurable per building? Then perhaps it would also be good to have an option not to display the way's FrontImage for some buildings, such as this "station":

station.gif
Making paksets since October 2023  |  pak48.bitlit | pak32.box | MLM for pak64 | Empire F7 cars

Life is like a multi-tasking OS: you know you'll eventually get back to everything, but you don't know when.

prissi

Actually, I was hiding the front image on stations and depots in r11402 not changing the drawing order. That was too complex.. Did you checked it?

Nazalassa

Here is what I get with r11402:

simscr126.png

I also tested the pak48.bitlit tunnels; the FrontImage is still here in both cases. But perhaps I missed something.



When I suggested per-building "way-FrontImage-display-mode", I was thinking of maybe something like this in the code that renders a tile (assuming it is implemented by sequencially blitting images on the screen; I could not find where in the source code it is, so I am just guessing it is like this; I hope it is not too un-understandable):

(render BackImages)
if (way on tile has a FrontImage && (station on tile)->(way FrontImage display mode) == 1)  /* Assuming "(way FrontImage display mode)" is ) 0, 1 or 2 depeding on station setting */
   (render way FrontImage)
(render station FrontImage)
(render other front stuff)
if (way on tile has a FrontImage && (station on tile)->(way FrontImage display mode) == 2)
   (render way FrontImage)

or:

(render BackImages)
if (way on tile has a FrontImage && (station on tile)->(way FrontImage display mode) == 2)
   (render station FrontImage)
if (way on tile has a FrontImage && (station on tile)->(way FrontImage display mode) != 0)
   (render way FrontImage)
(render other front stuff)
if (way on tile has a FrontImage && (station on tile)->(way FrontImage display mode) == 1)
   (render station FrontImage)

In these cases, "way FrontImage display mode" works as follows:
* 0 = No way FrontImage
* 1 = station FrontImage is rendered above way FrontImage
* 2 = way FrontImage is rendered above station FrontImage (default)
Making paksets since October 2023  |  pak48.bitlit | pak32.box | MLM for pak64 | Empire F7 cars

Life is like a multi-tasking OS: you know you'll eventually get back to everything, but you don't know when.

prissi

Change the render order will cost a lot of memory and speed. The list are sorted for background drawing once when the objects are cerated and then are drawn backwards. Any deviation from that order needs lengthy tests for every tile with a straight way to look for a depot.

Hiding the frontimage costs almost nothing. But the hiding seems not to work. Can I get your pak or dat and graphics for more tests, please? (Or are they in your repo?

Nazalassa

Attached is a "pakset" which has a way with a FrontImage and a depot. (I did not include the 16 boxes with other colors.)
Note: the depot is in the "uniflow" menu and the tracks can be accessed with "t".
Making paksets since October 2023  |  pak48.bitlit | pak32.box | MLM for pak64 | Empire F7 cars

Life is like a multi-tasking OS: you know you'll eventually get back to everything, but you don't know when.

prissi

That is broken, the uniflow way is just some triangles looking like diagonals. Can you send me the pak files for just the way and the depot you used for your screenshots? Then I can put them into an addon folder.

Nazalassa

They are pak32 size so pak32 is required (I do not know any other pak32); the depot was the one from pak32 anyway, so I did not include it.
Making paksets since October 2023  |  pak48.bitlit | pak32.box | MLM for pak64 | Empire F7 cars

Life is like a multi-tasking OS: you know you'll eventually get back to everything, but you don't know when.

prissi

It works now in r11405 but it requires some more tweaks to the depots as the previous tile may overlap too.

Octavius

The proper way to deal with this would be to use some form of depth mapping. Every image needs a depth image giving the height of the pixel above the tile floor (you could use 7 bits of the alpha channel for this, leaving the most significant bit for alpha testing); add to this the elevation of the tile times tile height, draw the pixel if it's not lower than what's in the buffer. The result can be cached, so that all static elements only have to be rendered once and updated when things on the map change. This also allows for things like overhead power poles intersecting platforms or platform roofs, pretty common in real life.

The downside is that this probably requires completely rebuilding the graphics system of Simutrans. And to use it effectively, you have to build the depth images of every visible object, but for legacy objects it should normally give decent results if you just assume the back image is at the rear faces of the box and the front image at the front faces of the box. It might be worth starting to build such depth images anyway.

makie

The alpha channel is not a bit that shows: is transparent or not.
It is a value form 0 to 255 how much transparent the pixel is.

prissi

The drawing order of tiles is way more complex, involving directional clipping for things overhanging a tile etc. which strongly reduces artefacts. Depth mapping is done in a way, on an object base. The actual drawing has no idea of the object it draws. Otherwise one would need 1 million images for the 1 million trees in a 500x500 map ...

Octavius

If you set things up with a per pixel depth test, the drawing order becomes irrelevant – except for the translucent parts, which still have to be drawn in order – whilst eliminating all artefacts and allowing intersecting objects, wouldn't it? Apart from intersecting translucent parts, but translucency is rarely used anyway. That's how I would do it if I were designing the rendering system of a game like Simutrans from scratch. All static parts (except translucent parts) can be rendered once to a texture (can be done in sections; with associated depth texture), using the object's depth texture offset with the objects elevation, then stored and quickly rendered each frame. Draw the dynamic stuff (vehicles) on top of that, then the translucent parts.

But of course, I'm not designing the rendering system of Simutrans from scratch. I'm just thinking. There's a little game project in the back of my mind with a 3D world with parallel projection like Simutrans, but it has been in de back of my mind for a long time with little progress.

Leartin

In theory, such an approach would work. But is it useful?
In the same vein, we could, for each pixel, add information in which direction it's facing, and have the day-night-cycle influence in different amounts based on that information...

Question: Where do you get the information from?
For 3D models, you could get all that information from 3D source files. Yet, if you have 3D models and mess with the drawing engine to that degree, you might as well use a 3D drawing engine and use the 3D models directly, basically skipping the "retrieval"-step for artists alltogether.
For pixel art, you'd need the information hand-placed by the artist, pixel by pixel. That's an awful lot of work there, and I certainly wouldn't want to do it. I'd rather 3D-model stuff.


I'd much rather see the current drawing order improved by allowing more "slots". Basically, we have two images for most objects, and both are in the render order individually. Imagine additionally to "front" and "back", we also get "ground" as even lower priority, and "center" as between "front" and "back" (and perhaps, for roads, even between cars in different directions). That alone would open up a lot more possibilities in how to order stuff - even if you don't get additional images, but rather the option to decide which "slot" will be used (which is what "draw_as_ding" kinda already does, switching ways from "ground" to "back" level)

Perhaps a three-level priority could work as well - such that ways frontimage has priority over depots frontimage by default, but only if they have the same priority set.
So the order goes "high prio way, high prio depot, mid prio way, mid prio depot, low prio way, low prio depot". I don't think that would involve lengthy tests, since it's still always the same order - just with more "empty" slots... drawing no pixels shouldn't cost much.

prissi

Currently, there is one object list. In order to have different order front to back and back to front, one would need 2 lists. With each slot another list is needed, and increases the size of ground tiles by 80% in memory (or 40% less ground tiles in cache). At each time a vehicle enters a tile, these lists needs to be resorted ... So these extra list will be hard to synch and will reduce playable map size and game speed.

The proper way out would be doing tile based full 3D (like in machinsky) which means a full rewrite and redrawing of all objects in 3D objects.

By the way, OpenTTD has no foreground images for most things (although it has a complex programmable sprite state machine executed at each redraw) and thus cannot handle even bridges or tunnels correctly without hardcoded hacks.

Leartin

Quote from: prissi on November 01, 2024, 06:22:37 AMCurrently, there is one object list. In order to have different order front to back and back to front, one would need 2 lists.

I can't quite get my head around how you'd have all objects in a single list in drawing order, given how vehicle mess everything up.
When a vehicle moves from tile A south to tile B, you'd want the vehicle in front of the backimages of both tiles, but behind the frontimages of both tiles, while the backimage of tile B needs to be in front of the frontimage of tile A. It's impossible with mere ordering.
My approach would be to cut such vehicles in half at the tile border, such that the order is (back to front) A-back, vehicle-on-A, A-front, B-back, vehicle-on-B, B-front.

Essentially, I assumed there wasn't a list of "all objects in drawing order", but rather a "list of all tiles", and for each tile, a "ground", a "list of static objects" and a "list of vehicles" (or "list of movables", including pax and moving groundobj). Then, you'd render (back to front):
- the ground (if an object on it doesn't exclude ground rendering - and perhaps all ground before you move on to - the next step),
- the backimages of the list of static objects,
- the movables, and then
- the frontimages of the list of static objects.

I'd get that the frontimages need to be rendered in the same order as the backimages since the same list is reused, but that wouldn't be a problem if there were several render passes of frontimages of different priorities using the same order. In cache, you only need two images for statics per tile - the collected back and the collected front, which is presumably what you already cache (else you couldn't put a vehicle inbetween). The object list does not change. It would affect how quickly the graphics are put together since additional passes are needed (even though in most cases, they don't do anything and should be rather quick), but isn't that why "construction graphics" are used to delay figuring out which graphic to show? (Presumably, that's why they existed in TT, no idea if that is or ever was functional in Simutrans)

prissi

There is a list of all tiles. The grounds is drawn first for all the world bottom tiles in a first pass.
Then each tile is iterated again and the objects are draw in that order. Then the tile is iterated backwards to draw the foreground objects. Only one list per tile, see objlist_t.

Construction graphics are a animated building with a certain half-life that is replaced by the normal building when expired. The insertion of object is immediately.

It is really a single list. Whenever a new vehicle enters a tile, it is sorted into the list. First NE vehciles, then trams and rail, then SW vehicles (and pedestrians even before/after that).
The tiles have their drawing order two and vehicles are not allowed to overlap with the next tile. (That is the reason of the other stopping positions, as the vehicle has to jump tiles to maintain drawing order without artefacts if the ground has been drawn before.
This works as the vehicles cannot change lanes apart from rare overtaking events.
This system does not work on bridges, which is why there is directional clipping.

Leartin

Okay, one list per tile is more reasonable than one list, period. I had a look at objlist_t. The real "magic" seems to happen in grund_t, display_obj_all, which explains how for each tile, vehicles of neighbouring tiles are considered etc. - not that I understood it fully.

But back to the order within a tile: You draw objects until you reach movables (the back-images), then draw movables, then the rest of the back-images (appearently, that's just for powerlines), and then the front-image of everything in reverse order:
// foreground (needs to be done backwards!)
for(  size_t n = top;  n-- != 0;    ) {
#ifdef MULTI_THREAD
obj.some[n]->display_after( xpos, ypos, clip_num );
...

"display_after" is found in obj_t and starts with image_id image = get_front_image();
if(  image != IMG_EMPTY  ) {
...

If we had a front-image priority system as an object parameter, it would be retrieved in obj_t by something like "get_front_image_priority()". Add a priority parameter in display_after, and the code in objlist_t could look like this:
// foreground (needs to be done backwards!)
for( int priority = 1; priority++ >= 3 ){
for(  size_t n = top;  n-- != 0;    ) {
#ifdef MULTI_THREAD
obj.some[n]->display_after( xpos, ypos, clip_num, priority );
...

while display_after in obj_t would start with
image_id image = get_front_image();
if(  image != IMG_EMPTY && priority == get_front_image_priority()  ) {
...



This does not touch the objlist, memory shouldn't be affected at all. I wouldn't think the additional calls to display_after() would cost much since they don't need to do anything, though of course it's not free.
Though still, to me that's an interesting thought experiment, I'd not expect this to be done.