Ancestors

Toot

Written by oli on 2024-11-09 at 11:51

I wrote a Rust version of @runevision 's LayerProcGen: https://crates.io/crates/layer-proc-gen

It has a small demo in the examples (i'll set it up on GitHub pages so you can try it out in the browser soon™️)

Initially I replicated the C# 1:1 (modulo internal layers and multithreading), and then modified it to feel more Rust-like (to me) as I extended the demo further.

It's entirely game engine independent, even though the demo is written for macroquad

=> View attached media | View attached media

=> More informations about this toot | More toots from oli@hachyderm.io

Descendants

Written by oli on 2024-11-11 at 12:52

The demo project for my top-down procedural generation crate is now live at https://oli-obk.github.io/layer-proc-gen/

WASD for movement. Space is a handbrake. Shift is cheat nitro.

F1 shows local debug information

F2 shows all loaded chunks overlaid on the game

F3 shows the layer hierarchy

F4 is a 3d view showing all layers stacked on top of each other

=> More informations about this toot | More toots from oli@hachyderm.io

Written by Rune Skovbo Johansen on 2024-11-09 at 15:50

@oli This is really interesting, and sounds cool!

I'm curious to better understand the differences in your version, like not having to implement layer types, and data being generated just-in-time. Unfortunately I don't know Rust well enough to really understand the code. Do you think these might work in C# as well or do they rely on Rust features?

What would be equivalents of the GetRoadsOverlappingBounds and GetRoadsOwnedWithinBounds example functions on this page?

https://runevision.github.io/LayerProcGen/md_Patterns.html

=> More informations about this toot | More toots from runevision@mastodon.gamedev.place

Written by oli on 2024-11-09 at 18:37

@runevision wrt Layers being (final?) types, not child classes:

This should definitely work in C#, too. Basically I moved the layer-specific information like chunk width and height to the chunk itself. LayerDependency registration disappeared when I moved to lazily fetching chunks.

A Rust/C# difference is that I statically define a set of types that are layer dependencies and then require you to pass them to Layer. But with the C# Singletons, this is not needed, you just access what you need

=> More informations about this toot | More toots from oli@hachyderm.io

Written by oli on 2024-11-09 at 18:37

@runevision on the just in time generation:

The current impl relies on the fact that generation (especially of just a handful of blocks) is very cheap and can always be done in under a frame. Essentially I request the top layer's chunks in a specific bounds, and those chunks request data from the dependency layers.

Initially I had your system of ensure-loaded-in-bounds and then when computing chunks I paniced (uncatchable exception) if a chunk I needed data from was not loaded.

=> More informations about this toot | More toots from oli@hachyderm.io

Written by oli on 2024-11-09 at 18:43

@runevision that caused a bit of almost code duplication.

When generating new chunks I check if they are there already. If not I generate it, else I increase the refcount

When accessing chunks I check if they are there. If not I panic, else I return the block.

So I made the access path generate instead of panic. At some point I realized my demo lost the ensure-loaded-in-bounds invocation during a refactoring. Some stat collection showed that my actually loaded chunk counts went down a lot

=> More informations about this toot | More toots from oli@hachyderm.io

Written by oli on 2024-11-09 at 18:48

@runevision In the future I want to make it so accessing a chunk returns it if loaded, and tells the generation thread pool to generate it otherwise.

If I'm a top level layer, I return None (null) for the chunk if it's not loaded yet and try again next frame. This gives me the "chunks are popping up in the world as they get generated" behavior. If loading bars are preferred, one can instead wait until the chunks are generated.

=> More informations about this toot | More toots from oli@hachyderm.io

Written by oli on 2024-11-09 at 18:50

@runevision if I fail to get a chunk while generating another layer's chunk, I get back a future/promise. I can now block on it, or obtain more futures for the other chunks i'll need to increase parallelism. Once I get my chunk data, I continue computing the new chunk

=> More informations about this toot | More toots from oli@hachyderm.io

Written by oli on 2024-11-09 at 18:37

@runevision I didn't understand the full class structures (my OOP/inheritance experience ended 10 years ago when I stopped doing anything but Rust), so I mostly did two things:

This means that in my first iteration the Layer and Chunk traits (C# interfaces) contained all the parent class APIs

=> More informations about this toot | More toots from oli@hachyderm.io

Written by oli on 2024-11-09 at 18:58

@runevision

pub fn get_roads_overlapping_bounds(layer: Layer, bounds: GridBounds, out_roads: &mut Vec) {
    for chunk in layer.get_grid_range(grid_bounds) {
        for &road in &chunk.roads {
            if bounds.overlaps(road.bounds) {
                out_roads.push(road);
            }
        }
    }
}

=> More informations about this toot | More toots from oli@hachyderm.io

Written by oli on 2024-11-09 at 19:03

@runevision

fn get_roads_owned_within_bounds(layer: Layer, out_roads: &mut Vec, bounds: GridBounds) {
    for chunk in layer.get_grid_range(bounds) {
        for &road in &chunk.roads {
            if bounds.contains(road.bounds.center()) {
                out_roads.push(road);
            }
        }
    }
}

=> More informations about this toot | More toots from oli@hachyderm.io

Written by oli on 2024-11-09 at 19:20

@runevision one thing I only noticed because Rust doesn't have a GC, is that DecrementUserCountOfLevel can be automated by using weak reference counted pointers. C# appears to have them in the form of https://learn.microsoft.com/en-us/dotnet/api/system.weakreference?view=net-8.0

It should be possible to put the actual chunk data behind this weak ref. If all strong refs (well normal C# by-ref values) have been GCed, the weak ref allows the actual chunk data to be GCed, and future attempts to access that chunk will fail

=> More informations about this toot | More toots from oli@hachyderm.io

Proxy Information
Original URL
gemini://mastogem.picasoft.net/thread/113452850363604891
Status Code
Success (20)
Meta
text/gemini
Capsule Response Time
366.990275 milliseconds
Gemini-to-HTML Time
3.77786 milliseconds

This content has been proxied by September (ba2dc).