// Recommended settings: // -w720 -h480 +a0.01 +am1 -j +fn +ua +kff744 // -w1280 -h720 +a0.01 +am1 -j +fn +ua +kff744 // -w1920 -h1080 +a0.01 +am1 -j +fn +ua +kff744 #include "colors.inc" #include "woods.inc" #include "metals.inc" #include "math.inc" #include "screen.inc" // As modified by SharkD // Create the camera (mostly default settings) #declare Camera_Direct_Mode = 0; #declare Camera_Orthographic = off; #declare Camera_Aspect_Ratio = image_width/image_height; #declare Camera_Location = -30*z; #declare Camera_Look_At = 0*x; #declare Camera_Sky = +y; #declare Camera_Zoom = 1; Update_Camera() // Just for testing; final render uses alpha channel background { rgb 1 } // Generic 3-point lighting light_source { 0*x rgb 1.000 translate < 80, 60, -100> } light_source { 0*x rgb 0.500 translate <-80, 30, -100> } light_source { 0*x rgb 0.250 translate < 20, 20, 60> } // Various motion paths #declare P1A = spline { quadratic_spline 0.000, <-16.000, 1.000, 0.000>, 0.125, <-12.500, 1.624, 0.250>, 0.250, < -9.000, 2.225, 0.625>, 0.375, < -5.000, 2.778, 1.500>, 0.500, < 0.000, 3.263, 2.000>, 0.625, < 4.000, 3.661, 1.000>, 0.750, < 8.000, 3.956, 0.250>, 0.875, < 9.000, 4.139, -0.100>, 1.000, < 9.500, 4.200, -0.250> } #declare P1CRoute = spline { linear_spline 0.000, P1A(1.000), 1.000, -P1A(1.000) } #declare P1C = spline { quadratic_spline 0.000, P1CRoute(0.000), 0.125, P1CRoute(0.100), 0.250, P1CRoute(0.200), 0.375, P1CRoute(0.350), 0.500, P1CRoute(0.500), 0.625, P1CRoute(0.650), 0.750, P1CRoute(0.800), 0.875, P1CRoute(0.900), 1.000, P1CRoute(1.000) } #declare P1ERoute = spline { linear_spline 0.000, P1C(1.000), 1.000, P1C(1.000)*<-1,1,1> } #declare P1E = spline { quadratic_spline 0.000, P1ERoute(0.000), 0.125, P1ERoute(0.100), 0.250, P1ERoute(0.200), 0.375, P1ERoute(0.350), 0.500, P1ERoute(0.500), 0.625, P1ERoute(0.650), 0.750, P1ERoute(0.800), 0.875, P1ERoute(0.900), 1.000, P1ERoute(1.000) } #declare P1GRoute = spline { linear_spline 0.000, P1E(1.000), 1.000, 0*x } #declare P1G = spline { quadratic_spline 0.000, P1GRoute(0.000), 0.125, P1GRoute(0.100), 0.250, P1GRoute(0.200), 0.375, P1GRoute(0.350), 0.500, P1GRoute(0.500), 0.625, P1GRoute(0.650), 0.750, P1GRoute(0.800), 0.875, P1GRoute(0.900), 1.000, P1GRoute(1.000) } // Motions are based on specific numbers of frames per motion, with // 24 frames/second assumed as the output rate #declare SetAFrames = 120; #declare SetBFrames = 192; #declare SetCFrames = 24; #declare SetDFrames = 96; #declare SetEFrames = 24; #declare SetFFrames = 96; #declare SetGFrames = 36; #declare SetHFrames = 120; #declare SetIFrames = 36; // Calculate boundaries for the motion sets #declare SetABegin = 1; #declare SetAEnd = SetABegin-1+SetAFrames; #declare SetBBegin = SetAEnd+1; #declare SetBEnd = SetBBegin-1+SetBFrames; #declare SetCBegin = SetBEnd+1; #declare SetCEnd = SetCBegin-1+SetCFrames; #declare SetDBegin = SetCEnd+1; #declare SetDEnd = SetDBegin-1+SetDFrames; #declare SetEBegin = SetDEnd+1; #declare SetEEnd = SetEBegin-1+SetEFrames; #declare SetFBegin = SetEEnd+1; #declare SetFEnd = SetFBegin-1+SetFFrames; #declare SetGBegin = SetFEnd+1; #declare SetGEnd = SetGBegin-1+SetGFrames; #declare SetHBegin = SetGEnd+1; #declare SetHEnd = SetHBegin-1+SetHFrames; #declare SetIBegin = SetHEnd+1; #declare SetIEnd = SetIBegin-1+SetIFrames; #declare TotalFrames = SetAFrames + SetBFrames + SetCFrames + SetDFrames + SetEFrames + SetFFrames + SetGFrames + SetHFrames + SetIFrames; // Copy frame_number to a variable; this allows easier testing of single // frames at various positions #declare Frame = frame_number; // If we're animating, bail out if the total number of frames isn't right #if (clock_on) #if (final_frame != TotalFrames) #error concat("Total frames must be ",str(TotalFrames,1,0),".") #end #end // This macro gives the position of the main sphere (called Head) at any // given frame number. Some motion sets come from a spline, some come from // a calculation #macro Position(Frame) #if (Frame < 1) #local FramePos = 1; #else #local FramePos = Frame; #end #switch (FramePos) #range(SetABegin, SetAEnd) P1A((FramePos-SetABegin)/(SetAFrames-1)); #break #range(SetBBegin, SetBEnd) #local Delta = (FramePos-SetBBegin)/(SetBFrames-1); #local Pos = P1A(1) + <1.000*sin(Delta*2*pi*2), 0.200*sin(Delta*3*pi), 0.500*sin(Delta*2*pi)>; Pos; #break #range(SetCBegin, SetCEnd) P1C((FramePos-SetCBegin)/(SetCFrames-1)); #break #range(SetDBegin, SetDEnd) #local Delta = (FramePos-SetDBegin)/(SetDFrames-1); #local Pos = P1C(1) + <1.000*sin(Delta*2*pi), 0.100*sin(Delta*3*pi), 0.250*sin(Delta*2*pi)>; Pos; #break #range(SetEBegin, SetEEnd) P1E((FramePos-SetEBegin)/(SetEFrames-1)); #break #range(SetFBegin, SetFEnd) #local Delta = (FramePos-SetFBegin)/(SetFFrames-1); #local Pos = P1E(1) + <1.100*sin(Delta*2*pi), 0.125*sin(Delta*3*pi), 0.375*sin(Delta*2*pi)>; Pos; #break #range(SetGBegin, SetGEnd) P1G((FramePos-SetGBegin)/(SetGFrames-1)); #break #range(SetHBegin, SetHEnd) #local Delta = (FramePos-SetHBegin)/(SetHFrames-1); #local Pos = P1G(1) + <0.750*sin(Delta*2*pi), 1.000*sin(Delta*3*pi), 0.250*sin(Delta*2*pi)>; Pos; #break #range(SetIBegin, SetIEnd) #local Delta = (FramePos-SetIBegin)/(SetIFrames-1); #local Pos = P1G(1) + <0, max(0, -0.500*sin(Delta*2*pi)), -28.5*pow(Delta,2.000)>; Pos; #break #end #end // Create s spring-like object using a sphere sweep. The spring approximates // a cylinder from 0*y to Height*y of about Spring_Radius radius. #macro Spring(Height, Spring_Radius, Turns, Wire_Radius, Vertical_Scale) #local Points = 4*Turns + 3; #local YSep = (Height/Vertical_Scale) / (4*Turns); #local YPos = -YSep; sphere_sweep { b_spline Points #local Counter = 0; #local PosIndex = 0; #while (Counter < Points) #switch (PosIndex) #case (0) , -Spring_Radius*x+YPos*y, Wire_Radius #break #case (1) , Spring_Radius*z+YPos*y, Wire_Radius #break #case (2) , Spring_Radius*x+YPos*y, Wire_Radius #break #case (3) , -Spring_Radius*z+YPos*y, Wire_Radius #break #end #local PosIndex = mod(PosIndex + 1, 4); #local YPos = YPos + YSep; #local Counter = Counter + 1; #end scale Vertical_Scale*y + (x + z) } #end // This macro creates a spring in space at specific points. The number of // turns is always the same, so the spring will appear to stretch if the points // move apart. #macro SpringAt(FromCenter, ToCenter, Texture) #local BPrime = FromCenter - ToCenter; #local Height = vlength(BPrime); #local Spring_Radius = 0.125; #local Turns = 20; #local Wire_Radius = 0.030; #local Vertical_Scale = 0.100; object { Spring(Height, Spring_Radius, Turns, Wire_Radius, Vertical_Scale) texture { Texture } // Rotate the spring to match the points in space. NOTE: THIS DOESN'T // WORK RIGHT, AND I HAVEN'T DEBUGGED IT YET. #local Ref = vrotate(Height*y, VRotationD(Height*y, BPrime, z)*z); rotate VRotationD(Height*y, BPrime, z)*z rotate VRotationD(Ref, BPrime, y)*y translate ToCenter } #end // Calculate positions in space for three spheres, called Head, Body, and Tail. // Body occupies the same position as Head did three frames prior and 1.5 units // lower on the y-axis. Tail is 7 frames behind Head, and 2.5 units below. This // gives the illusion of a physically plausible animation of balls connected by // springs, without the overhead of actually calculating anything. #declare HeadPos = Position(Frame) #declare BodyPos = Position(Frame-3) #declare BodyPos = BodyPos - 1.500*y; #declare TailPos = Position(Frame-7) #declare TailPos = TailPos - 2.500*y; // This function is used to rotate the material on the spheres to match the angle formed // between the centers of two balls in the XY plane. This enhances the illusion of movement, // as the balls appear to angle themselves in the direction of their motion. #macro MaterialRotation(FromCenter, ToCenter) #local Hypotenuse = sqrt(pow(FromCenter.x-ToCenter.x,2)+pow(FromCenter.y-ToCenter.y,2)); #local Theta = 0; #if (Hypotenuse > 0) #local Theta = degrees(asin(abs(FromCenter.x-ToCenter.x)/Hypotenuse)); #end #if (ToCenter.x < FromCenter.x) #local Theta = -Theta; #end (Theta) #end #declare HeadTheta = MaterialRotation(HeadPos, BodyPos); #declare BodyTheta = MaterialRotation(BodyPos, TailPos); #declare TailTheta = MaterialRotation(HeadPos, TailPos); // This creates the actual balls and springs connecting them. All use standard POV materials. union { sphere { 0*x, 0.750 texture { T_Wood11 rotate 180*y finish { phong 0.500 phong_size 60 ambient 1 } rotate HeadTheta*z } translate HeadPos } sphere { 0*x, 0.625 texture { T_Wood28 rotate 0*y finish { phong 0.500 phong_size 60 ambient 1 } rotate BodyTheta*z } translate BodyPos } sphere { 0*x, 0.400 texture { T_Wood30 rotate 45*y finish { phong 0.500 phong_size 60 ambient 1 } rotate TailTheta*z } translate TailPos } SpringAt(HeadPos, BodyPos, T_Brass_2A) SpringAt(BodyPos, TailPos, T_Brass_2A) } // Create a position map for PArticle Illusion. The format of the file is one frame per line, with // frame number, screen X, and screen Y numbers separated by tabs. This program will output the center // of the tail sphere, than a particle emitter will be mapped to show up behind it in the // composition. #declare EmitterPosition = Get_Screen_XY(TailPos); #if (Frame > 1) #fopen Target "Target.txt" append #else #fopen Target "Target.txt" write #end #write (Target,int(Frame),"\t",int(EmitterPosition.x),"\t",int(EmitterPosition.y),"\n") #fclose Target