Guys,
Although my wife is totally underwhelmed by this, here's a nice SDL demo. It needs some more work, but I won't get time to visit it again in the next week.
The backface culling isn't perfect, but you get the idea,
Cheers,
Nick
p.s. If you don't have enough memory to run this, try removing the third 'surfaces = bisect_array (surfaces)'.
########################################################################### # Macros ###########################################################################
.macro swap(a,b)
$I1000 = .a
.a = .b
.b = $I1000
.endm
.macro point_uninit(var)
.var = new IntList
.var = 7
.var[6] = 0
.endm
.macro point(var, x,y,z)
.point_uninit (.var)
.var[0] = .x
.var[1] = .y
.var[2] = .z
.endm
###########################################################################
# Demo Harness
###########################################################################
.sub main @MAIN
# profile 1
.local pmc surfaces
surfaces = read_data ("teapot.dat")
surfaces = bisect_array (surfaces)
surfaces = bisect_array (surfaces)
sdl_init()
lock ()
.local pmc main_screen
main_screen = global 'main_screen'
# Create a color to paint the new pixels
.local pmc args
args = new PerlHash
args[ 'r' ] = 255
args[ 'g' ] = 0
args[ 'b' ] = 0
.local int color_type
find_type color_type, 'SDL::Color'
.local pmc colour
colour = new color_type, args
.local int converted_colour
converted_colour = colour.'color_for_surface' (main_screen)
.local pmc matrix
matrix = transformation_matrix (0.02, 0.,1.,0., -190.,-160.,0.)
matrix_multiply_patches (matrix, surfaces, 1)
plot_dots (surfaces, converted_colour)
unlock ()
update ()
lock ()
args[ 'r' ] = 0
args[ 'g' ] = 0
args[ 'b' ] = 255
colour = new color_type, args
converted_colour = colour.'color_for_surface' (main_screen)
matrix = transformation_matrix (0.025, 0.,1.,0., 190.,-150.,0.)
matrix_multiply_patches (matrix, surfaces, 2)
plot_lines (surfaces, converted_colour, 0)
unlock ()
update ()
lock ()
surfaces = bisect_array (surfaces)
args[ 'r' ] = 0
args[ 'g' ] = 128
args[ 'b' ] = 0
colour = new color_type, args
converted_colour = colour.'color_for_surface' (main_screen)
matrix = transformation_matrix (0.05, 0.,1.,0., 50.,75.,0.)
matrix_multiply_patches (matrix, surfaces, 3)
plot_lines (surfaces, converted_colour, 1)
unlock ()
update ()
sleep 10
.local pmc app
app = global 'app'
app.'quit' ()
end
.end
###########################################################################
# Bezier Stuff
###########################################################################
.macro bisect_line(in1,in2, out)
tmp1 = .in1[0]
tmp2 = .in2[0]
tmp1 += tmp2
tmp1 >>= 1
.out[0] = tmp1
tmp1 = .in1[1]
tmp2 = .in2[1]
tmp1 += tmp2
tmp1 >>= 1
.out[1] = tmp1
tmp1 = .in1[2]
tmp2 = .in2[2]
tmp1 += tmp2
tmp1 >>= 1
.out[2] = tmp1
.endm
.macro bisect_bezier(in, out1, out2, index1, index2, index3, index4)
b0 = .in[.index1]
b1 = .in[.index2]
b2 = .in[.index3]
b3 = .in[.index4]
.point_uninit (A)
.point_uninit (B)
.point_uninit (C)
.point_uninit (D)
.point_uninit (E)
.point_uninit (F)
.bisect_line (b0, b1, A)
.bisect_line (b1, b2, B)
.bisect_line (b2, b3, C)
.bisect_line (A, B, D)
.bisect_line (B, C, E)
.bisect_line (D, E, F)
.out1[.index1] = b0
.out1[.index2] = A
.out1[.index3] = D
.out1[.index4] = F
.out2[.index1] = F
.out2[.index2] = E
.out2[.index3] = C
.out2[.index4] = b3
.endm
.sub bisect_patch
.param pmc in
.param pmc out1
.param pmc out2
.param pmc out3
.param pmc out4
out1 = 16
out2 = 16
out3 = 16
out4 = 16
.local pmc b0
.local pmc b1
.local pmc b2
.local pmc b3
.local int tmp1
.local int tmp2
.local pmc A
.local pmc B
.local pmc C
.local pmc D
.local pmc E
.local pmc F
.bisect_bezier (in, out1, out2, 0, 1, 2, 3)
.bisect_bezier (in, out1, out2, 4, 5, 6, 7)
.bisect_bezier (in, out1, out2, 8, 9, 10, 11)
.bisect_bezier (in, out1, out2, 12, 13, 14, 15)
.bisect_bezier (out1, out1, out3, 0, 4, 8, 12)
.bisect_bezier (out1, out1, out3, 1, 5, 9, 13)
.bisect_bezier (out1, out1, out3, 2, 6,10, 14)
.bisect_bezier (out1, out1, out3, 3, 7,11, 15)
.bisect_bezier (out2, out2, out4, 0, 4, 8, 12)
.bisect_bezier (out2, out2, out4, 1, 5, 9, 13)
.bisect_bezier (out2, out2, out4, 2, 6,10, 14)
.bisect_bezier (out2, out2, out4, 3, 7,11, 15)
.end
###########################################################################
.sub bisect_array
.param pmc array
.local int upto
upto = 0
.local int array_size
array_size = array
.local pmc result
result = new Array
$I0 = 4*array_size
result = $I0
.local int index
index = 0
bisect_array_loop:
.local pmc bezier
bezier = array[index]
.local pmc patch1
.local pmc patch2
.local pmc patch3
.local pmc patch4
patch1 = new Array
patch2 = new Array
patch3 = new Array
patch4 = new Array
bisect_patch (bezier, patch1, patch2, patch3, patch4)
# (Pushing to an array doesn't look like it's supported)
result[upto] = patch1
upto += 1
result[upto] = patch2
upto += 1
result[upto] = patch3
upto += 1
result[upto] = patch4
upto += 1
index = index + 1
if index < array_size goto bisect_array_loop
.pcc_begin_return
.return result
.pcc_end_return
.end
###########################################################################
# Read from text file
###########################################################################
.sub read_data
.param string filename
# Open File
open $P0, filename, "<"
defined $I2, $P0
if $I2 goto found_file
printerr "Cannot find '"
printerr filename
printerr "' configuration file\n"
end
found_file:
# Read number section
.local int number_count
number_count = 0
readline $S1, $P0
$I10 = $S1
.local pmc numbers
numbers = new Array
numbers = $I10
next_number:
readline $S1, $P0
$N0 = $S1
numbers[number_count] = $N0
number_count = number_count + 1
if number_count < $I10 goto next_number
# Read points section
readline $S1, $P0
$I10 = $S1
.local int point_count
point_count = 0
.local pmc points
points = new Array
points = $I10
$I5 = 0
next_point1:
readline $S1, $P0
$S0 = substr_r $S1, 0, 4
$I0 = $S0
$I3 = $I0
$I0 = abs $I0
$I0 = numbers[$I0]
if $I3 >= 0 goto no_negate_0
$I0 = -$I0
no_negate_0:
$S0 = substr_r $S1, 5, 9
$I1 = $S0
$I3 = $I1
$I1 = abs $I1
$I1 = numbers[$I1]
if $I3 > 0 goto no_negate_1
$I1 = -$I1
no_negate_1:
$S0 = substr_r $S1, 10, 14
$I2 = $S0
$I3 = $I2
$I2 = abs $I2
$I2 = numbers[$I2]
if $I3 > 0 goto no_negate_2
$I2 = -$I2
no_negate_2:
.point ($P1, $I0, $I1, $I2)
points[point_count] = $P1
point_count = point_count + 1
if point_count < $I10 goto next_point1
# Read surfaces section
readline $S1, $P0
$I10 = $S1
.local int surface_count
.local int point_count
.local pmc surfaces
surfaces = new Array
surfaces = $I10
surface_count = 0
next_surface:
.local pmc surface
surface = new Array
surface = 16
point_count = 0
next_point:
readline $S1, $P0
$I0 = $S1
$P1 = points[$I0]
surface[point_count] = $P1
point_count = point_count + 1
if point_count < 16 goto next_point
surfaces[surface_count] = surface
surface_count = surface_count + 1
if surface_count < $I10 goto next_surface
close $P0
.pcc_begin_return
.return surfaces
.pcc_end_return
.end
###########################################################################
# Screen Access Stuff
###########################################################################
.sub plot_dots
.param pmc array
.param int converted_colour
.local int array_size
array_size = array
.local int index
index = 0
plot_dots_loop:
.local pmc bezier
bezier = array[index]
.local int index2
index2 = 0
plot_dots_loop2:
.local pmc point
point = bezier[index2]
.local int p0
p0 = point[3]
.local int p1
p1 = point[4]
.local int p2
p2 = point[5]
_plot_point (p0,p1,p2, converted_colour)
index2 = index2 + 1
if index2 < 16 goto plot_dots_loop2
index = index + 1
if index < array_size goto plot_dots_loop
.end
###########################################################################
# Line drawing code
###########################################################################
.sub line3d
.param int x0
.param int y0
.param int z0
.param int x1
.param int y1
.param int z1
.param pmc callback
.param int converted_colour
.local int dd_x
.local int d_x
.local int inco_x
.local int incb_x
.local int dd_y
.local int d_y
.local int inco_y
.local int incb_y
.local int dd_z
.local int d_z
.local int inco_z
.local int incb_z
callback (x0, y0, z0, converted_colour)
.local int dx
.local int dy
.local int dz
dx = x1 - x0
dy = y1 - y0
dz = z1 - z0
if dx != 0 goto line_continue
if dy != 0 goto line_continue
goto line_done
line_continue:
$I0 = abs dx
$I1 = abs dy
if $I1 <= $I0 goto is_shallow
# Steep line
if dy >= 0 goto steep_dont_swap
.swap (x0,x1)
.swap (y0,y1)
.swap (z0,z1)
dx = -dx
dy = -dy
dz = -dz
steep_dont_swap:
dd_x = 1
d_x = 2*dx
d_x -= dy
inco_x = 2*dx
incb_x = dx-dy
incb_x = 2*incb_x
if dx >= 0 goto steep_dont_negate_x
dd_x = -dd_x
d_x = -2*dx
d_x -= dy
inco_x = -inco_x
incb_x = dy+dx
incb_x = -2*incb_x
steep_dont_negate_x:
dd_z = 1
d_z = 2*dz
d_z -= dy
inco_z = 2*dz
incb_z = dz-dy
incb_z = 2*incb_z
if dz >= 0 goto steep_dont_negate_z
dd_z = -dd_z
d_z = -2*dz
d_z -= dy
inco_z = -inco_z
incb_z = dz+dy
incb_z = -2*incb_z
steep_dont_negate_z:
steep_loop:
if d_z <= 0 goto steep_d_z_gt_0
d_z += incb_z
z0 += dd_z
goto steep_d_z_le_0
steep_d_z_gt_0:
d_z += inco_z
steep_d_z_le_0:
if d_x <= 0 goto steep_d_x_gt_0
d_x += incb_x
x0 += dd_x
goto steep_d_x_le_0
steep_d_x_gt_0:
d_x += inco_x
steep_d_x_le_0:
y0 += 1
callback (x0, y0, z0, converted_colour)
if y0 < y1 goto steep_loop
goto line_done
is_shallow:
# Shallow line
if dx >= 0 goto shallow_dont_swap
.swap (x0,x1)
.swap (y0,y1)
.swap (z0,z1)
dx = -dx
dy = -dy
dz = -dz
shallow_dont_swap:
dd_y = 1
d_y = 2*dy
d_y -= dx
inco_y = 2*dy
incb_y = dy-dx
incb_y = 2*incb_y
if dy >= 0 goto shallow_dont_negate_y
dd_y = -dd_y
d_y = -2*dy
d_y -= dx
inco_y = -inco_y
incb_y = dy+dx
incb_y = -2*incb_y
shallow_dont_negate_y:
dd_z = 1
d_z = 2*dz
d_z -= dx
inco_z = 2*dz
incb_z = dz-dx
incb_z = 2*incb_z
if dz >= 0 goto shallow_dont_negate_z
dd_z = -dd_z
d_z = -2*dz
d_z -= dx
inco_z = -inco_z
incb_z = dz+dx
incb_z = -2*incb_z
shallow_dont_negate_z:
shallow_loop:
if d_y <= 0 goto shallow_d_y_gt_0
d_y += incb_y
y0 += dd_y
goto shallow_d_y_le_0
shallow_d_y_gt_0:
d_y += inco_y
shallow_d_y_le_0:
if d_z <= 0 goto shallow_d_z_gt_0
d_z += incb_z
z0 += dd_z
goto shallow_d_z_le_0
shallow_d_z_gt_0:
d_z += inco_z
shallow_d_z_le_0:
x0 += 1
callback (x0, y0, z0, converted_colour)
if x0 < x1 goto shallow_loop
line_done:
.end
###########################################################################
.sub plot_lines
.param pmc array
.param int converted_colour
.param int cull
.local pmc cb
cb = new .Sub
cb = addr _plot_point
.local int array_size
array_size = array
.local int bez_index
bez_index = 0
plot_bezier_loop:
.local pmc bezier
bezier = array[bez_index]
.local int x
.local int y
x = 0
plot_outer:
y = 0
plot_inner:
.local int index
index = 4*x
index = index + y
.local int x_next_index
.local int y_next_index
x_next_index = index + 1
y_next_index = index + 4
$P0 = bezier[index]
$P1 = bezier[x_next_index]
$P2 = bezier[y_next_index]
$I0 = $P0[3]
$I1 = $P0[4]
$I2 = $P0[5]
$I3 = $P1[3]
$I4 = $P1[4]
$I5 = $P1[5]
$I6 = $P2[3]
$I7 = $P2[4]
$I8 = $P2[5]
# Try to do back face culling
if cull == 0 goto dont_cull
.local int tmp
$I9 = $I0 - $I3
$I10 = $I1 - $I4
$I12 = $I0 - $I6
$I13 = $I1 - $I7
$I17 = $I9*$I13
tmp = $I10*$I12
$I17 = $I17 - tmp
if $I17 < 0 goto do_cull
dont_cull:
line3d ($I0,$I1,$I2, $I3,$I4, $I5, cb, converted_colour)
line3d ($I0,$I1,$I2, $I6,$I7, $I8, cb, converted_colour)
do_cull:
y = y + 1
if y < 3 goto plot_inner
x = x + 1
if x < 3 goto plot_outer
bez_index = bez_index + 1
if bez_index < array_size goto plot_bezier_loop
.end
###########################################################################
# SDL code
###########################################################################
.sub sdl_init
# First load the necessary libraries
load_bytecode "library/SDL/App.imc"
load_bytecode "library/SDL/Rect.imc"
load_bytecode "library/SDL/Color.imc"
# Arguments for the SDL::App constructor
.local pmc args
args = new PerlHash
args[ 'height' ] = 480
args[ 'width' ] = 640
args[ 'bpp' ] = 16
args[ 'flags' ] = 1
# Create an SDL::App object
.local pmc app
.local int app_type
find_type app_type, 'SDL::App'
app = new app_type, args
store_global 'app', app
# Fetch the SDL::Surface representing the main window
.local pmc main_screen
main_screen = app.'surface'()
store_global 'main_screen', main_screen
args = new PerlHash
args[ 'height' ] = 480
args[ 'width' ] = 640
args[ 'x' ] = 0
args[ 'y' ] = 0
# Create an SDL::Rect representing the entire main screen
.local pmc rect
.local int rect_type
find_type rect_type, 'SDL::Rect'
rect = new rect_type, args
store_global 'rect', rect
.local int color_type
find_type color_type, 'SDL::Color'
# Create a white color to paint the background
args = new PerlHash
args[ 'r' ] = 255
args[ 'g' ] = 255
args[ 'b' ] = 255
.local pmc white
white = new color_type, args
store_global 'white', white
cls()
main_screen.'update_rect'( rect )
.end
###########################################################################
.sub cls
.local pmc main_screen
.local pmc rect
.local pmc white
main_screen = global 'main_screen'
rect = global 'rect'
white = global 'white'
# draw the background
main_screen.'fill_rect'( rect, white )
.end
###########################################################################
.sub update
.local pmc main_screen
main_screen = global 'main_screen'
.local pmc rect
rect = global 'rect'
main_screen.'update_rect'( rect )
.end
.sub lock
.local pmc main_screen
main_screen = global 'main_screen'
# lock the raw framebuffer
main_screen.'lock'()
.end
.sub unlock
.local pmc main_screen
main_screen = global 'main_screen'
# lock the raw framebuffer
main_screen.'unlock'()
.end
###########################################################################
.sub _plot_point
.param int p0
.param int p1
.param int p2
.param int converted_colour
if p0 < 0 goto skip_plot
if p0 >= 640 goto skip_plot
if p1 < 0 goto skip_plot
if p1 >= 480 goto skip_plot
.local pmc main_screen
main_screen = global 'main_screen'
main_screen.'draw_pixel' (p0, p1, converted_colour)
skip_plot:
.end
###########################################################################
# Transformation
###########################################################################
.sub transformation_matrix
.param num scale
.param num roll
.param num pitch
.param num yaw
.param num x
.param num y
.param num z
.local num cosroll
.local num sinroll
cos cosroll, roll
sin sinroll, roll
.local num cospitch
.local num sinpitch
cos cospitch, pitch
sin sinpitch, pitch
.local num cosyaw
.local num sinyaw
cos cosyaw, yaw
sin sinyaw, yaw
.local num sinyawcosroll
.local num sinyawsinroll
sinyawcosroll = sinyaw*cosroll
sinyawsinroll = sinyaw*sinroll
$P0 = new Array
$P0 = 16
.local num tmp1
.local num tmp2
# Matrix row 1
tmp1 = cosyaw*cosroll
tmp1 = scale * tmp1
$P0[0] = tmp1
tmp1 = sinpitch*sinyawcosroll
tmp2 = cospitch*sinroll
tmp1 = tmp1 - tmp2
tmp1 = scale * tmp1
$P0[1] = tmp1
tmp1 = cospitch*sinyawsinroll
tmp2 = sinpitch*sinroll
tmp1 = tmp1 + tmp2
tmp1 = scale * tmp1
$P0[2] = tmp1
$P0[3] = x
# Matrix row 2
tmp1 = cosyaw*sinroll
tmp1 = scale * tmp1
$P0[4] = tmp1
tmp1 = sinpitch*sinyawsinroll
tmp2 = cospitch*cosroll
tmp1 = tmp1 + tmp2
tmp1 = scale * tmp1
$P0[5] = tmp1
tmp1 = cospitch*sinyawsinroll
tmp2 = sinpitch*cosroll
tmp1 = tmp1 - tmp2
tmp1 = scale * tmp1
$P0[6] = tmp1
$P0[7] = y
# Matrix row 3
tmp1 = -sinyaw
tmp1 = scale * tmp1
$P0[8] = tmp1
tmp1 = cosyaw*sinpitch
tmp1 = scale * tmp1
$P0[9] = tmp1
tmp1 = cosyaw*cospitch
tmp1 = scale * tmp1
$P0[10] = tmp1
$P0[11] = z
# Matrix row 4
$P0[12] = 0
$P0[13] = 0
$P0[14] = 0
$P0[15] = 1
.pcc_begin_return
.return $P0
.pcc_end_return
.end
.sub matrix_multiply_point
.param pmc matrix
.param pmc point
# Convert the point to floats
.local num px
.local num py
.local num pz
$I0 = point[0]
px = $I0
$I0 = point[1]
py = $I0
$I0 = point[2]
pz = $I0
# Actually do the matrix multiplication
.local num m00
.local num m01
.local num m02
.local num x
.local num m10
.local num m11
.local num m12
.local num y
.local num m20
.local num m21
.local num m22
.local num z
m00 = matrix[0]
m01 = matrix[1]
m02 = matrix[2]
x = matrix[3]
m10 = matrix[4]
m11 = matrix[5]
m12 = matrix[6]
y = matrix[7]
m20 = matrix[8]
m21 = matrix[9]
m22 = matrix[10]
z = matrix[11]
.local num tmp1
.local num tmp2
tmp1 = m00 * px
tmp2 = m01 * py
tmp1 = tmp1 + tmp2
tmp2 = m02 * pz
tmp1 = tmp1 + tmp2
tmp1 = tmp1 + x
$I0 = tmp1
point[3] = $I0
tmp1 = m10 * px
tmp2 = m11 * py
tmp1 = tmp1 + tmp2
tmp2 = m12 * pz
tmp1 = tmp1 + tmp2
tmp1 = tmp1 + y
$I0 = tmp1
point[4] = $I0
tmp1 = m20 * px
tmp2 = m21 * py
tmp1 = tmp1 + tmp2
tmp2 = m22 * pz
tmp1 = tmp1 + tmp2
tmp1 = tmp1 + z
$I0 = tmp1
point[5] = $I0
.end
.sub matrix_multiply_patches
.param pmc matrix
.param pmc array
.param int generation
.local int array_size
array_size = array
.local int index
index = 0
multiply_raw_loop:
.local pmc bezier
bezier = array[index]
.local int index2
index2 = 0
multiply_raw_loop2:
.local pmc point
point = bezier[index2]
$I0 = point[6]
# Since points are referenced by multiple patches, check if
# this one has been transformed already
if $I0 == generation goto skip_transform
matrix_multiply_point (matrix, point)
# Perform some final adhustments for screen rendering
$I0 = point[3]
$I1 = point[4]
$I2 = point[5]
# Could add some perspective here...
# Flip y
$I1 = -$I1
# Place origin at canvas centre
$I0 += 320
$I1 += 240
point[3] = $I0
point[4] = $I1
point[6] = generation
skip_transform:
index2 = index2 + 1
if index2 < 16 goto multiply_raw_loop2
index = index + 1
if index < array_size goto multiply_raw_loop
.end
