// ------------------------------ // Cloth simulation // Martin Magnusson, March 1999 // ------------------------------ // This is a cloth model consisting of a quadratic mesh of nodes connected with springs. Credits to Hugo Elias, I found the description of // this model at his page at http://freespace.virgin.net/hugo.elias/ // First some declarations:---------------------------- #declare Normal_Length = 16/20; // The normal distance between the nodes. #declare n = 20; // The number of nodes. The whole cloth is made of n*n nodes. #declare Gravity = <0, -.02/2, 0>; // Gravity force. -0.01*y seems to be a good value. #declare h1 = .01*35; // For ForceScaler. Higher values makes stiffer cloth. #declare h2 = .01*8; // Timestep. Too high values can produce unpredictable results. #declare Damping = 0.99; // How much should the cloth slow motion down? A value of 1 would make it oscillate forever. #declare Floor = -11; // Height of the floor. #declare R = seed(7); // Initialize arrays and such:------------------------- #declare Cloth1 = array[n][n] // This array holds the postiotions of the nodes. #declare Cloth2 = array[n][n] // Temporary array for positions. #declare Velocity = array[n][n] // This ones hold the velocity vectors. //#declare Save = array[n][n][n][n] // This one could hold force values to eliminate redundant calculations, but it doesn't increase the speed anyway. #declare Clo = <1,1,1>; // Dummy variable for file handling. #declare Vel = <2,2,2>; // Dummy variable for file handling. /*#declare UnstretchedArray = array[5] // Contains precomputed values, for speed-up, or so I thought... #declare UnstretchedArray[1] = Normal_Length; #declare UnstretchedArray[0] = sqrt(pow(1*Normal_Length,2) + pow(1*Normal_Length,2)); #declare UnstretchedArray[2] = 2*Normal_Length; #declare UnstretchedArray[4] = sqrt(pow(2*Normal_Length,2) + pow(2*Normal_Length,2)); #declare UnstretchedArray[3] = sqrt(pow(1*Normal_Length,2) + pow(2*Normal_Length,2));*/ // Initialize all the nodes:-------------------------- #declare i = 0; #declare j = 0; #while (i < n) #while (j < n) #declare Cloth1[i][j] = <(i-n/2)*Normal_Length*1, rand(R)*Normal_Length/25, (j-n/2)*Normal_Length*1>; #declare Velocity[i][j] = <0, 0, 0>; #declare j = j + 1; #end #declare j = 0; #declare i = i + 1; #end #if (clock != 0) // If this is not the first frame, read initial positions from file instead. #fopen file "cloth.dat" read #declare i = 0; #declare j = 0; #while (i < n) #while (j < n) #read (file, Clo, Vel) #declare Cloth1[i][j] = Clo; #declare Velocity[i][j] = Vel; #declare j = j + 1; #end #declare j = 0; #declare i = i + 1; #end #fclose file #end // Macros:-------------------------------------------- // This array dictates the order in which to look when averaging normals: look clock-wise. #declare La = array[8][2] {{-1,-1}, {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0}} // NormSum(i, j) takes the indices of a node as arguments and returns the normalized sum of the normals // of the surrounding triangles. #macro NormSum(i, j) #declare _m = 0; #declare _normsum = <0, 0, 0>; #while ( (_m < 8) ) #if ( (i+La[_m][0]<=(n-1)) & (i+La[_m][0]>=0) & (j+La[_m][1]<=(n-1)) & (j+La[_m][1]>=0) & (i+La[mod(_m+1,8)][0]<=(n-1)) & (i+La[mod(_m+1,8)][0]>=0) & (j+La[mod(_m+1,8)][1]<=(n-1)) & (j+La[mod(_m+1,8)][1]>=0) ) #declare _normsum = _normsum + vcross(Cloth1[i][j] - Cloth1[i+La[_m][0]][j+La[_m][1]], Cloth1[i][j] - Cloth1[i+La[mod(_m+1, 8)][0]][j+La[mod(_m+1, 8)][1]]); #end #declare _m = _m + 1; #end (vnormalize(_normsum)) #end // This macro draws the cloth as a mesh of smooth_triangles. The arguments are pretty obsolete now. #macro Draw(Size, Opacity) #local i = 0; #local j = 0; #debug "Draw\n" mesh { #while (i < n) #while (j < n) /*sphere {Cloth1[i][j], Size pigment{rgbf <1, .9, .8, 1-Opacity>} }*/ #if (((j+1) <= (n-1)) & ((i+1) <= (n-1))) smooth_triangle {Cloth1[i][j], NormSum(i, j), Cloth1[i][j+1], NormSum(i, j+1), Cloth1[i+1][j], NormSum(i+1, j) } #end #if (((j-1) >= 0) & ((i-1) >= 0)) smooth_triangle {Cloth1[i][j], NormSum(i, j), Cloth1[i][j-1], NormSum(i, j-1), Cloth1[i-1][j], NormSum(i-1, j) } #end /*#if (vlength(Velocity[i][j]) != 0) cylinder {Cloth1[i][j], Cloth1[i][j]-Velocity[i][j]*45, Size/7.5 pigment{rgbf <.4, .45, .5, 1>} no_shadow } #end */ #local j = j + 1; #end #local j = 0; #local i = i + 1; #end pigment {rgb <1, .9, .4>} //{rgb <.4, .6, 1>} } #end // Camera and stuff----------------------------------- camera {location <4, 3, -16> look_at <.5, -4.2, -.3> } light_source {<5, 5, -25> rgb 1} plane {y, Floor-.01 pigment {rgb <1,0,0>} } // Start---------------------------------------------- #declare loop = 0; #while (loop < 5) // Make 5 movement steps for each animation frame. The speed of the animation depends on this number. #debug concat(str(loop,1,0), "\n") #declare Scent = <0, -4.01, 0.3>; // A sphere to make things a bit more interesting. #declare Srad = 4; sphere {Scent, Srad - 0.1 pigment {rgb <.2, .1, .5>} //{rgbf<.6, .9, .3, .7>} } // Big bad loop to calculate new positions for the nodes: #declare i = 0; // i and j are indices for the nodes #declare j = 0; #while (j < n) #while (i < n) #declare Move = Gravity; #declare k = -2; // k and l are offsets from i and j, respectively #declare l = -2; #while (k <= 2) // Look 2 nodes away => compare each node with its 24 closest neighbours. #while (l <= 2) #if ( (!((k = 0) & (l = 0))) & (i + k <= n-1) & (i + k >= 0) & (j + l <= n-1) & (j + l >= 0) ) //#if ( (l > 0) | ((l = 0) & (k > 0)) ) // If Force hasn't already been calculated // Unstretched is the distance that normally is between the node and its neighbour /*#if (abs(k) + abs(l) != 2) #declare Unstretched = UnstretchedArray[abs(k) + abs(l)]; #else #if ((k = 0) | (l = 0)) #declare Unstretched = UnstretchedArray[2]; #else #declare Unstretched = UnstretchedArray[0]; #end #end*/ #declare Unstretched = sqrt(pow(k*Normal_Length,2) + pow(l*Normal_Length,2)); #declare Spring = Cloth1[i+k][j+l] - Cloth1[i][j]; #declare Length = vlength(Spring); #if (Length = 0) #declare ForceScaler = 0; //Prevent divide-by-zero errors #else #declare ForceScaler = (Length - Unstretched) / (Length*Length) * h1; #end #declare Force = Spring * ForceScaler; #declare Move = Move + Force; //#declare Save[i][j][i+k][j+l] = -Force; //#else // Else just look it up //#debug concat("i:", str(i,1,0), " j:", str(j,1,0), " k:", str(k,1,0), " l:", str(l,1,0), "\n") // #declare Move = Move + Save[i+k][j+l][i][j]; //#end #end #declare l = l + 1; #end #declare l = -2; #declare k = k + 1; #end #declare Velocity[i][j] = Velocity[i][j] * Damping + Move * h2; #declare Cloth2[i][j] = Cloth1[i][j] + Velocity[i][j]; // Store new position in temporary array // Collision detection (the speed adjustments aren't really accurate): #if (Cloth2[i][j].y < Floor) // If the node is beneath the floor, move it to the floor and adjust the speed #declare Cloth2[i][j] = ; #declare Velocity[i][j] = ; #else #if (vlength(Cloth2[i][j] - Scent) < Srad) // If the node is inside the sphere, move it to the surface and adjust the speed #declare Force2 = vnormalize(Cloth2[i][j] - Scent); #declare Cloth2[i][j] = (Scent) + Force2 * Srad; #declare Velocity[i][j] = Velocity[i][j] + vlength(Velocity[i][j])*Force2; #end #end #declare i = i + 1; #end #declare i = 0; #declare j = j + 1; #end #declare i = 0; #declare j = 0; // Copy the temporary array to the real one: #while (i < n) #while (j < n) #declare Cloth1[i][j] = Cloth2[i][j]; #declare j = j + 1; #end #declare j = 0; #declare i = i + 1; #end #declare loop = loop + 1; #end Draw(Normal_Length/3, 1) // Write data to a file so it can be used in the next frame: #fopen file "cloth.dat" write #declare i = 0; #declare j = 0; #while (i < n) #while (j < n) #write (file, Cloth1[i][j], ",", Velocity[i][j], ",") #declare j = j + 1; #end #declare j = 0; #declare i = i + 1; #end #fclose file