Code Monkey home page Code Monkey logo

voxelspace's Introduction

Voxel Space

web demonstration

History

Let us go back to the year 1992. The CPUs were 1000 times slower than today and the acceleration via a GPU was unknown or unaffordable. 3D games were calculated exclusively on the CPU and the rendering engine rendered filled polygons with a single color.

Game Gunship 2000 in 1991 Game Gunship 2000 published by MicroProse in 1991

It was during that year NovaLogic published the game Comanche.

Game Comanche in 1992 Game Comanche published by NovaLogic in 1992

The graphics were breathtaking for the time being and in my opinion 3 years ahead of its time. You see many more details such as textures on mountains and valleys, and for the first time a neat shading and even shadows. Sure, it's pixelated, but all games in those years were pixelated.

Render algorithm

Comanche uses a technique called Voxel Space, which is based on the same ideas like ray casting. Hence the Voxel Space engine is a 2.5D engine, it doesn't have all the levels of freedom that a regular 3D engine offers.

Height map and color map

The easiest way to represent a terrain is through a height map and color map. For the game Comanche a 1024 * 1024 one byte height map and a 1024 * 1024 one byte color map is used which you can download on this site. These maps are periodic:

periodic map

Such maps limit the terrain to "one height per position on the map" - Complex geometries such as buildings or trees are not possible to represent. However, a great advantage of the colormap is, that it already contains the shading and shadows. The Voxel Space engine just takes the color and doesn't have to compute illumination during the render process.

Basic algorithm

For a 3D engine the rendering algorithm is amazingly simple. The Voxel Space engine rasters the height and color map and draws vertical lines. The following figure demonstrate this technique.

Line by line

  • Clear Screen.
  • To guarantee occlusion start from the back and render to the front. This is called painter algorithm.
  • Determine the line on the map, which corresponds to the same optical distance from the observer. Consider the field of view and the perspective projection (Objects are smaller farther away)
  • Raster the line so that it matches the number of columns of the screen.
  • Retrieve the height and color from the 2D maps corresponding of the segment of the line.
  • Perform the perspective projection for the height coordinate.
  • Draw a vertical line with the corresponding color with the height retrieved from the perspective projection.

The core algorithm contains in its simplest form only a few lines of code (python syntax):

def Render(p, height, horizon, scale_height, distance, screen_width, screen_height):
    # Draw from back to the front (high z coordinate to low z coordinate)
    for z in range(distance, 1, -1):
        # Find line on map. This calculation corresponds to a field of view of 90°
        pleft  = Point(-z + p.x, -z + p.y)
        pright = Point( z + p.x, -z + p.y)
        # segment the line
        dx = (pright.x - pleft.x) / screen_width
        # Raster line and draw a vertical line for each segment
        for i in range(0, screen_width):
            height_on_screen = (height - heightmap[pleft.x, pleft.y]) / z * scale_height. + horizon
            DrawVerticalLine(i, height_on_screen, screen_height, colormap[pleft.x, pleft.y])
            pleft.x += dx

# Call the render function with the camera parameters:
# position, height, horizon line position,
# scaling factor for the height, the largest distance, 
# screen width and the screen height parameter
Render( Point(0, 0), 50, 120, 120, 300, 800, 600 )

Add rotation

With the algorithm above we can only view to the north. A different angle needs a few more lines of code to rotate the coordinates.

rotation

def Render(p, phi, height, horizon, scale_height, distance, screen_width, screen_height):
    # precalculate viewing angle parameters
    var sinphi = math.sin(phi);
    var cosphi = math.cos(phi);

    # Draw from back to the front (high z coordinate to low z coordinate)
    for z in range(distance, 1, -1):

        # Find line on map. This calculation corresponds to a field of view of 90°
        pleft = Point(
            (-cosphi*z - sinphi*z) + p.x,
            ( sinphi*z - cosphi*z) + p.y)
        pright = Point(
            ( cosphi*z - sinphi*z) + p.x,
            (-sinphi*z - cosphi*z) + p.y)
        
        # segment the line
        dx = (pright.x - pleft.x) / screen_width
        dy = (pright.y - pleft.y) / screen_width

        # Raster line and draw a vertical line for each segment
        for i in range(0, screen_width):
            height_on_screen = (height - heightmap[pleft.x, pleft.y]) / z * scale_height. + horizon
            DrawVerticalLine(i, height_on_screen, screen_height, colormap[pleft.x, pleft.y])
            pleft.x += dx
            pleft.y += dy

