This weekend I was playing a bit with displacement filters, with the help of https://www.smashingmagazine.com/2021/09/deep-dive-wonderful-world-svg-displacement-filtering/

It's actually not that hard to get some cool effects from this. However, it's a bit of a shame that nice distortions require some big gaussian blur kernel... I need to see if I can get similar effects by precomputing the blured images and just blending them with transparency.

GIF
João Costa shared 2 days ago
João Costa shared 7 days ago

I've been dusting off my PS2 lately - the DVD player was a goner a long time ago, but with the new PSBBN patches it's trivial to launch some of the old games from an SSD for that nostalgia hit.

One thing that I don't remember being as annoying is the lack of a "Quit" button in games and a "Shutdown" button in the main menu.

I remember finding it odd when the next generation of consoles added a home button (it's not a PC, why would you want to go back to the main menu?), but in hindsight, that's super helpful 😄 .

(I know that there's some homebrew to add a shutdown button to the main menu, but it's kind of useless if I need to reset the console to get back to it)

  • Want to stop playing? Get up!
  • Want to change games? Get up! (To be fair, back then you would have to do that anyway)
  • Want to load a different save file? Get up!
  • Want to change settings? Get up!

Scastie finally supports Scala 3 libraries with Scala.js.🔥

I was playing around with the idea of interactive-ish graphic tutorials: https://scastie.scala-lang.org/JD557/sXkQ2yQsSpqoQgXounejXQ/4

Playing around with Ordered Dithering and Bayer Matrices.

Interactive demo with multiple dithering sizes (2x2, 4x4, 8x8 and 16x16): https://joaocosta.eu/Demos/Dither/

This was much simpler than I thought. The whole dithering logic is just:

def buildBayerMatrix(n: Int): Vector[Vector[Int]] =
  if (n <= 1) Vector(Vector(0))
  else {
    val mn2 = buildBayerMatrix(n / 2) // M_(n/2)

    val q1 = mn2.map(_.map(_ * 4)) // 4*M_(n/2)
    val q2 = q1.map(_.map(_ + 2)) // 4*M_(n/2) + 2*J_(n/2)
    val q3 = q1.map(_.map(_ + 3)) // 4*M_(n/2) + 3*J_(n/2)
    val q4 = q1.map(_.map(_ + 1)) // 4*M_(n/2) + 1*J_(n/2)

    q1.zip(q2).map(_ ++ _) ++ q3.zip(q4).map(_ ++ _)
  }

def buildDitherSurface(n: Int): RamSurface = {
  val mask = buildBayerMatrix(n)
  val max  = n * n
  RamSurface(mask.map(_.map(x => Color.grayscale((x * 255) / max))))
}

def ditherImage(image: Surface, ditherMask: Surface): Surface =
  image.view.zipWith(
    ditherMask.view.repeating,
    (color, mask) =>
      Color(
        if (color.r >= mask.r) 255 else 0,
        if (color.g >= mask.g) 255 else 0,
        if (color.b >= mask.b) 255 else 0
      )
  )
Ordered Dithering Demo joaocosta.eu

More on updates.

This time I wrote an InterIm backend for AthenaEnv.

I also had to work a bit on InterIm's performance to get it to work, as I can't really use Doubles and my text layout algorithm was a bit inneficient. The frame rate still gets a bit choppy when there's a ton of text on screen, but it kind of works.

To test the whole thing, I ported my Quiz game example. This part was quite easy - just had to replace the HTTP calls with hard coded questions/answers and reimplement the RNG (I'm still not sure why scala.util.Random doesn't work... I suspect it's something related to Long math).

However, after testing this for so long on PCSX2, I was quite shocked on how bad things looked on real hardware. I know I'm using a crappy €20 HDMI converter instead of a propper upscaler, but god damn, the text is pretty much unreadable. 😬

Something to keep in mind in the future, I guess.

Lately I've been playing around with getting to run on my (using Scala.js and AthenaEnv, no Scala Native... yet).

I finally got the Minart snake example to run on real hardware with minor modifications to the code (uncap the FPS and read the input from the gamepad).

It does run quite poorly, and it seems to crash if I get an apple, but it's a start, and good enough for a video.

I do have some ideas on how to improve the performance, but it will require a non-trivial amount of work, so I need to build up the courage first. 😅

GIF
João Costa shared 4 months ago
João Costa shared 4 months ago
João Costa shared 4 months ago

Just released an entry for the 32BitJam: Xtreme Skydive 3D!

You can play it in itch.io or check the source code on GitHub: https://github.com/JD557/xtreme-skydive-3d

There's a lot of stuff that could be improved but, while there's still a lot of time in the jam (until Friday), I will be pretty busy next week. Maybe I'll release a post-jam version with some improvements.

Either way, I'm happy that I finally "shipped a 3D game" with my software renderer. 😄

I've been playing the first Jade Cocoon lately - I played that demo a lot when I was younger, but had never played the full game.

It's not the greatest game ever, but I had fun and it didn't overstay it's welcome (~15 hours. I don't have time for long RPGs anymore 😅). I would say it holds up pretty well, especially for a game with tank controls, and the fusion system still remains impressive.

However, I was extremely disappointed with the ending cinematic... It's a low-FPS (with obvious blending between frames) FMV with the in-game character models.

While usually I would be fine with it, this game starts with an incredible animated opening!

I was really expecting that there would be more of that. Although I wouldn't be surprised if that got cut for budget reasons.

It's not easy to show in side-by-side screenshots (as the animation quality makes a big difference) but man, what a downgrade.

Playing around a bit more with 3D, got a basic Z-Buffer and vertex colors (without perspective correction yet... affine z-buffer?).

This already lets me draw some quite complex scenes without having triangles popping up left and right, and it seems it might actually be faster than naively sorting the polygons.

Scene in question: https://poly.pizza/m/2twvfQfSlHB

Been playing around a bit with samply to fix some performance issues in Minart (especially polygon rasterization and convolutions).

Ended up making a bunch of things quite faster, so now my old 3D Stanford Bunny demo went from ~12 FPS to 20 FPS on my machine.

But, since I was able to also make convolutions faster, I can add two hackish depth of field passes while keeping the old performance numbers. 🔥

João Costa shared 6 months ago
João Costa shared 7 months ago

About 4 years ago (24th of June 2021, according to my old tweet: https://x.com/JD557/status/1408166583575449601) I got a KaiOS phone (Nokia 8000 4g) for emergency use.

It has proven to be pretty useful over the years.

Mostly as a WiFI hotspot when abroad (that way I don't have to buy a data plan for me and another for my girlfriend, and we save our smartphone battery a bit), but it's also pretty nice to have a phone with a long battery life that lets me do some normal modern day stuff (such as check ActivityPub).

Yesterday it also proved pretty useful. Due to the Iberian blackout, I was without power and comms during most of the day, so having a radio around was great to listen to the news without having to be in my car. 😄

I wouldn't recommend anyone to buy this particular mode though, as the keyboard is garbage.

João Costa shared 7 months ago
João Costa shared 7 months ago