Texturing

Well, we've got some nifty blue swirls going on here (or whatever test system you implemented), but it's not helping our texturemapped tunnel much. In fact, applying the texture is now very simple! For each pixel, take the value for the angle from the table and use that as the x-coordinate for the texture lookup, and use the value from the depth table as the y-coordinate for the texture lookup. Essentially, this:

for y is 0 to screen_height - 1
    for x is 0 to screen_width - 1
	
        // Look up the coordinates to read from the
        // texture:
		
        texture_x = angle_lut(x,y)
        texture_y = depth_lut(x,y)
		
        // Fix the values so they are in range
        // of the texture:
		
        while x < 0 : x = x + texture_width : loop
        while y < 0 : y = y + texture_height : loop		

        while x > texture_width : x = x - texture_width : loop
        while y > texture_height : y = y - texture_height : loop
		
        // Set the screen pixel:		

        set_screen_pixel(x,y) = texture(texture_x,texture_y)

    next x
next y

...where we are treating the texture as an array of colour values. If you run that, you should see your texture applied neatly to the wall! One thing that does look a bit odd is that the texture only wraps around the wall's circumference once, but is quite short along the length of the tunnel. To fix this, multiply the calculated angle by the number of times you want the texture to repeat around the perimeter when building the angles lookup table.

The above image shows the difference between using the normal angle in the range 0 to 255, the second shows the result of multiplying it by eight before saving it to the angle lookup table.

Animating the tunnel

It's one thing to have a static tunnel, and quite another to have a tunnel that you move along! How do you animate the tunnel, then?

The answer is incredibly simple. Once you have taken your texture_x and texture_y values from the two lookup tables, add the rotation value and the amount you want to move down the tunnel to them respectively.

while prog_is_running
    for y is 0 to screen_height - 1
        for x is 0 to screen_width - 1
        
            // Look up the coordinates to read from the
            // texture:
            
            texture_x = angle_lut(x,y) + tunnel_rotated
            texture_y = depth_lut(x,y) + tunnel_zoomed
            
            // Fix the values so they are in range
            // of the texture:
            
            while x < 0 : x = x + texture_width : loop
            while y < 0 : y = y + texture_height : loop        
    
            while x > texture_width : x = x - texture_width : loop
            while y > texture_height : y = y - texture_height : loop
            
            // Set the screen pixel:        
    
            set_screen_pixel(x,y) = texture(texture_x,texture_y)
    
        next x
    next y
	
    tunnel_zoomed = tunnel_zoomed + 1
    tunnel_rotated = tunnel_rotated + 1
loop

(Unchanged code is shown in pale grey). Now, there is one thing that you might have noticed - and that's that the rotation and zooming in are not very smooth. That's because we're using integers to hold the values in the lookup tables. They might be very fast, yes, but rather limiting. The trick here is to use fixed point maths, which basically means that when we calculate our lookup tables we multiply the values by a large number, then divide again by that number before looking up that value on the texture. How does this work?

Let's say our angle ranges from 0 to 255 again. If we were to multiply this by 256, it now ranges from 0 to 65535 (assuming you multiply by the large constant factor before casting to an integer - if you multiply afterwards, you don't gain any precision). Now, when you add the rotation (tunnel_rotated in the above example) it is only worth 1/256th of what it had been - we can now rotate our tunnel with a lot more precision. Of course, these values are too big to use directly, so we divide by 256 again to make them usable.

It's a good idea to choose a power-of-two number (2i, such as 2, 4, 8, 16, 32, 64, 128, and so on) as we can perform the division by bit-shifting, which is a very fast way to divide integers.