Readit News logoReadit News
antirez · 13 years ago
Awesome implementation, actually no advanced feature is used, this code could easily ported even to assembler in any device where you can set pixels in RGB format.

And indeed it is pretty impressive how fast the rendering happens given that the code operates at such lower level... Even selecting the color of every pixel requires non trivial work in the inner loop.

AWESOME code.

jrogers65 · 13 years ago
I would argue that this is terrible code. It's not using the appropriate tools (webGL), it's badly structured, uncommented and unmaintainable (seriously, why on earth would anyone name their variables zd, _zd and __zd?). The Minecraft source code is also of notoriously poor quality. I honestly don't understand how you came to your conclusion.
mmcnickle · 13 years ago
The code is a direct port of Minecraft4k[1], it's optimised for space considerations.

[1] https://twitter.com/notch/status/275329867984302081

antirez · 13 years ago
It's an handful amount of lines of code doing a world generation, texture map generation, and ray casting rendering with a few tricks like a simple lightning model that still looks good.

All this using nothing more than a frame buffer, so you could port this easily from a C64 to any other computer. This code contains a lot of knowledge VS use of pre-build APIs.

If a programmer reads this code and understands how every part works, he or she ends knowing a lot more about computer graphics than before.

Jare · 13 years ago
Indeed, writing straightforward code with no dynamic memory allocations, and simple and predictable types, lets the VM do wonders with optimization and JITting. Good stuff.
WA · 13 years ago
I totally agree. You probably meant "code operates at such a _high_ level" :)

low level = close to hardware, high level = high abstraction, like JS

antirez · 13 years ago
No I really mean low level :-)

Javascript is high level, but here notch is using it as a low level framebuffer.

ukjadoon · 13 years ago
If the creator of Redis approves this, it's definitely awesome! =)
JoeCortopassi · 13 years ago
Such a great example of what can be done in javascript/canvas. As it is, I was completely blown away by how little code it actually took to do that. My only gripe would be, why couldn't he have used descriptive variable names, so I can better go through and understand it? :-)
antirez · 13 years ago
This code uses zero canvas / javascript specific functions. It is using basic trigonometric functions and is using canvas only to write RGB pixels. Canvas are much more powerful and high level than that, but this is not a good example as Notch just needed to set pixels.
petercooper · 13 years ago
Having watched Notch write Prelude of the Chambered (which I then ported to JRuby) it seems he really loves this direct pixel manipulation style - as do I!

The sad part, though, is it doesn't particularly scale well with canvas (yet) and using sprites, shape primitives, or even WebGL is way to get full speed at a regular resolution. (Imagine a 640x480 pixel field of randomized colors, you can get hundreds of FPS for that in Java without a sweat. Not so in the browser and most certainly not in JRuby.. my PotC port struggled to hit 12fps with equivalent code.)

The title of "ChamberedTest" here makes me wonder if notch is planning to use JS and canvas in the forthcoming Ludum Dare 25 (the gamedev contest where he created Prelude of the Chambered). I hope so!

DanielRibeiro · 13 years ago
Notch gave a little more context[1]:

Spent most of today learning new stuff. Ported Minecraft4k. Code is awful due to the nature of the project, but here: http://jsdo.it/notch/dB1E

[1] https://twitter.com/notch/status/275329867984302081

cgcardona · 13 years ago
Aren't the following canvas/javascript specific?

ctx = document.getElementById('game').getContext('2d');

pixels = ctx.createImageData(w, h);

ctx.putImageData(pixels, 0, 0);

EDIT: Thanks for the clarification!

tlrobinson · 13 years ago
Reminds me of when I wrote a Game of Life implementation in JavaScript in college using a bunch of tiny absolutely positioned <divs>.
joelthelion · 13 years ago
I would never have thought canvas performance would be good enough for this.
petercooper · 13 years ago
I've recorded a screencast that digs into how the programmatic texture generation works in this code: https://www.youtube.com/watch?v=WaZvDCmlERc
ozataman · 13 years ago
Thanks a lot for taking the time! Any chance you can do one for the game loop and the rest of the code?
unoti · 13 years ago
Enjoyed that immensely, thank you!
terhechte · 13 years ago
Great analysis of whats going on. Thanks.
chii · 13 years ago
great work.

it'd be also great to do an analysis of the geometry data as well, if you can!

petercooper · 13 years ago
Here's a rendering of the texture map so you can see what it generates directly: http://jsfiddle.net/cXvKa/
recurser · 13 years ago
Notch has made a couple of comments on reddit :

http://www.reddit.com/r/programming/comments/146v69/how_notc...

    Please do NOT write code like this. It was originally written for the Java4k 
    competition, which focuses on executable code size, and is as a result almost 
    intentionally poorly written. It got even worse when I ported it to JS.
    It was mainly meant as a test for me to see what HTML5/JS can do, and an 
    exercise in porting code over.
