My cheesy Lego assembly animation system for POV-Ray... V0.63 Beta! ------------------------------------------------------------------- The story so far: I had seen some animations of Lego models being put together, such as in the commercial for the Honda Element or this other video I once saw where a castle is put together by bricks falling from the sky. I thought a while about how they accomplished that and wondered if I could come up with my own version. Here's what I have come up with so far, in case anyone is interested. I wanted the system to be configurable so that I could easily plug in different models and watch them assemble. I decided to put the pieces into arrays, defining what each piece was (organized by steps just like in the building instructions) and where it was positioned. I loop through each step, and through each piece in a step. As time progresses I move each piece closer to its destination position. I offset the timing of each piece (and step) so that they fall into place in order rather than all at once. (By sequencing them in this way I don't have to worry as much about the pieces going through each other. Furthermore, with each piece definition I give a starting point; I can help keep pieces from colliding by giving them a clear path from their starting points to their destinations. Taking into consideration which pieces precede others in the construction process makes this task easier.) I use Chris Colefax' ClockMod macro for Pov-Ray to accomplish the smooth movement of pieces. I couldn't have done much without it. (I tried.) The ClockMod system also does rotation so I threw that in for each piece as well. Spinning parts look cool. http://www.geocities.com/SiliconValley/Lakes/1434/clockmod.html My first test model had to be something small and simple so I went with the standard Classic Space buggy. Here's what the array definition looks like for this model (follow along at http://library.brickshelf.com/scans/0000/0886/0886-01.html if you'd like! ;) ), along with some notes: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #declare number_of_steps = 5; // Pov-Ray's array system is kind of picky, so I had to set up these things ahead of time #declare PieceArray = array[number_of_steps+1][13]; #declare EndingPositionArray = array[number_of_steps+1][13]; #declare StartingPositionArray = array[number_of_steps+1][13]; #declare RotationPointArray = array[number_of_steps+1][13]; #declare RotationAngleArray = array[number_of_steps+1][13]; // I added a step #0 so that the step indices would match up with the instruction step numbers #declare PieceArray[0][1] = sphere { 0, 0.000001 translate 1000000 } #declare EndingPositionArray[0][1] = <0, 0, 0>; #declare StartingPositionArray[0][1] = <0, 0, 0>; #declare RotationPointArray[0][1] = <0, 0, 0>; #declare RotationAngleArray[0][1] = <0, 0, 0>; // Here's where the steps actually begin // Note: wide = width of 1 Lego stud (more accurately, the distance between two standardly adjacent studs) // flat = height of a Lego plate // tall = height of a Lego brick // l3g0 = teh winnar! // 1 #declare PieceArray[1][1] = object { plate2x6 } // what piece to use #declare EndingPositionArray[1][1] = <0, 0, 0>; // where it is placed in the completed model #declare StartingPositionArray[1][1] = <0, 5, -5>; // where it is when the animation starts #declare RotationPointArray[1][1] = ; // where its center is - for rotation #declare RotationAngleArray[1][1] = <360, 360, 360>; // how to rotate it while waiting to be placed // 2 #declare PieceArray[2][1] = object { Plate2x2WithWheelsOld(wheel_rotation_degrees+13.13*33) } #declare EndingPositionArray[2][1] = <0, -flat, 0>; #declare StartingPositionArray[2][1] = <-1, -10, -3>; #declare RotationPointArray[2][1] = ; #declare RotationAngleArray[2][1] = <360, 360, 0>; #declare PieceArray[2][2] = object { plate4x2 } #declare EndingPositionArray[2][2] = <-wide, -flat, 2*wide>; #declare StartingPositionArray[2][2] = <0, -11, 0>; #declare RotationPointArray[2][2] = <2*wide, flat/2, wide>; #declare RotationAngleArray[2][2] = <360, 0, 0>; #declare PieceArray[2][3] = object { Plate2x2WithWheelsOld(wheel_rotation_degrees+13.13*33) } #declare EndingPositionArray[2][3] = <0, -flat, 4*wide>; #declare StartingPositionArray[2][3] = <1, -10, 3>; #declare RotationPointArray[2][3] = ; #declare RotationAngleArray[2][3] = <360, 360, 0>; // 3 #declare PieceArray[3][1] = object { plate2x1_with_1_stud } #declare EndingPositionArray[3][1] = <0, flat, 0>; #declare StartingPositionArray[3][1] = <-1, 12, -2>; #declare RotationPointArray[3][1] = ; #declare RotationAngleArray[3][1] = <360, 0, 360>; #declare PieceArray[3][2] = object { Slope2x2_45(lg_grey) rotate y*180 translate <2*wide, 0, 2*wide> } #declare EndingPositionArray[3][2] = <0, flat, 4*wide>; #declare StartingPositionArray[3][2] = <2, 12, 4>; #declare RotationPointArray[3][2] = ; #declare RotationAngleArray[3][2] = <360, 360, -360>; #declare PieceArray[3][3] = object { air_tanks rotate y*180 } #declare EndingPositionArray[3][3] = ; #declare StartingPositionArray[3][3] = <-3, 13, -3>; #declare RotationPointArray[3][3] = <0, 0, 0>; #declare RotationAngleArray[3][3] = <0, 360, 0>; // 4 #declare PieceArray[4][1] = object { steering_wheel } #declare EndingPositionArray[4][1] = <0, flat, wide>; #declare StartingPositionArray[4][1] = <3, 14, 0>; #declare RotationPointArray[4][1] = ; #declare RotationAngleArray[4][1] = <0, -360, 360>; #declare PieceArray[4][2] = object { antenna } #declare EndingPositionArray[4][2] = <0, flat+tall, 5*wide>; #declare StartingPositionArray[4][2] = <-2, 7, 7>; #declare RotationPointArray[4][2] = ; #declare RotationAngleArray[4][2] = <0, 0, 360>; // 5 #declare PieceArray[5][1] = object { #declare right_hand_object = union { object { ray_gun texture { lg_grey } } object { brick1x1r_clear material { l3g0_clear_green_material } translate <0, -tall, 0> } translate <-wide/2, 0, -wide/2> rotate y*180 rotate z*90 translate rotate z*90 rotate x*-90 translate <0, -0.1, 0> rotate y*180 } #declare suit_color = 0; // 0 = white, 1 = red, 2 = yellow, 3 = black, 4 = blue #declare position = 0; // 0 = sitting, 1 = standing #declare torso_angle = 0.13; #declare head_angle = -17; #declare left_arm_angle = 30; #declare right_arm_angle = 50; object { #include "minifigs/instant_spacefig.inc" } } #declare EndingPositionArray[5][1] = <0, flat, 2*wide>; #declare StartingPositionArray[5][1] = <0, 15, 0>; #declare RotationPointArray[5][1] = ; #declare RotationAngleArray[5][1] = <0, -360, 0>; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// After defining the model the code later loops through the steps and pieces and then does something like this for each piece: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// object { PieceArray[step_count][piece_count] translate -RotationPointArray[step_count][piece_count] rotate From (0, RotationAngleArray[step_count][piece_count]) Using ( "", 1, step_count, "Decelerate") To ( assembly_start_time + (step_count - 1/10) * (assembly_total_time/number_of_steps) - ( (number_of_pieces_in_current_step-piece_count) * (assembly_total_time/number_of_steps)/number_of_pieces_in_current_step), <0, 0, 0> ) translate RotationPointArray[step_count][piece_count] translate From ( assembly_start_time + step_count * (assembly_total_time/ (number_of_steps+1) ) - ( (number_of_pieces_in_current_step-piece_count) * (assembly_total_time/number_of_steps)/number_of_pieces_in_current_step), StartingPositionArray[step_count][piece_count]) Using ("S-Curve", 1, 1, "") To ( assembly_start_time + (step_count+1) * (assembly_total_time/ (number_of_steps+1) ) - ( (number_of_pieces_in_current_step-piece_count) * (assembly_total_time/number_of_steps)/number_of_pieces_in_current_step), EndingPositionArray[step_count][piece_count]) } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// See? Painless! No, I don't really know what it all means. It makes stuff move around, so that's good. The first part spins the piece around (until it's almost time to move it into place). The second part moves the piece from its starting point to its ending point. Things I want to improve: - make movement paths less linear, maybe automatically-generated splines - define more interesting rotations - bigger models! - option to assemble by all pieces sequentially rather than divided by step - make the wheels on the buggy turn forward! Let me know if you have questions, comments, or suggestions: coby@tubafrog.com Thanks for reading! Coby