# Call the render function with the camera parameters:
# position, viewing angle, height, horizon line position, 
# scaling factor for the height, the largest distance, 
# screen width and the screen height parameter
Render( Point(0, 0), 0, 50, 120, 120, 300, 800, 600 )

More performance

There are of course a lot of tricks to achieve higher performance.

  • Instead of drawing from back to the front we can draw from front to back. The advantage is, the we don't have to draw lines to the bottom of the screen every time because of occlusion. However, to guarantee occlusion we need an additional y-buffer. For every column, the highest y position is stored. Because we are drawing from the front to back, the visible part of the next line can only be larger then the highest line previously drawn.
  • Level of Detail. Render more details in front but less details far away.

front to back rendering

def Render(p, phi, height, horizon, scale_height, distance, screen_width, screen_height):
    # precalculate viewing angle parameters
    var sinphi = math.sin(phi);
    var cosphi = math.cos(phi);
    
    # initialize visibility array. Y position for each column on screen 
    ybuffer = np.zeros(screen_width)
    for i in range(0, screen_width):
        ybuffer[i] = screen_height

    # Draw from front to the back (low z coordinate to high z coordinate)
    dz = 1.
    z = 1.
    while z < distance
        # Find line on map. This calculation corresponds to a field of view of 90°
        pleft = Point(
            (-cosphi*z - sinphi*z) + p.x,
            ( sinphi*z - cosphi*z) + p.y)
        pright = Point(
            ( cosphi*z - sinphi*z) + p.x,
            (-sinphi*z - cosphi*z) + p.y)

        # segment the line
        dx = (pright.x - pleft.x) / screen_width
        dy = (pright.y - pleft.y) / screen_width

        # Raster line and draw a vertical line for each segment
        for i in range(0, screen_width):
            height_on_screen = (height - heightmap[pleft.x, pleft.y]) / z * scale_height. + horizon
            DrawVerticalLine(i, height_on_screen, ybuffer[i], colormap[pleft.x, pleft.y])
            if height_on_screen < ybuffer[i]:
                ybuffer[i] = height_on_screen
            pleft.x += dx
            pleft.y += dy

        # Go to next line and increase step size when you are far away
        z += dz
        dz += 0.2

# Call the render function with the camera parameters:
# position, viewing angle, height, horizon line position, 
# scaling factor for the height, the largest distance, 
# screen width and the screen height parameter
Render( Point(0, 0), 0, 50, 120, 120, 300, 800, 600 )

Links

Web Project demo page

Voxel terrain engine - an introduction

Personal website

Maps

color, height

C1W.png D1.png

color, height

C2W.png D2.png

color, height

C3.png D3.png

color, height

C4.png D4.png

color, height

C5W.png D5.png

color, height

C6W.png D6.png

color, height

C7W.png D7.png

color, height

C8.png D6.png

color, height

C9W.png D9.png

color, height

C10W.png D10.png

color, height

C11W.png D11.png

color, height

C12W.png D11.png

color, height

C13.png D13.png

color, height

C14.png D14.png

color, height

C14W.png D14.png

color, height

C15.png D15.png

color, height

C16W.png D16.png

color, height

C17W.png D17.png

color, height

C18W.png D18.png

color, height

C19W.png D19.png

color, height

C20W.png D20.png

color, height

C21.png D21.png

color, height

C22W.png D22.png

color, height

C23W.png D21.png

