well i did roughly do some two week projects for last month, which i guess makes this the most on-schedule i've been for these things since covid started
probably at some point i should stop opening these posts with a downer statement about how i'm stressed and haven't been doing much. well. i will keep doing that until it stops being true, i guess
anyway! i worked on some code stuff this month!
early in the month i decided to revisit one of my haskell code projects, mostly because (as i had kind of mentioned in the last post) i had been thinking about modernizing my rendering, and that would require switching over to OpenGLRaw for my haskell rendering. so i was like, well, let's look at where my code is currently at. answer: it is bad.
like, i don't really know anything about how to render, really? i have a vague understanding that modern rendering techniques are something like, shader-heavy, actual vertex buffers full of model data; all actually-mutated data is handled through geometry instancing in separate buffers than index into the vertex buffers. large buffers allocated early on and reused. idk. anyway,
this code was very much not that. there was a lot of allocating perfectly-sized buffers for each new rendered thing and just stuffing vertex data into it. one of the major issues i was having with general rendering stuff is that, if you're storing raw vertex data to be rendered, and you want to update map data (b/c the pc changed it, for example) that means either totally rebuilding your vertex buffer (slow) or partially writing over some vertices (which means you have to know which vertices in your buffer correspond to what geometry & have enough room to write your new geometry in place). my understanding is a more modern approach is like, you have a vertex buffer full of world geometry options (your marching-cubes cases, or your landscape geometry chunks, or w/e) and then an instance buffer array for your actual world grid (or w/e) that just refers to whichever relevant geometry chunk, and then you can update the instance data without caring about all of your geometry, like, having the exact same vertex count, since you don't need things to line up. probably at some day i should work out how all that instanced code works, but, uh, this was not that day.
anyway previously for this project i was drawing landscape quads, one per coordinate, and then up to eight quads for vertical walls between them, which meant that it wasn't really feasible to update values (since the number of vertical walls would throw off any count; i'd have to track the relative height of every tile and its neighbor and then index into some huge offset array-- basically not something that would be fun or reasonable at all). so, first step, i was just like, okay, no, this is now fully a heightmap, and the rendering for each tile happens by drawing a quad for the floor, the left wall, and the front wall. even if the floor is flat and seamless and the wall quads are a flat line. because at least then every landscape chunk has the exact same number of vertices laid out in the exact same order, which is part of what will be needed at some point for instanced stuff. it also means it's no longer a nightmare to index into the vertex buffer array to update stuff, while i'm still using that technique.
(tbh graphics code is both fun and infuriating b/c it's nothing but a long sequence of figuring out Techniques. sometimes this can be fun to do a deep dive into some complicated, like, deferred rendering texture-writing shader technique, but other times it's like, okay i just totally restructured the entire rendering pipeline and now... it looks exactly the same. but trust me it's working differently under the hood. and the performance characteristics of different approaches tend to be pretty opaque unless you already know everything about how the rendering model works, which, let me tell you i absolutely do not.)
so anyway one consequence of that is that i needed to write a sprite-wrap tiling shader. since all wall quads were a single quad now, i couldn't just draw a quad for the relevant sprite using the relevant uvs, since that would just render one big stretched sprite. but i couldn't just extend the uvs, since that would render arbitrary sections of the sprite atlas. a made
a tumblr post about this bit. (
also on mastodon)
i also made it so i could render multiple game maps together. tbh my white whale with gamedev has been like, infinite world, seamless, with caves, with acceptable performance. i just want a game that presents as a coherent space, and it turns out that most games do not do that b/c that is kind of a nightmare. so even though this project kinda started out like, "i will just have some big 256x256 maps and you will transition between them" i very rapidly decided, no, actually i still want a single connected world. even though that's way more difficult to code than a series of maps that load and unload and that have boundaries.
so all of that... i wasn't really keeping track but let's say that took up the first half of the month timeslot. i did finish it around the 15th, so, that's good enough for me.
the
second half of the month was mostly me going down a procgen dungeon generation hole again. it's very easy to do.
so the thing i want to test with this 'engine', before i get into anything really complex wrt map loading/unloading, is just like: you are on a map. there is a cave entrance in the map. you can move from the world map (which is a heightmap) into the cave map (which is a slightly different variety of heightmap) by walking into a cave entrance, which would have to be literally a hole sliced through the heightmap, either vertically by destroying a wall, or horizontally by destroying a floor. then, the connection between them would have some kinda notation in the map data, like 'hey here's a hole leading to this other map', and i'd need to generate new seamless geometry for the connection point, and handle the moment when the pc is colliding with both maps at once.
instead of working on any of that, i mostly just put together a cave map generator. whoops. i didn't even get cave rendering (with a ceiling and no 'oob' tile rendering) done! oh well i can work on that later i guess.
i posted a bunch more screenshots of this. i posted
a thread on mastodon and also a tumblr post (
1,
2). the actual algorithm is pretty simple -- place down various-sized cells totally randomly -- but in order to make sure each grid was fully-connected and didn't have any stranded cells i did this total-overkill bucketing floodfill pathfinding thing. it's also pretty slow, so in the future i'd probably want to limit it a bit. i didn't even get to further stages of the generation, where i would take the maximally-connected graph and then start removing edges to get something slightly less connected and more treelike. i did get enough done to actually start using the generator 'in-game'; that post was
here. so i'm calling this a tentative success.
(the big step for cave testing would be, figuring out a good entry point for a given map, which would be a cell where i can attach a stairway down and have it fit into both the cave map and into the worldmap heightmap above. that's its own challenge that i did not get to at all. but before i do that i'd want to get 'cave rendering' working, so a cave actually has a ceiling and doesn't have all those floor tiles between rooms. this would also involve tweaking the collision code to handle ceilings, and also cave walls. it's all a process.)
anyway that's some progress on some stuff, which is a lot more than i've been doing lately. tbh in this post-covid world i am just a constantly-exhausted demoralized mess, which has made it very difficult to muster up the energy to care about doing anything. that'sssss life though