How Cathedral is built, and why we are postponing the release

TL;DR, Cathedral is still being released, but we’re aiming for Q2 2018 instead.

 

I’ve been meaning to write this blog post for a couple of weeks, but frankly haven’t had time to find the time and mental energy for it.

As the title states, we’re moving the release up for Cathedral. Currently, we’ll aim for a Q2 2018 release instead. In this post, I figured I’d talk a bit about why we’re doing this, but also *how* we’re building Cathedral.

Why am I writing this on my personal blog, instead of the one at decemberborn.com? Basically, the thought was to keep the Decemberborn site for announcements, and this blog for personal musings around the game (and development in general).

Well. This is an announcement of course (so I’ll put links in from the main Decemberborn page), but for me, it’s also a personal matter and I’ll express a lot of personal reflections and opinions in this blog post. Over the last 3 years Cathedral has been built, I’ve met a lot of people, had a lot of fun and learned a hell of a lot about game design. On the other hand, it has also taken its toll in various ways.

 

The making of Cathedral, so far

As of 8th of December, 2017, it’s been 3 years since I committed the first lines of code on Cathedral. The idea was to build a game, running on a custom-made engine in C++. This was purely an exercise in fun for me, and at that point, I had no plans on making it a commercial game. I mean, I had a job that I loved, working as a developer/software architect.

I still have that job, and I still love that job. Cathedral has been the passionate side project that I’ve wanted to build for so long. It has had a very tiny budget so far (compared to what games have in general), in the ballpark of 25K Euro (roughly a bit under 30k USD at the time of writing). Everyone involved so far has either been hired as a freelancer, or been a founder and/or worked for equity. All of us who founded Decemberborn Interactive are still working full time for other companies.

Today, we’ve produced a game that have a main quest, a large world, side quests, collectibles, and tons of fun. We’re quite proud of what we’ve built so far, but we’ve also noticed the problem with trying to set a deadline on something that you’re trying to build in your spare time.

For me, the last year or so, has been an endless cycle of either working at my dayjob, or spending every minute when I get back in front of the computer, hacking on code, coordinating team members and building things. I haven’t slept enough, I haven’t thought about anything but what I wanted to build next in the game for a long time. Cathedral has been on my mind when I went to sleep. I’ve dreamt about it, and I’ve had ideas popping up in my head as soon as I woke up.

It’s been a fun ride, but it’s also been a very, very stressful and unhealthy one. Personally, I’ve noticed some less-than-desirable stress reactions, and have started thinking more about my mental health lately. Other people in the team has expressed similar concerns regarding stress, and I’ve had a feeling that morale is not always on top due to us putting all of our energy into stressing to our previous release date (Q4, 2017). We’ve all had tunnel vision, aiming towards this date (or well, quarter).

When the thing you love to do becomes one of your greater sources of negative stress, I think it’s time to take a step back, evaluate, and come back with a new plan. We still have a ton of user-testing we don’t want to skimp on. We still have known bugs that we want to fix, and while it honestly sucks to not be able to release this year, I still feel like I want to make my vision of Cathedral come as close as possible to being as good as it deserves to be. And I don’t mean good just in terms of gameplay. I mean good in terms of craftsmanship. I don’t want to release a incomplete game. I don’t want to release a buggy game either. I want to release something I can feel proud of, and that my team members feel equally proud of.

Most importantly, I want to release Cathedral without burning myself or my team members out in the process. We’re kind of hoping that we have more games in us after this one.

So, finally…

I have awesome team members at Decemberborn, and Cathedral would not exist if it wasn’t because I’ve teamed up with people I fully trust and love to work with.

I consider these people my friends, and I don’t want to burn their energy out anymore than my own. We have each other’s back, and everyone always try to come through and help each other out. At some point though, when everyone is feeling tired and out of energy, there’s only two options left in my opinion:

  1. Finish the game by more insane crunching and risk actual health issues.
  2. Realize that it’s counter-productive and harmful, and move the release up.

I choose option 2.

We will be taking a couple of weeks of break during Christmas and new year’s eve. See you all for a bright 2018. My new year’s resolution will be to make sure we record and release more gameplay footage, and as many details as possible, leading up to the release of the game.

Thanks for understanding, and happy holidays to everyone.

Rewriting the renderer for Cathedral

So. Slack is down, which means I can’t actually talk to my team mates right now. It’s 01:18 AM, and I’m sipping on a nice Nikka whisky after just solving a really stupid and annoying bug. I figured I might as well write my first blog post for this blog that I setup ages ago and never used. I’ve actually started writing a bunch of posts, but I can never seem to keep them short enough to have time to finish them up.

Just to be clear – I seldom have a goal when I start writing here. It’s usually just to clear my mind and get new ideas while working on Cathedral . Don’t expect things to always be coherent or worthwhile to read.

Anyway: In short –  Cathedral is built around an OpenGL-based renderer. Initially, we just made a 1:1-renderer in terms of drawcalls to sprites, and over the last few days, I decided to rewrite it.

Whenever I can rip out something vital of a game engine (or any other software project for that matter), rewrite it and plug the new thing in and just have it work, I always get a happy feeling. It’s like a really good indicator that I made the right call when it comes to the architecture. This time, it ALMOST worked that way. It worked fine on OSX, Debian and Ubuntu, but Windows just produced the dreaded black screen, meaning that something was wrong with my OpenGL state.

