// sponge /* Ok, here's the deal. The problem with the sponge shape is that as the recursion gets deep it quickly gets quite complex in the number of objects needed to create it. Using the stuff described below I've made an iteration 8 sponge and I think 10 is possible. Here are the major optimizations I used: 1. Don't use any CSGCSGdiferences, make it only out of additive CSG (helps in rendering, not in parsing). 2. The shape as a whole is mirrored on all three axi, use this fact and render one 1/8th of the whole as an object and rotate it and translate it to make the whole. 3. The actual shape could be made out of boxes but the mesh is much more effecient for memory and and rendering so use it instead. This does make the process a bit more complex. 4. Because a sponge in a fractal it is a self referential shape, that is, the whole is made up of many smaller shapes that are similar to the whole. This fact can be used to create a small sponge of a medium iteration (e.g., 4) and use that smaller shape as a building block to create the large sponge at a similar iteration level. 2 level 4 sponges are much less complex than 1 level 8 shape. This could even be done a third time for higher levels. */ #include "colors.inc" #declare SKIN = 0.00001; // use these 2 interations setting together for the total (e.g., 3 and 3 would be a total of 6) #declare iterations = 3; #declare iterationsB = 3; #declare res = pow(3, iterations); #declare resB = pow(3, iterationsB); #declare total = res * resB; #declare single = 10 / total; // since the box is mirrored in X, Y, and Z we only need 1/2 on all sides (1/8th memory) #declare res2 = int( res / 2 ) + 1; #declare it = array[res2][res2][res2] #declare size = 10 / res / resB; // init array #macro init( arr, dx, dy, dz ) #declare X = 0; #while( X < dx ) #declare Y = 0; #while( Y < dy ) #declare Z = 0; #while( Z < dz ) #declare it[X][Y][Z] = 1; #declare Z = Z + 1; #end #declare Y = Y + 1; #end #declare X = X + 1; #render concat( "Init ", str(X,1,0), "/", str(dx,1,0), "\r" ) #end #end #macro cut( p1, diff ) #local X = p1.x; #while( X < min( res2, p1.x + diff.x ) ) #local Y = p1.y; #while( Y < min( res2, p1.y + diff.y ) ) #local Z = p1.z; #while( Z < min(res2, p1.z + diff.z) ) #declare it[X][Y][Z] = 0; #local Z = Z + 1; #end #local Y = Y + 1; #end #local X = X + 1; #end #end #macro sponge( p1, diff, iter ) #local dd = diff / 3; cut( p1 + <0, dd.y, dd.z>, dd ) cut( p1 + , dd ) cut( p1 + , dd ) cut( p1 + , dd ) cut( p1 + , dd ) cut( p1 + , dd ) cut( p1 + , dd ) #if( iter > 1 & p1.x < res2 & p1.y < res2 & p1.z < res2 ) #if (iter > 2 ) #render concat( "\nIter ", str(iter,1,0), " <", str(p1.x,1,0), ",", str(p1.y,1,0), ",", str(p1.z,1,0), ">\n" ) #else #render concat( str(iter,1,0), "<", str(p1.x,1,0), ",", str(p1.y,1,0), ",", str(p1.z,1,0), "> " ) #end sponge( p1 + <0, 0, 0>, dd, iter - 1 ) sponge( p1 + <0, 0, dd.z>, dd, iter - 1 ) sponge( p1 + <0, 0, 2 * dd.z>, dd, iter - 1 ) sponge( p1 + <0, dd.y, 0>, dd, iter -1 ) sponge( p1 + <0, dd.y, 2 * dd.z>, dd, iter -1 ) sponge( p1 + <0, 2 * dd.y, 0>, dd, iter -1 ) sponge( p1 + <0, 2 * dd.y, dd.z>, dd, iter -1 ) sponge( p1 + <0, 2 * dd.y, 2 * dd.z>, dd, iter -1 ) sponge( p1 + , dd, iter - 1 ) sponge( p1 + , dd, iter - 1 ) sponge( p1 + , dd, iter -1 ) sponge( p1 + , dd, iter -1 ) sponge( p1 + <2 * dd.x, 0, 0>, dd, iter - 1 ) sponge( p1 + <2 * dd.x, 0, dd.z>, dd, iter - 1 ) sponge( p1 + <2 * dd.x, 0, 2 * dd.z>, dd, iter - 1 ) sponge( p1 + <2 * dd.x, dd.y, 0>, dd, iter -1 ) sponge( p1 + <2 * dd.x, dd.y, 2 * dd.z>, dd, iter -1 ) sponge( p1 + <2 * dd.x, 2 * dd.y, 0>, dd, iter -1 ) sponge( p1 + <2 * dd.x, 2 * dd.y, dd.z>, dd, iter -1 ) sponge( p1 + <2 * dd.x, 2 * dd.y, 2 * dd.z>, dd, iter -1 ) #end #end // this translates from the 1/8th array until a full one #macro retr( X, Y, Z ) #local rX = X; #local rY = Y; #local rZ = Z; #if( rX >= res2 ) #local rX = res2 + res2 - rX - 2; #end #if( rY >= res2 ) #local rY = res2 + res2 - rY - 2; #end #if( rZ >= res2 ) #local rZ = res2 + res2 - rZ - 2; #end it[rX][rY][rZ] #end // make boxes #macro make_eigth( it, res2, size, obj, MESH ) #if( MESH ) mesh { #else union { #end #declare X = 0; #while( X < res2 ) #declare Y = 0; #while( Y < res2 ) #declare Z = 0; #while( Z < res2 ) #if( it[X][Y][Z] = 1 ) #if( MESH ) #if( X = 0 ? 1 : it[max(0,X-1)][Y][Z] ) triangle { * size, * size, * size } triangle { * size, * size, * size } #end #if( Y = 0 ? 1 : it[X][max(0,Y-1)][Z] ) triangle { * size, * size, * size } triangle { * size, * size, * size } #end #if( Z = 0 ? 1 : it[X][Y][max(0,Z-1)] ) triangle { * size, * size, * size } triangle { * size, * size, * size } #end #if( X = res2 ? 1 : !it[min(res2 - 1,X+1)][Y][Z] ) triangle { * size, * size, * size } triangle { * size, * size, * size } #end #if( Y = res2 ? 1 : !it[X][min(res2 - 1,Y+1)][Z] ) triangle { * size, * size, * size } triangle { * size, * size, * size } #end #if( Z = res2 ? 1 : !it[X][Y][min(res2 - 1,Z+1)] ) triangle { * size, * size, * size } triangle { * size, * size, * size } #end /* This was the start of some more optimizations to reduce the number of triangles in the mesh #local z1 = Z; #while( Z < res2 - 1 & it[X][Y][Z] = 1 ) //retr( X, Y, Z ) = 1 ) #declare it[X][Y][Z] = 0; #declare Z = Z + 1; #end #local z2 = Z; box { * size, * size } */ #else object { obj translate * size } #end #end #declare Z = Z + 1; #end #declare Y = Y + 1; #end #declare X = X + 1; #render concat( "Make ", str(X,1,0), "/", str(res2,1,0), "\r" ) #end #if( MESH ) } #else } #end #end // // now put it all together // init( it, res2, res2, res2 ) #render "\n" sponge( <0,0,0>, , iterations ) #render "\n\n" #declare eighth = make_eigth( it, res2, size, 0, 1 ) #render "\n\n" // this might clear up some memory...well, it would in a real language #undef it #declare half = union { object { eighth } object { eighth rotate y * -90 translate x * size * res } object { eighth rotate y * 90 translate z * size * res } object { eighth rotate y * 180 translate } } #declare obj = union { object { half } object { half rotate x * 180 translate z * size * res translate y * size * res } } // since the box is mirrored in X, Y, and Z we only need 1/2 on all sides (1/8th memory) #declare res2 = int( resB / 2 ) + 1; #declare it = array[res2][res2][res2] #declare size = 10 / resB; init( it, res2, res2, res2 ) #render "\n" sponge( <0,0,0>, , iterationsB ) #render "\n\n" #declare eighth = make_eigth( it, res2, size, obj, 0 ) #render "\n\n" #undef it #declare half = union { object { eighth } object { eighth rotate y * -90 translate x * size * resB } object { eighth rotate y * 90 translate z * size * resB } object { eighth rotate y * 180 translate } } #declare whole = union { object { half } object { half rotate x * 180 translate z * size * resB translate y * size * resB } } object { whole texture { pigment { color Red } finish { phong 1 phong_size 90 } } } box { <-20, 0, -20>, <30, 30, 30> texture { pigment { checker <1,1,1>, <0,0,1> scale 2 } finish { phong 1 phong_size 90 } } hollow } light_source { <1, 6, -5> White } light_source { <-10, 29, 5> color <1,1,1> } camera { up y // top full location <-7, 13, -10> location <-2, 12, -2> right 4/3*x look_at <5, 6, 4> }