This
will byers stan first human second

izzy's playlists!
Monterey Bay Aquarium
sheepfilms

JVL
we're not kids anymore.
$LAYYYTER
hello vonnie
cherry valley forever

ellievsbear
Acquired Stardust

JBB: An Artblog!

Origami Around

blake kathryn
Misplaced Lens Cap

pixel skylines
styofa doing anything

Kiana Khansmith
RMH
seen from Brazil

seen from United States
seen from Canada
seen from United States

seen from Germany
seen from Costa Rica
seen from United States

seen from Vietnam
seen from United States

seen from Ukraine

seen from Netherlands
seen from Malaysia

seen from United Kingdom

seen from Singapore

seen from United States
seen from Switzerland

seen from Germany

seen from Malaysia

seen from United Kingdom
seen from United States
@j0ecool
This

Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
Free to watch • No registration required • HD streaming
i'm going insane
i‘ve been called weird, but never unfunny, let that sink in.
Nothing Doing no. 64
manic episodes be like: errybody's gotta know how I wipe (god said)
it’s a beautiful day to ominously wingman for your unwilling vessel

Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
Free to watch • No registration required • HD streaming
kris
all of my writing is actually just thinly-veiled fantasy about being seen at your worst and still being loved
Safety First
Bonus:
The facial expressions here are so, so goddamn good
fuck it, skitter miku
I add tests for the builder logic. This means I need to add builder.h to the build logic for the test runner. This means I need to redesign my builder to avoid needing to maintain three seperate builds, one for the kernel, one for the game dylib, and one for the tests
While we're at it it would be nice to set the builder up in such a way that it can itself hot-reload, which feels like an unsolvable bootstrapping problem??
I guess I can move the build logic into a data file, which can reload without needing a code update, and that covers everything sane I could do with a hot-reloading code-rebuilder
Yo dawg, I herd u like infrastructure, so imma need better infrastructure before I can work on improving this infrastructure

Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
Free to watch • No registration required • HD streaming
When you're making a game engine, there's a tradeoff when it comes to working on dev quality-of-life features vs game-facing features.
A factor I don't usually see discussed is the project's PITA Budget. It may take more swe-hours to implement a shortcut that saves 5 seconds at a time than it will ever pay back, but the qualitative difference in working without that 5s annoyance reduces the mental-strain cost of continuing to work on the project at all. As a motivation-bottlenecked solo dev, having good tools not only makes asset creation more efficient, but effectively gives you more hours in the month to work with.
today's small thing is interpreting a UI Label of just "\n" to be a shortcut for a linefeed, which lets us change this from
to
cutting lines-of-code in half
It's the sort of thing that makes actually* zero difference in the finished product, but it makes the daily life of being a game developer fractionally less frustrating, so that's satisfying.
*: If compilers can optimize across strcmp, which I strongly suspect they can, then they would literally compile into the same code
For funsies (because I'm stoned) let's pretend we're compiler engineers again. Why should this be optimizable? ui.labels() is a variadic template function that emits one call to ui.label() per argument, which has overloads for each type. the const char* overload has a clause at the top of it:
With inlining, we would get if (strcmp("\n", "\n") == 0), which a human programmer can clearly see is always true. Can compilers? Well, given that strcmp is a C Standard Library function, compilers are free to make special assumptions about how they work that may not be obvious from the type signature. We also would not be able to inline strcmp, because it's compiled separately (unless you're building your own libc from source, or if your libc ships bitcode which would be weird)
Anyway! So it stands to reason that some enterprising young lad may have realized that they can save 0.2ms on some benchmark by constant-folding strcmps, or by precomputing string manipulations, or whatever. How can we check? Godbolt.
Sure enough, even at -O1 clang is able to constant-fold the strcmps and turn test("foo") into just doSomethingElse()
This ramble didn't really have much of a point. C++ is a language built around zero-cost abstractions. There's something really satisfying about knowing enough about how the optimizer works, and what the language's semantics are, including its cost model, and thus being able to reason about what will actually get optimized away in practice.
gradients
gradients
so now i've got it set up so it lerps between two arbitrary colors
this is far less dramatic than the patterns we would see previously, but that's because that was using a 3-color gradient, blue-black-red
I'm excited to mess around with arbitrary-number-of-colors gradients, but not excited about the hacky UIs I'll need to make along the way
(I guess I could use the same colors as the hardcoded 3color version for a better apples-to-apples comparison huh)
There we go
it is a pain in the butt to use but oo shiny pretty colors
we're basically doing the same thing as a Gradient Map (pictured: Krita's version), which is a more direct way to manipulate the relevant data, but this works as a v0 because we have (awkward) access to all the bits (and bytes)
simple black-blue-white gradient
greebly
here's an example of intentionally using lower resolution to imply details/texture
vs the high-res version is clearly kinda nonsense
and now to get dirty
this is pretty much pushing the limits of what can be done with one single gradient map alone
zoomed out + params (8 colors! that's probably too many!)
so the next steps for generating things would be generating multiple noise maps and combining them. We can use thresholding, multiplication/masking, and different noise resolutions in order to make more coherent generated features
finally, stars
gradients
so now i've got it set up so it lerps between two arbitrary colors
this is far less dramatic than the patterns we would see previously, but that's because that was using a 3-color gradient, blue-black-red
I'm excited to mess around with arbitrary-number-of-colors gradients, but not excited about the hacky UIs I'll need to make along the way
(I guess I could use the same colors as the hardcoded 3color version for a better apples-to-apples comparison huh)
There we go
it is a pain in the butt to use but oo shiny pretty colors
we're basically doing the same thing as a Gradient Map (pictured: Krita's version), which is a more direct way to manipulate the relevant data, but this works as a v0 because we have (awkward) access to all the bits (and bytes)
gradients
so now i've got it set up so it lerps between two arbitrary colors
this is far less dramatic than the patterns we would see previously, but that's because that was using a 3-color gradient, blue-black-red
I'm excited to mess around with arbitrary-number-of-colors gradients, but not excited about the hacky UIs I'll need to make along the way
(I guess I could use the same colors as the hardcoded 3color version for a better apples-to-apples comparison huh)

Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
Free to watch • No registration required • HD streaming
quick UI update
I finally added some sliders and visually this is way more effective than I was expecting
specifically the decision to try just drawing a box outline instead of a filled in rect for the range indicator; I wasn't expecting much but it actually looks kinda competent? in stark contrast to the rest of it
the rendering code is super straightforward
the update code is less so, and I don't feel like going into it in any detail
I also took the liberty of aligning everything, which was dummy-simple
... though getting the text right-aligned was less automatic than I'd prefer (for now)
it's nice to have features built-in, so you can say text.alignment = RightAlign and have that more clearly communicate your intent (plus not having to think about how to write or read a math expression can be a boon), but being able to just cobble something together with a minimum spanning set of functionality is important for Good Enough engineering
UI Crescendo
So now I gotta extend the UI to draw more than just buttons I suppose
My first thought is to do the obvious thing and have a UIElement base class, from which individual elements can derive. The problem with this is that we're hot-reloading the game from a .dll, so vtables have a tendency to get thrashed when we reload, so we'll jump into stale memory and try to execute malformed instructions (what we professionals call a "big yikes").
We're persisting UI elements because we've split the update and render steps, so we just need to clear the stale state on reload. So we'll add a callback that we can fire right before unloading the dll, and use that to clear all the stale elements
We also add an Allocator struct so that we can bundle malloc AND free and simplify that plumbing.
Then in the main (non-dylib) part,
and when we unload the game, clear the UI elements
(a simpler thing to do would be to clear the buffered UI elements after rendering them so that we never persist them across frames regardless of whether we reload the dll. However we *do* persist interaction data (button state) from frame to frame, so it's pretty important that we not do that. This does mean we lose clicks in progress when the dll reloads, but that should never happen to non-devs (and is impractical to reproduce for devs, at that), so #NAB #wontfix)
Then I realize that we still have problems, because we need to store some kind of runtime type information (RTTI) so we know how to actually update the elements in question. So if we have to add an enum per derived class, store that in the base class, and make sure that stays in sync, that seems way messier than just using an ADT-like design in the first place. (Dr. Hindsight here: dynamic_cast would probably have worked just fine, I just didn't think about it until typing "RTTI" just now)
So uh what are ADTs? Well if you go full expanding brain memes and do pure-functional programming, you wind up in the land of Algebraic Data Types. Specifically Sum types, so called because the members of a type are the SUM of possibilities of its components. Which is to say a UIElement can be a Button OR a text Label. And another way to say sum/or is "union"
this is basically the mechanism behind how Haskell's sum types work, only with more footguns because we're dealing with the plumbing directly. Fortunately we're using these as essentially an implementation detail, so we control access to a very limited number of public interfaces, so what could possibly go wrong?
(I should probably split some of this stuff out of being all in one big game.cpp file, which would let me actually hide these implementation details, but we'll deal with that cleanup when it becomes more relevant)
back in the UI class, let's pull out the code that validates our element index, because we'll need to duplicate this logic for every type of thing
and now our button preamble looks like this,
that elem.kind jiggery-pokery is something we'll need to repeat at least once per element, which is annoying, but the cleanest way to reduce the repetition is with a macro, and frankly I'd rather not
Now then we just add a label method to add text elements,
and some helper methods to handle formatting numbers (todo: formatting)
All of that leads us to this rather-svelte result for actually building a UI
which rendered in-engine, finally, nets us,
If you can't tell, I am very much of the "MIT approach" to software, where I will spend tremendous amounts of effort in the Implementation, in order to have a cleaner Interface. I did used to be a compiler engineer after all. This is probably related to why I have yet to ship a commercial product solo, isn't it.
anyhow, all that was a bunch of architectural gobbledygook. Mostly just figuring out how to partition the problem and what mechanisms to use in order to not explode within the confines of dll hot-reloading, while still scaling linearly with additional elements. In theory, adding different types of elements at this point should be straightforward, not requiring much additional infrastructure. (In theory, theory and practice are identical. In practice...)
Now, the thing that's cool about the imgui-style of UI architecture is that it lets you make compound custom widgets via simple composition of existing elements. Meaning that application logic doesn't need to deal with any of the complexity of the underlying system.
So the specific thing I wanted a UI for was to tweak the parameters of the texture generator. At some point I pulled out all the different constants I was using, so I could have them all collated in the same space
I don't have default values there, because I set the values in the onLoad callback, so they update whenever I change the code and rebuild
(and I do mean whenever I change the code; I have it watching game.cpp for changes and rebuilding in the main .exe, so that's automated too)
So that's *pretty good*, but it's still a two second compile every time I want to make a tweak to the parameters, along with being in a separate window from where the textures are actually displayed. It would be way smoother to get that down to near-instantaneous
yeah yeah you get the idea
so now I can create a custom widget for each parameter I want to change, consisting of a button each to increase and decrease the value, plus displaying the current value
this is just a normal function that takes parameters totally orthogonal to the UI, that does whatever arbitrary logic it wants, with no custom rendering logic or anything. (Ideally this would use a Slider element, and lo/hi would dictate the range of values, but that would be a new element type, and I'm sick of dealing with UI internals and just want to move on to doing other stuff for a bit)
so now we can remove the onload logic ("I fucking love deleting //todo:s" etc), set those values as defaults, and update our UI code:
which does Pretty Much What You'd Expect
reducing iteration time down ot the 50ms it takes to generate a new batch of 16 textures. amazing
note that we're setting min/max manually, and we could probably reduce repetition by factoring out the common case where we just +/- one with some minimum, this is Good Enough and sufficiently obvious that I'm fine with it
so of course I start playing around with it (noise size = 7, tex size = 16, noise scale = 1, mode = 3, tex repetitions = 8)
ok this one too (noise size 31, noise scale 4, tex size 1024, tex repetitions 1)
and mode 2, noise scale 9 (3 repeating bands of blue, red, green)
mode 3, noise scale 3, noise size 13, repetitions 16
so, the moral here I guess is that by making it more fun to mess around with, I'm more likely to explore the possibility space. Which was the whole point of investing in better tooling in the first place :)