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.
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.
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.
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? :-)
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.
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.
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.
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.
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.
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...
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.
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...
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.
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!
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.
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;
Very cool, but a few notes to anyone getting their hopes up on the powers of Canvas:
As you may have anticipated, this demo runs at less than 1 frame per second on my Nexus 7. :/
In terms of per-pixel manipulation of a Canvas, it is very slow, even with javascript's (not very well supported) typed arrays. Simply put, multidimensional loops (unrolled or not) will always kill performance in JS with any significant dimensions. I learned this the hard way by trying to write a pixel-based GUI, and even that (with some crazy optimizations) could not render fast enough for all devices. If you are just doing blits, Canvas works very well (especially since this operation often uses the GPU).
For now, I think the best option is still WebGL, even though it is not widely supported yet, mobile devices are beginning to pick it up (Blackberry, for example).
That's faster than the JS demo (even the improved one) in any browser. It's sad that browsers still don't have at least the same performance as flash :(
While this is cool, isn't this exactly what WebGL is for? I know drivers/support is an issue, but Chrome also ships with a multicore software renderer (SwiftShader) which can probably get a lot further than a JS putImageData engine.
The first set of three nested loops is procedurally generating texmap, which is a multidimensional Array(16 * 16 * 3 * 16). 16x16 pixel textures, one for each the top, sides, and bottom, and then 16 of those. The inner loop tests against i, which is the current block "type" and performs customizations to the procedurally generated textures.
The next set of three nested loops is generating a random "world" with a tunnel cut out of it.
The renderMinecraft function is performing a minimal perspective projection http://en.wikipedia.org/wiki/3D_projection for a single ray cast into the world. Each pixel is cast and then, for each object hit by the ray cast, the closest is found, and a texture mapped pixel is calculated and written into the frame buffer.
I've recorded a screencast that digs into how the programmatic texture generation works: https://www.youtube.com/watch?v=WaZvDCmlERc (but I'm not touching the 3D.. yet ;-))
I personal do this for legibility and ease of comprehension. Kinda loses its value when dealing with such nicely rounded numbers but, as an example, 1000ms / 24 is easier or me to comprehend as 24 ticks per second than 41.6666ms.
Tooting my own horn here, but here's my own little "Minecraft" renderer in Neja, circa 2005 ( http://www.youtube.com/watch?feature=player_detailpage&v... ). Over 4 years before Minecraft, and even then that was nothing new. It was kinda cool for JavaScript, in 2005, but all things considered, it was pretty lame compared to Ken Silverman's voxlap for instance.
The "cool" thing about this demo, was that Canvas was not widespread back then so I generated all the chunky effects on the fly as 24bits BMP image, then made a data: URI and update an IMG tag.
"Notch coding in JS" is a bit more descriptive than "trying jsfiddle", specially since it looks like code posted after the fact. A minecraft-like env rendered on canvas with it's own mini-3d-engine is amazing nonetheless :D
This is actually quite fluid on iPhone 5. I asked a co-worker to try this on his Galaxy S3(quad-core, no lte) and it was really slow there. And in Chrome it was even slower than that on the S3.
Why is the framerate poor even when I shrink the viewing area down 10x10? It seems to have the same stutter as the original large view. Is it because it's doing the same calculations but just showing fewer pixels?
Yes. It's the calculations and direct pixel setting that's slow, not the actual "rendering" of the canvas (which is accelerated like crazy in most browsers now).
[+] [-] antirez|13 years ago|reply
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|reply
[+] [-] Jare|13 years ago|reply
[+] [-] WA|13 years ago|reply
low level = close to hardware, high level = high abstraction, like JS
[+] [-] ukjadoon|13 years ago|reply
[+] [-] JoeCortopassi|13 years ago|reply
[+] [-] antirez|13 years ago|reply
[+] [-] petercooper|13 years ago|reply
[+] [-] ozataman|13 years ago|reply
[+] [-] unoti|13 years ago|reply
[+] [-] terhechte|13 years ago|reply
[+] [-] chii|13 years ago|reply
it'd be also great to do an analysis of the geometry data as well, if you can!
[+] [-] petercooper|13 years ago|reply
[+] [-] recurser|13 years ago|reply
http://www.reddit.com/r/programming/comments/146v69/how_notc...
http://www.reddit.com/r/programming/comments/146v69/how_notc... http://www.reddit.com/r/programming/comments/146v69/how_notc...[+] [-] scottyallen|13 years ago|reply
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|reply
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!
[+] [-] dsl|13 years ago|reply
[+] [-] undergroundhero|13 years ago|reply
[+] [-] experiment0|13 years ago|reply
[+] [-] antirez|13 years ago|reply
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.
[+] [-] unknown|13 years ago|reply
[deleted]
[+] [-] arriu|13 years ago|reply
[+] [-] albertzeyer|13 years ago|reply
[+] [-] moconnor|13 years ago|reply
Part of me can't decide if that's awesome or tragic.
[+] [-] gavanwoolery|13 years ago|reply
As you may have anticipated, this demo runs at less than 1 frame per second on my Nexus 7. :/
In terms of per-pixel manipulation of a Canvas, it is very slow, even with javascript's (not very well supported) typed arrays. Simply put, multidimensional loops (unrolled or not) will always kill performance in JS with any significant dimensions. I learned this the hard way by trying to write a pixel-based GUI, and even that (with some crazy optimizations) could not render fast enough for all devices. If you are just doing blits, Canvas works very well (especially since this operation often uses the GPU).
For now, I think the best option is still WebGL, even though it is not widely supported yet, mobile devices are beginning to pick it up (Blackberry, for example).
[+] [-] zzzzzzzzz|13 years ago|reply
[+] [-] spyder|13 years ago|reply
[+] [-] tgandrews|13 years ago|reply
[+] [-] AshleysBrain|13 years ago|reply
[+] [-] Raticide|13 years ago|reply
[+] [-] Pezmc|13 years ago|reply
[+] [-] snprbob86|13 years ago|reply
The next set of three nested loops is generating a random "world" with a tunnel cut out of it.
The renderMinecraft function is performing a minimal perspective projection http://en.wikipedia.org/wiki/3D_projection for a single ray cast into the world. Each pixel is cast and then, for each object hit by the ray cast, the closest is found, and a texture mapped pixel is calculated and written into the frame buffer.
[+] [-] petercooper|13 years ago|reply
[+] [-] phete|13 years ago|reply
using window.requestAnimationFrame just because we can!
[+] [-] Aissen|13 years ago|reply
[+] [-] shocks|13 years ago|reply
edit: Thanks guys!
[+] [-] anatoli|13 years ago|reply
The usefulness is more obvious when you want 60FPS, which is not easily represented without rounding.
[+] [-] dag11|13 years ago|reply
Similarly, people will write (10 * 60 * 60) if the value is seconds and the programmer intends to indicate 10 hours.
[+] [-] estel|13 years ago|reply
[+] [-] sisk|13 years ago|reply
[+] [-] Riesling|13 years ago|reply
[+] [-] p01|13 years ago|reply
The "cool" thing about this demo, was that Canvas was not widespread back then so I generated all the chunky effects on the fly as 24bits BMP image, then made a data: URI and update an IMG tag.
[+] [-] ricardobeat|13 years ago|reply
[+] [-] felixthehat|13 years ago|reply
[+] [-] Flow|13 years ago|reply
Doesn't any web browser on Android use a JIT?
[+] [-] JungleGymSam|13 years ago|reply
[+] [-] kevingadd|13 years ago|reply
[+] [-] petercooper|13 years ago|reply
[+] [-] jQueryIsAwesome|13 years ago|reply
[deleted]
[+] [-] unknown|13 years ago|reply
[deleted]