So, some background info: The initial renderer was never supposed to be used in production, but on the other hand, it actually scaled quite well, seeing how we’re rendering a bunch of quads and nothing else.

The engine that Cathedral is built on (we call it “Ganymede”), uses a quad tree to figure out what to render on-screen. We build our maps up using layers, where we have a designated collision layer. Anything below the collision layer is behind the camera, and anything in front of it is in front of the camera:

bonechurch

This has been working out surprisingly good, but we’ve designed a few levels with very dense screens. We have several layers of 16×16 tiles, at a resolution of 400×240, and some of these are parallax layers. First of all, for each non-parallax layer, that’s 375 tiles. Secondly, the way Ganymede renders parallax layers is by simply not tracking them in our quad tree. It’s usually a small enough portion of our maps so that keeping track of their spatial locations in a tree is more expensive than just brute forcing it (seeing how they change location as you move left/right)

So. In some areas, we might actually render a couple of thousand sprites getting rendered each frame. For the old renderer, this simply meant doing several thousand draw calls, rebinding vertex buffer objects on each frame, and a noticeable drop in performance, especially on low-end hardware such as cheaper laptops. After checking with apitrace, we noticed that we were doing a whopping 33k OpenGL calls per frame in our heavier areas. Of course, only part of these were draw calls, but still.

… So, what to do?

I decided to write a batch renderer instead. One of the things that put us in a good position for this was that we pack all of our textures into a texture atlas. It did produce a new set of issues:

  1. We don’t always use the same shader. Luckily, our “extra” shaders that we use for the default one is simply to apply things such as vertex colors and clipping on UI elements.
  2. We do  have some generated textures, and we needed to still have these outside of the texture atlas.
  3. We used to create all sprites at origin, and then transform them to the correct location

The first point could be partially solved by simply killing off our GUI shaders and add an extra attribute to our default shader. The only issue left was clipping (I’ll get back to that). The second issue could be solved by simply adding more texture samplers to the default shader.

Now, we still needed to solve the transformation part, as well as the clipping. And just to show you what I mean with having to clip things, here’s a shot of what our map should look like when the inventory screen is opened:cathedral-screenshot-2017-10-11-9_33_44

We originally did this completely on the GPU, using GLSL shaders (by simply discarding pixels outside of the defined boundaries). This went away as we built the batch renderer, since we needed as much as possible to NOT have to switch any vital state, such as shaders, textures (or at least not too frequently).

Imagine this UI without clipping. Or well. You don’t have to. Here’s a screenshot with clipping disabled:

cathedral-screenshot-2017-10-29-2_28_6

Kind of a mess. So to reiterate: We needed to solve clipping and we needed to solve transformations, and we needed to do it in as few draw calls as possible. And here’s the thing: Our data is pretty uniform. Everything (and I mean absolutely everything) in Cathedral is a quad. Each one needs 4 vertices and 6 indices.

So, we simply moved these operations over to the CPU. In essence, our batch renderer does this (and there’s a lot of details left out here. I’ve tried to keep it to the essentials only):

void begin() {
    // Keep track of how many things we have in our buffer
    idx = 0;

    // The vbo object is an array of 3 buffers that we switch
    // between to avoid locking up the renderer
    glBindBuffer(GL_ARRAY_BUFFER, vbo[bfrIndex]);

    // glEnableVertexAttribArray, glVertexAttribPointer etc:
    setupShaderInput();

    // Map a buffer that we can write to this frame
    buffer = static_cast<Vertex*>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
}

void batch(Surface* s, Time dt, const glm::mat4& view) {
    // For each of our 4 verts per surface, do:
    auto pos =
        transformVertex(view, s, glm::vec4(-1.0f, 1.0f, 0.0f, 1.0f));

    buffer->position = glm::vec4(pos.x, pos.y, pos.z, 1);
    buffer->color = color;
    buffer->texture = glm::vec2(uo, vo);
    buffer->sampler = slot;
    s->clip(buffer);
    buffer++;

    // ... We also have some extra flushing in here if we fill
    // up our buffer or run out of texture slots etc.

    // At the end, add the number of indices per sprite (6)
    idx += INDICES_PER_SPRITE;
}

void end() {
    // Unmap buffer and calculate the next buffer index
    glUnmapBuffer(GL_ARRAY_BUFFER);
    bfrIndex = (bfrIndex + 1) % BUFFER_COUNT;
}

… So, we do clipping on the CPU, we do transformations on the CPU, and batch everything into a single draw call. There’s not a whole lot of branching or anything like that going on in this code either, so for a game like Cathedral, this is pretty well-behaved. It’s measurably much much faster than the old approach (which again, wasn’t really meant to last forever anyway)

Clipping by the way, is basically just interpolation of each vertex, and since we never even rotate sprites in Cathedral (messes up the pixel art look!), we don’t even have to care about that. We can simply just clip along the x and the y axes, based on a clipping rectangle.

Oh, and by the way. After doing all this work, it worked fine on Linux and OSX (compiled on GCC and Clang, respectively). Windows however, was just a black screen (compiled with MSVC++ 2015). Apitrace saved the day and let me inspect my buffers (which were all thrashed by the way).

The issue was our vertex declaration, and A+ to you if you spot the issue:

namespace Ganymede {
   // I did something really stupid in this struct!
   struct Vertex {
        glm::vec3 position;
        glm::vec4 color;
        glm::vec2 texture;
        float     sampler;
    };
}

 

Anyway. Bug solved, time to sleep.