Hexal Engine Demonstration

Hexal Engine is an isometric rendering engine that presents an array of objects (called "hexals") in an axial grid.
Controls: Pan with mouse (click & drag) or arrow keys; Zoom in/out with +/-; Move slice with </> or PgUp/PgDn
For the best experience, view in Chrome. Not intended for mobile devices. See below for an explanation of the project. Github Source

If you can read this, your browser is either out of date or does not support HTML5 Canvases. To fix this, simply download the latest version of Chrome, Firefox, Internet Explorer, Safari, Opera, or any other HTML5 canvas-compliant browser.

Purpose

This project is primarily an experimentation with the canvas API, but it also serves as routine exercise in healthy development patterns and practices. Needless to say it is also a study in vanilla JavaScript.

Technical Details

Technology
Hexal Engine is written exclusively in vanilla JavaScript and renders via the HTML5 canvas element. As such, only modern, up-to-date browsers will be able to view this demonstration performantly. The engine exports an UI-API that is wired to browser events via JQuery (it is up to the implementing programmer to decide how best to trigger the UI-API; I use JQuery here to save time).

Object Structure
Hexal Engine has two primary datastructures: the Hexal Engine prototype and the subordinate Hexal prototype. The latter is a small data collection used to track points in 2-space, the abstract component of the visible hexagons in the demo. The other prototype, Hexal Engine, does the vast majority of the work and is responsible for organizing, manipulating, and rendering Hexals as hexagons (it even knows how to create the hexagon rasters).

Axial Coordinates
Axial coordinates are very similar to cartesian, except the two axes are not at a right angle. Hexal Engine uses axial coordinates to place hexagons, which produces a coordinate system that is much more intelligible than a staggered cartesian system (which was the implementation I chose in a preceding version.) Uniquely identifying Hexals is a slightly more laborious task, but can be achieved by counting outwards from the center of the map in rings (the id is derived by a finite geometric sum, so the calculation is neat and performant. Obligatory: "I used Real Analysis for something!")

Graphics Processing
  • The hexagons that Hexal Engine renders are not, indeed, true hexagons, because of the unfortunate fact that true hexagons have irrational side lengths. As such, they can not be rendered in discrete pixel units; that is, to render a true hexagon would be to trigger antialiasing (which can not be disabled in all browsers) and/or cause graphical artifacts on hexagon boundaries.
  • Because of performance issues, extreme care must be taken when rendering anything to a canvas. The typical practice of prerendering in many steps is completely untenable in most browsers, especially since hardware acceleration is not always invoked opportunistically. Hexal Engine subdivides the world into rectangular units ("chunks") which are prerendered a single time and cached as the client pans the map. The initial prerender step causes some lag but also triggers hardware acceleration, moving the raster images to the GPU and increasing draw speed thereafter (provided this is how the client behaves--it's a bit of a hack and won't necessarily work for all browsers). The chunks, by nature of being larger than individual hexagons, also allow large swaths of the map to be rendered in fewer steps (the canvas drawImage method has a significant amount of overhead, so volume of calls weighs more than size of images.) Thus, by "chunking" the map into appropriately-sized units, Hexal Engine is able to increase performance by minimizing the number of draw steps per frame and by triggering GPU acceleration.
  • Hexal Engine only does as much work as it needs to. When chunks pan off-screen, they are not rendered, which is to be expected. Moreover, each draw step merely copies and translates the already-rendered screen, utilizing the preceding draw step as a sort of prerender step for the next. Hexal Engine must then only fill in the gaps along the edges of the viewport, achieved by pasting chunks where appropriate. This step of translating the scene globs many draw operations into one, improving performance significantly.
  • A noteworthy feature of Hexal Engine is that it does not rerender chunks when the map is rescaled (zoomed). This was, at one point, an implementation feature that has since been replaced with clever css tricks: By altering the canvas width and height attributes independently of the css attributes of the same name, the browser will happily render a scaled-up or -down version of the scene very performantly. The only drawback of this method is the resolution does not change, so the quality of the image becomes distorted.
  • The most complex aspect of Hexal Engine is rendering visible Hexals below the surface that may be partially obscured, creating the illusion of depth (2.5D, isometric display, psudo-3D, etc.) This is achieved through carefully optimized sampling, where the engine chooses points in space and "drills down" through the map until it finds an occupied space (think raycasting). The engine uses a few mathematical calculations (including GCD; obligatory: "I used Number Theory for something!") to minimize the number of points that must be sampled to the absolute minimum. This optimization results in a tidy performance gain, though this is hardly noticable with intelligent chunking (every little bit counts though, and the technology is all the more "really cool" because of it). Currently, there is a literal edge-case bug where hexagons can become cut-off at chunk boundaries, most likely because the sampling is not performed relative to the correct origin.