color, height

C24W.png D24.png

color, height

C25W.png D25.png

color, height

C26W.png D18.png

color, height

C27W.png D15.png

color, height

C28W.png D25.png

color, height

C29W.png D16.png

License

The software part of the repository is under the MIT license. Please read the license file for more information. Please keep in mind, that the Voxel Space technology might be still patented in some countries. The color and height maps are reverse engineered from the game Comanche and are therefore excluded from the license.

voxelspace's People

Contributors

garrettw avatar gingerbeardman avatar jbn avatar ryan-haskell avatar s-macke avatar sirrah23 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

voxelspace's Issues

Draw sprites

How can you calculate when to draw sprites (eg. Trees) that have an x and y coordinate on the map and a z offset from the terrain?

License?

Hi! There doesn't seem to be a license for this project. choosealicense.com explains some of your choices.

MIT is a very simple, do-whatever-you-want-but-give-credit license. The GPLv3 ensures that you get back modifications. If you chose the GPL, you might license the maps as CC BY or CC BY-SA 4.0, which are one-way compatible with the GPL (CC --> GPL).

Camera roll

Do you know how the game/engine rolls the camera? Does the vertical line drawing function draw the line at an angle instead? Or does it render to a buffer and then rotate that? Or does it skew the image for a rough approximation of roll at low angles, like how this demo fakes moving the camera up and down by panning the image?

TypeError: unsupported operand type(s) for &: 'float' and 'int'

When I download the project and execute tools/animations/run.sh, it says:

delete
anim
0
Traceback (most recent call last):
  File "anim.py", line 181, in <module>
    DrawFrontToBack(Point(670, 500 - i*16), 0, 120, 800, Point(670, 500 - i*16))
  File "anim.py", line 161, in DrawFrontToBack
    -height, -1./z*240., +100, pmap)
  File "anim.py", line 86, in HorlineHidden
    xi = math.floor(p1.x) & 1023
TypeError: unsupported operand type(s) for &: 'float' and 'int'

and when I try to fix this it using float(integer) it says:

delete
anim
0
Traceback (most recent call last):
  File "anim.py", line 181, in <module>
    DrawFrontToBack(Point(670, 500 - i*16), float(0), float(120), float(800), Point(670, 500 - i*16))
  File "anim.py", line 161, in DrawFrontToBack
    float(-height), -1./z*240., +100, pmap)
  File "anim.py", line 86, in HorlineHidden
    xi = math.floor(p1.x) & float(1023)
TypeError: unsupported operand type(s) for &: 'float' and 'float'

don't know how to fix this, please help.

View broken on mobile phones

Application loads the initial terrain fine but the second you click anywhere the camera flies into the ground and you can't do anything else.

Use requestAnimationFrame rather than setTimeout

Since your repo got on the hackernews front page: your draw loop uses window.setTimeout(..., 0), but this leads to rather jarring stutter. Replacing window.setTimeout(...) with window.requestAnimationFrame(Draw) lets the code run much smoother.

On a secondary note, a setTimeout with 0 as argument doesn't actually run "immediately"; it has 20 years of legacy baggage and depending on the browser you're basically guaranteed a minimum interval of 3 to 5 milliseconds instead (because in the 90s, not every browser could do immediate timeouts, and so the lower bound was capped. Then the spec folks went "we should formalize that behaviour" rather than not doing that...)

Keyboard doesn't work

Nothing happens when using keyboard to control. Only mouse works.

No errors in the console, either.

VoxelSpace.html

When I cloned your repo and opened VoxelSpace.html, for some reason I get this:

screen shot 2017-12-03 at 12 29 53 pm

Do you know why I'm getting this? Please note that I'm on a MacBook (just that) using High Sierra

Any plans to port Delta Force to it ?

Delta Force games will benefit greatly from running on a modern voxel engine in terms of higher resolution, widescreen, multi platform incl web, game controller support and even modding. Any plans in that direction?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.