http://www.reddit.com/r/programming/comments/146v69/how_notc...

    It would run a bit smoother if it was written in C++ (mainly due to not having 
    to rely on the GC, and having it precompiled gets rid of the warmup time), and 
    modern OpenGL would help quite a lot as well. A lot of the stuff done in CPU 
    now could be moved to a shader, which both makes code simpler, and gets rid of 
    the slow JNI calls required now.
    The main reason why Minecraft is slow is mainly 1) There's a LOT of polygons, 
    combined with 2) The difficulty of making an efficient culling algorithm in a 
    dynamic world.
    If someone solves #2, very interesting things could be made with a game similar 
    to Minecraft.
http://www.reddit.com/r/programming/comments/146v69/how_notc...

    No, they don't get merged because the textures are all pulled from the same atlas 
    to avoid texture swapping. With GLSL, they could be merged, saving quite a lot of 
    polygons. For a while, I did attempt switching the atlas from a 16 x 16 grid to a 
    1 x 256 grid and merging horizontal runs of the same texture, but the resulting 
    texture size was to tall some graphics cards (on low end computers) would 
    automatically downsample it.
    The problem with the occlusion culling is not about knowing what parts are static, 
    but rather figuring out what occluders there are. It would be very beneficial not 
    to have to render caves under ground below the player, for example, or not to 
    render the entire outside when a player is inside a small closed house. Figuring 
    this out in runtime on the fly as the player moves around is.. expensive.

scottyallen · 13 years ago
This would be a great demo to see running in a responsive programming environment, ala Bret Victor's amazing Inventing on Principle talk (http://vimeo.com/36579366). I want to be able to click on hex values and get a color picker that updates the demo in realtime, and be able to click on various magic numbers and drag a slider to change them.

Incidentally, Bret Victor's talk was the starting point for Khan Academy's CS curriculum that John Resign led (http://ejohn.org/blog/introducing-khan-cs/).

Edit: I managed to get this running in livecoding.io, which does some of what Bret Victor was talking about (basic sliders and color pickers): http://livecoding.io/4191583. Not sure why it's running so much slower though...

enjalot · 13 years ago
Here is a version adapted for Tributary: http://tributary.io/inlet/4199801/

Press play in the bottom left to have it go, and scrub numbers/colors to your hearts content!

Part of the reason it slows down in livecoding is because of the way the original code uses setInterval, and every change reevaluates the code which polutes the scope and starts way too many threads going. I've added optional functionality to Tributary which gives you a run loop (using requestAnimationFrame) that doesn't polute anything.

Hope this helps!

scottyallen · 13 years ago
Very awesome:) It's _almost_ fast enough to feel like you're modifying it in realtime on my macbook air. This is a great demo of what might be possible with this responsive programming approach.
dsl · 13 years ago
This "responsive programming environment" you speak of has been around for a really long time. Don't expect sliders and colorpickers to do cool stuff, but this should be a good jumping off point for you to start learning from: http://stackoverflow.com/questions/3305164/how-to-modify-mem...
scottyallen · 13 years ago
Thanks, I'm plenty aware of gdb and debuggers:) (ex-google software engineer, etc etc) Go watch the video. Bret presents a very cogent vision for a dramatic improvement on most of today's standard engineering/design tools.
undergroundhero · 13 years ago
Thanks for the livecoding link. I assume there's some overhead in constantly checking for and applying code changes - that's probably the reason for the lower frame rate.
scottyallen · 13 years ago
Good point, I hadn't thought about the fact they might be polling for changes. Might be better if livecoding used an event based model to check for updates...
enjalot · 13 years ago
it's not polling, its reevaluating code when codemirror triggers an event for text changing (which uses the dom underneath).

the problem is from calling setInterval every time the code is evaluated, leading to way too many function calls/second

experiment0 · 13 years ago
Correct me if I'm wrong but does this procedurally generate the textures for each block. I can't see any code for loading assets and the init function, has some quite complicated color code. If so that is awesome!
antirez · 13 years ago
Yes, the procedure generates the texture of every "type" of block, the first loops after the init() function. 16 different types of blocks are generated.

Then a random map is created, with the "center" that is set to empty blocks with high probability. Finally a function traces the map on the screen at every tick of the clock.

andrewmunsell · 13 years ago
Ok, that's pretty cool :P

Deleted Comment

arriu · 13 years ago
I enjoy that everything is procedural generated. The software rasterizing is pretty cool too. I don't mind that he uses short variable names, sometimes it's nice to have multiple lines line up perfectly. But this is just silly...

    for ( var x = 0; x < w; x++) {
        var ___xd = (x - w / 2) / h;
        for ( var y = 0; y < h; y++) {
            var __yd = (y - h / 2) / h;
            var __zd = 1;

            var ___zd = __zd * yCos + __yd * ySin;
            var _yd = __yd * yCos - __zd * ySin;

            var _xd = ___xd * xCos + ___zd * xSin;
            var _zd = ___zd * xCos - ___xd * xSin;

albertzeyer · 13 years ago
Can you explain why?
codeka · 13 years ago
I assume because there's four different "xd" variables differing only in the number of underscores prefixed ("xd", "_xd", "__xd" and "___xd"). (Same for "yd" and "zd").
arriu · 13 years ago
I don't enjoy comparing the length of relatively similar lines. Why not use xa, xb, xc, etc... instead of xd, _xd, __xd, ___xd, yd, _yd, __yd, ___yd, zd, _zd, __zd, ___zd?