Un monde, enfinA world, at last
Un monde infini né d'une graine, dix blocs, une mousse sarcelle sous un ciel pervenche, et des montagnes qui flottaient dans le vide au premier essai.An endless world born from a seed, ten blocks, teal moss under a periwinkle sky, and mountains floating in mid-air on the first try.
Il y a enfin quelque chose à voir : un monde. Il est infini, généré à partir d'une graine, et on peut le survoler librement. C'est le jalon « Le monde voxel ». Dès le premier regard, j'ai voulu qu'on comprenne que ce n'est pas la Terre : la mousse qui couvre les collines est d'un sarcelle profond, et le ciel passe du pervenche au lilas vers l'horizon.
There is finally something to look at: a world. It is endless, generated from a seed, and you can fly over it freely. This is the “Voxel world” milestone. From the very first look, I wanted it to be clear that this is not Earth: the moss covering the hills is a deep teal, and the sky fades from periwinkle to lilac towards the horizon.

Le monde de départ mélange des océans et des plages, des collines couvertes de mousse, des crêtes rocheuses aux sommets enneigés et, dans les zones froides, une mer qui gèle en surface. Il est fait de dix blocs : pierre, roche sombre en profondeur, terre, mousse, sable, galets et argile au fond de l'eau, neige, glace et eau. Chaque bloc est décrit par un petit fichier de données, donc en ajouter un ne demande pas une ligne de code.
The starting world mixes oceans and beaches, moss-covered hills, rocky ridges with snowy peaks and, in cold areas, a sea that freezes over. It is made of ten blocks: stone, dark rock deep down, dirt, moss, sand, pebbles and clay on the seabed, snow, ice and water. Each block is described by a small data file, so adding one does not take a single line of code.
Toutes les textures font 8×8 pixels et piochent dans la palette fermée de 32 couleurs. Petite subtilité : elles ne sont pas stockées en couleurs, mais en numéros de couleur. C'est la carte graphique qui va chercher la teinte dans la palette du monde. La planète de départ en profite déjà : ses textures sont dessinées avec la palette de base, et sa palette à elle remplace les verts par des sarcelles et le bleu du ciel par du pervenche. Ces textures ne sont pas dessinées à la main pour l'instant : un petit outil que j'ai écrit les génère à partir de règles (couleurs autorisées, type de grain, motif), et je peux les retoucher ensuite.
Every texture is 8×8 pixels and draws from the closed 32-colour palette. One subtlety: they are not stored as colours but as colour numbers. The graphics card looks up the actual shade in the world's palette. The starting planet already takes advantage of it: its textures are drawn with the base palette, and its own palette swaps the greens for teals and the sky blue for periwinkle. These textures are not hand-drawn for now: a small tool I wrote generates them from rules (allowed colours, kind of grain, pattern), and I can touch them up afterwards.
La même graine donne toujours le même monde, au bloc près, et un test automatique le vérifie à chaque modification.
The same seed always gives the same world, down to the block, and an automated test checks it on every change.
Afficher le monde sans bégayer
Showing the world without a stutter
Même en solo, c'est le serveur qui fabrique le monde, et le jeu se contente de l'afficher. Le monde est découpé en cubes de 16 blocs de côté. Le serveur les génère sur plusieurs cœurs du processeur, en commençant par les plus proches du joueur, puis les envoie. La plupart de ces cubes sont uniformes (que de l'air, que de la pierre ou que de l'eau) et voyagent en quelques octets. Les autres sont compressés et ne pèsent que quelques centaines d'octets.
Even in solo, the server builds the world and the game just displays it. The world is cut into cubes 16 blocks on a side. The server generates them across several CPU cores, nearest to the player first, then sends them. Most of these cubes are uniform (all air, all stone or all water) and travel in a few bytes. The rest are compressed down to a few hundred bytes.
Côté affichage, le défi était d'en montrer environ 8 000 sans jamais faire bégayer l'image. Les faces identiques sont fusionnées en grands rectangles, si bien qu'un flanc de montagne plat ne coûte presque rien. Les coins enfoncés sont légèrement assombris, ce qui donne du relief sans éclairage coûteux. Un morceau n'est construit qu'une fois tous ses voisins connus : il apparaît correctement du premier coup, sans trou ni couture. Et la carte graphique reçoit les nouveaux morceaux par petites portions, un peu à chaque image.
On the display side, the challenge was showing around 8,000 of them without ever making the picture stutter. Identical faces are merged into large rectangles, so a flat mountainside costs almost nothing. Recessed corners are slightly darkened, which adds depth without expensive lighting. A chunk is only built once all its neighbours are known: it shows up correctly the first time, with no holes or seams. And the graphics card receives new chunks in small portions, a little every frame.
Pour vérifier tout ça, j'ai écrit des vols scriptés : un parcours de caméra joué automatiquement, qui mesure chaque image et compte les trous autour de la caméra. Ils ont payé dès le premier essai. Le premier enregistrement montrait des montagnes qui flottaient dans le vide : la caméra volait très haut, et le serveur n'envoyait le monde que sur une hauteur limitée autour d'elle. Le sol et la mer étaient tout simplement hors de portée. Le volume envoyé est maintenant aussi haut que large, et le brouillard prend la couleur du ciel pour cacher le bord du monde chargé.
To check all of this, I wrote scripted flights: a camera path played back automatically, measuring every frame and counting holes around the camera. They paid off on the very first run. The first recording showed mountains floating in mid-air: the camera was flying very high, and the server only sent the world within a limited height around it. The ground and the sea were simply out of reach. The volume sent is now as tall as it is wide, and the fog takes on the colour of the sky to hide the edge of the loaded world.
Sur ma machine, le vol tient les 165 images par seconde de mon écran, synchronisation verticale activée, du début à la fin et sans un seul trou près de la caméra.
On my machine, the flight holds my screen's 165 frames per second with vsync on, from start to finish, without a single hole near the camera.

Lex Games