Thursday 30 June 2011

Quantum Bricks

New brick occlusion code is in - well, actually old block-based occlusion has been updated to correctly work with bricks. Its sub-optimal at the moment - and could be sped up by caching and reusing previous results to significantly reduce the work it does.

Also, the brick template doesn't have an occlusion cache, which I'm going to add in future brick data so it doesn't have to be derived from the brick collision. This will also make supporting transparent blocks easy too.

Being GPU bound, the occluded bricks have taken a lot of load off of the poor GPU at a very small CPU overhead so really makes a difference. They also reduced the VBO construction CPU spike as it iterates over fewer bricks to build the buffer, this has a huge impact

In general development, each baseplate object represents a 16x16 studded area with an AddBrick()/RemoveBrick() interface, as well as a smart pointer to the surrounding four baseplates. It has containers for AllBricks and VisibleBricks, as well as block data used for occlusion and collision. While it seemed like a good idea at the time, this one class is now running most of the game (VBO+Rendering, Collision, Occlusion, Streaming...) and I'm uncomfortable about it having so many responsibilities even if a lot of the work is done by external classes. In general, its a non-issue as the baseplate is constructed and configured by a factory, and managed as needed by the correct system(s) but the part of me that knows its wrong wants to fix it before it breaks rather than after.



Monday 27 June 2011

Factory

Earlier I was contemplating protected constructors with friend class factories for a couple of classes that really shouldn't be instantiated without configuring.

Clearly this is feedback from The Code. Its trying to tell me that the class in question is too bloated and complicated, with too many responsibilities, methods or members.
I guess the first step is components? I could refactor into components that would have more granular unit tests I guess. But then I'd have to atomically construct several components for it to work - which I'd do in a factory. The system under test would be made of a graph of interconnecting objects. Am I unfairly prejudiced against irreducible complexity, or is this the way of the future?

The question has arisen because I have a bug in an object that requires several steps to initialise. While I may have been aware of this, it wasn't until adding a unit test did I really notice that I didn't know how to construct and configure the object. My factory knows the magic to make it happen, but its not obvious from the interface which is a problem to address.

Now I've spotted the problem I'm going to fix it so that Future Roger will like my code more than I currently like it.


Sunday 26 June 2011

Hidden secrets

The first optimisation path I tried when I had a world made of blocks was a block-based occlusion scheme. Since I know whether each block is occluding or not, its easy to check the six neighbours of a block and mark it as occluded.
Occluded blocks are removed from the visible set, and their vertex data is ignored for VBO construction so they lighten the load on the GPU at the cost of a small CPU and memory overhead.
In early versions occlusion data could be frozen - allowing you to manually check for problems and the scene could be rendered in wireframe, which again shows up if too many blocks are rendered. Both of these visual tests were removed when I changed from regular uniform cubes to irregular cuboid blocks, and somewhere along the line the occlusion check broke too.

Secondly, I'm rendering quite a lot of large Vertex Buffer objects and have just added a vertex dot product check to only render the ones that are a bit onscreen - this is all simple stuff but does serve as a reminder that I've built a lot of code from scratch and the rendering - until now - has all been placeholder.

As I'm approaching prototype, the graphical look and feel and specifically the framerate are turning up as items that need to be addressed as the project shifts gear from a whimsical code experiment to a functional tech demo. Thrashing the GPU with millions of polys isn't quite acceptable, and 100ms CPU spikes for vertex transforms or perlin noise gerneration are being smoothed out too.

The usual suspects are being wheeled out in the form of LOD models, fog, environment skybox et. al. in preparation for a prototype release. Like the previous development, each of these will only have to exist in its crudest state so I can cross them off as bug-free and feature-complete, two thirds of the development trinity. Agile Homebrew has the advantage of hiding the third "on-schedule" bar of the triangle and turning it into a non-issue. As with everything, it'll be done when its done.

The entire of Sprint 14 looks like render optimisations with no end in sight, but I'll probably get it running a bit faster and then cast my eye at the next set of tasks. I'm already drafting a list of what I want to see in Sprint 15.




Tuesday 21 June 2011

Slightly More Technical

Axis-aligned building blocks provide a number of advantages and cheap optimisations for collision, occlusion and rendering. This adds to the gameplay and development reasons for using building blocks.
The pioneer world is divided into chunks that are sixteen blocks square. Each chunk is processed atomically and owns a vertex buffer object that it maintains when a brick is added or removed from it.

Whole chunks are constructed/deconstructed by a Horizon object that keeps track of which chunk the player is standing in and ensures a few chunks in each direction are visible. This allows very large landscapes to be represented and to stream in and out to allow seamless travel over long distances. It also provides a base for distance culling to scale the renderer to match the performance of different computers.
Each brick is an arrangement of blocks that can be atomically added to or removed from a chunk.
Each chunk contains a list of the bricks inside it, and each time the brick list changes the brick list is iterated over to construct a Vertex Buffer Object for the chunk.

Because of the chunk architecture, its not yet possible to place a brick so that it spans two or more chunks. Each brick must lay entirely within one chunk. This restriction has proved pretty annoying when building, and does need to be resolved.

Overview

Pioneer is a game development experiment written in C++ using OpenGL and SDL. The unusual development approach taken is to code before design, instead of the other way around. To set this up I’m starting with a development sandbox to try ideas in.

Since the entire project is a construction toy, It seemed appropriate to make the environment a construction toy, similar to popular plastic building blocks. This provides a direction for development and a surprisingly specific set of goals, functionality and user-stories that can be summarised as “a world made of bricks”

The second advantage of making a world out of building blocks is it provides inspiration for gameplay mechanics. Just looking at a brick-based landscape calls to mind what you could do with buildings, bridges and castles or with roads, trains and planes. Each idea can be tried out as a gameplay experiment, and either forge a direction for the project or form a fork that can be worked on in isolation.

The third advantage of using building blocks is one of nostalgia. If you have an affinity for the construction medium or just fond memories, the environment becomes easy to pick up and to visualise constructions.

The prototype is nearly finished and aims to provide a sandbox environment for construction with a reasonable UI.