woensdag 15 januari 2014

VoxelJS in acht zorgen

Na wat surfen op YouTube kwam ik een talk tegen van Max Ogden met de de titel "Minecraft.js". Geen idee wie de gast is, maar hij heeft een Baard als Josef en draagt een bril. (Geloof me, als je zijn baard ziet, zou je het ook een hoofdletter geven.) Mijn Geek-sense deed tinglee, dus ik kijken.

Zijn talk begint over het onstaan VoxelJS, een javascript library om zelf minecraft-achtige games te maken in de browser. Puur Javascript en WebGL. Me likey-likey. Als begin van zijn project besloot hij alles te downloaden van Github wat ook maar iets met Minecraft en Javascript te maken had, want dat is wat je doet als je begint met een project: Github fetchen, right? Riiight. Wat hij vond was een hele hoop troep. (Wat raar.)

Maar gelukkig vond-ie ook de repository van Mikola Lysenko die een algoritme heeft bedacht om uit Minecraft-achtige voxel-data een efficiente 3d mesh te generen: De basis van elke voxel-engine. Als hij gewoon gegoogled had op "minecraft meshing" dan was het zijn eerste hit geweest. Tsja, hij besloot te Githubben. Hier onstond de eerste zorgen.

Dat filmpje duurt 43 minuten, duurt wel lang denk ik. Ik besluit een nieuw tabje te openen, want zijn baard had al genoeg bewondering van me gehad en op geluid alleen kon ik het prima volgen. Ik ga naar voxeljs.com. Op alinea 2 lees ik het volgende: "It was written by @maxogden and @substack, two non-game developers that want to make voxel games easy, fun and modular." Ik denk, amagawd .. Non-game developers and they tweet! Zorgen twee en drie.

Zijn verhaal gaat verder over de verschillende manieren om een Minecraft wereld te genereren. Je kan voor elke voxel keihard een box plaatsen met 6 zijden, 12 faces en tot 24 vertices maar dat zou gigantisch veel geheugen eten omdat Minecraft-achtige spellen vaak miljoenen voxels bevatten. Je kan bijvoorbeeld ook alleen de boxen plaatsen op de buitenste laag voxels, de speler zal het verschil niet zien. Op dit punt praat hij alleen nog maar Mikola na. Mikola's blogpost hierover is zeker meer de moeite waard.

Ik scroll verder op de website en zie een hele hoop modules, klaar voor gebruik. Ik denk, dit zou wel eens kunnen werken. En met Mikola's meshing engine in de basis van VoxelJS besloot ik toch maar eens te kijken. Ik ga naar de Github pagina en lees een beetje wat er is. Een typische documentatie pagina die je regelmatig vind op Github, niets mis mee. Alle duiveltjes van een voxel engine zijn aanwezig: Events bij het plaatsen/weghakken van blokken, Api's om blokken te plaatsen, raycasting etc etc. Maar dan kom ik bij de Style guide terecht:
basically https://github.com/felixge/node-style-guide#nodejs-style-guide with a couple of minor changes:
  • no semicolons
  • single line ifs/fors when appropriate for terseness
  • no hard to understand for n00bs 'tricks' unless they are faster


Wat?! Geen puntkomma's!? If- en For blokken op enkele regel is never appropriate! Daarnaast predikt de "node-style-guide" als gebod nummer 1 om twee spaties te gebruiken voor indentatie. Er is een reden waarom we het tab-tekentje gebruiken voor indentatie meneer Geisend√∂rfer! Wanneer je een regel code uitcommentarieerd met "//", dan blijft de indentatie afstand op die regel gelijk. Ik snap dat je de wereld wilt redden van code waar tabs en spaties gemixed zijn, maar ik denk niet dat genocide op het tab-tekentje de oplossing is. Zorgen vier.

VoxelJS is helemaal de NPM-way gebouwd, dus wanneer je iets daarmee wilt moet je ook helemaal de NPM-way gaan. Ik maak een NPM-module directory compleet met package.json. Installeer alle nodige VoxelJS modules als dependencies. Ik start met de voxel-hello-world module, draai browserify en run het in de browser.

Het werkt! Ik sta op een bal van 20 bij 20 blokjes. Ik kan hakken en blokjes plaatsen. Nice! Ik val van de bal af.. Crash! Niet zo nice. Ik reload mijn tab en spring deze keer direct van de bal af. Weer crasht mijn tab. Na een aantal reloads is het zeker: Het vallen uit de wereld is letterlijk fataal voor mijn tabs. Een van de modules bij VoxelJS heet "voxel-rescue". De module transporteert de speler naar het startpunt wanneer het een bepaalde (lage) y-positie bereikt, dus wanneer je uit de bodem van de wereld valt. Ik vond het al een typische naam "voxel-rescure", zoiets noem je toch eerder "voxel-respawn"? Maar gezien deze bug, begreep ik de naam beter. Want het red je browsertab van een crash omdat je dan niet meer tegen de bug aan loopt. Zorgen vijf.

