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