Max gaat verder over hoe hij met zijn maat Substack moeite had om de meshes te texturen. Want Mikola's algoritmen spuugde geen UV-coordinaten uit. Really?! Re-he-he-eally? In een Minecraft wereld is elke vlak axis-aligned en bevat slechts 1 uit 6 normals. Met andere woorden: je hebt geen UV-coordinaten nodig! Je hebt alleen maar 6 matrices nodig voor de 6 kanten van de box. Elke matrix transformeert de 3d-wereld coordinaten naar 2d coordinaten in "vlak"-space, fract() erover en het zijn UV-coordinaten. 

In plaats daarvan gebruikt hun "algoritme" de vier rauwe vertex posities van een vlak die worden omgevormd naar een breedte en hoogte vector (punt 0,0 - punt 1,0 en punt 0,0 - punt 0,1) met behulp van if-jes. Letterlijk als in: `if(size.x === 0) u = size.y && v = size.z;` (Wanneer mijn vlak 0 breedte heeft, dan bepaalt hoogte de hoogte van de texture en de diepte de breedte van de texture). Zorgen zes.

Maar dankzij Mikola's mesher, hoeft dat stukje aanzienlijk minder vaak aangeroepen worden dan je zou denken, de impact is minder groot. En is eigenlijk best wel schattig: Het doet me denken aan mijn allereerste "move to point"-code wat ik ooit geschreven heb. Het was de code in een RTS die ervoor zorgt dat je unit naar een bepaalde plek toe gaat. In die tijd zat ik nog op de middelbare school, begreep niets van vectoren, lette niet op tijdens wiskunde en deed maar wat. (Shit, wat een heerlijke tijd was dat!) Mijn algoritme deed ongeveer het volgende:
//elke frame
if(doel.x < unitpositie.x) {
    unitpositie.x -= 1;
} else if(doel.x > unitpositie.x) {
    unitpositie.x += 1;
}
if(doel.z < unitpositie.z) {
    unitpositie.z -= 1;
} else if(doel.z > unitpositie.z) {
    unitpositie.z += 1;
}
Deze methode zorgde ervoor dat mijn units altijd recht of diagonaal over het veld verplaatsde. Tegenwoordig zou ik gewoon de genormaliseerde relatieve vector nemen tussen `unitpositie` en `doel` en dat vermedigvuldigen met de framerate-aangepaste snelheid. De hacky UV solver kan ik wel vergeven.

Ik skim verder door de source van VoxelJS en ik kom erachter dat de geoptimaliseerde meshing algoritme van Mikola niet eens gebruikt word! Mikola's meshing package heeft 4 tal meshing methoden: Stupid, Culled, Greedy en Monotone. De eerste twee methoden zijn super inefficent en eigenlijk alleen bedoeld ter ondersteuning van zijn blogpost voor screenshots en benchmark vergelijkings materiaal. VoxelJS maakt gebruik van Culled. De Culled versie gebruikt alleen de zijden van de boxen die lucht aanraken. Eigenlijk de minimale optimalisatie die je zou verwachten bij een dergelijke game. Ik vervang Culled voor Greedy en ja hoor: De UV-coordinaten helemaal frox! Zorgen zeven.

Na een aantal avondjes stoeien met VoxelJS blijkt het behoorlijk lastig te zijn om daadwerkelijk een minecraft achtig game in elkaar te hakken. Bijna elk aspect aan Minecraft bestaat wel in een of andere voxel-* module vorm. Dus dat zou niet al te lastig moeten zijn. Maar de verschillende modules werken allemaal weer met andere versies van voxel-engine. Na wat gegoogel op Google, kwam ik het volgende blogpost tegen van Kyle "Shama" Robinson Young. Die precies de kutheid beschrijft waarmee ik al een een aantal avonden te kampen had. Zorgen acht!

Tijd om af te haken en VoxelJS te ditchen! Thanks but no thanks!

Ik geloof dat Minecraft het begin is van een hele nieuwe game genre, net zoals The Rise of Triad ooit was voor FPS. Ik geloof ook dat op een dag, al onze software via een browser gaat draaien. Alleen professionals die de extra rekenkracht/hardware toegang nodig hebben zullen native apps blijven gebruiken. Dus zoiets als Minecraft in de browser gaat er gewoon komen. (Minecraft draait nu al de browser als applet, maar dat is wat anders.) Daarom waag ik zelf een poging om dit voor elkaar te krijgen. Eens kijken hoe ver een actual game developer die niet aan twitter doet komt.

Wordt vervolgd.

Geen opmerkingen:

Een reactie posten