// Persistence of Vision Ray Tracer version 3.5 Include File // File: shapes.inc // Last updated: 2001.8.13 // Description: This file contains macros for working with objects, as well // as macros for creating special objects, such as bevelled text, // height fields, and rounded shapes. #ifndef(SHAPES_INC_TEMP) #declare SHAPES_INC_TEMP = version; #version 3.5; #ifdef(View_POV_Include_Stack) #debug "including shapes.inc\n" #end #include "shapes_old.inc" #include "consts.inc" #include "transforms.inc" #include "strings.inc" #include "math.inc" // These macros are just interfaces to the trace() function. // They return values through their parameters: // If an intersection is found, they return true and set // OPt to the intersection point, and ONorm to the normal. // Otherwise they return false, and do not modify OPt or ONorm. #macro Isect(Pt, Dir, Obj, OPt) #local Norm = <0,0,0>; #local IPt = trace(Obj, Pt, Dir, Norm); #if (vlength(Norm) > 0) #declare OPt = IPt; #local Return=true; #else #local Return=false; #end (Return) #end #macro IsectN(Pt, Dir, Obj, OPt, ONorm) #local Norm = <0,0,0>; #local IPt = trace(Obj, Pt, Dir, Norm); #if (vlength(Norm) > 0) #declare OPt = IPt; #declare ONorm = Norm; #local Return=true; #else #local Return=false; #end (Return) #end // A shortcut for getting both min and max extents of an object #macro Extents(Obj, Min, Max) #declare Min = min_extent(Obj); #declare Max = max_extent(Obj); #end // shortcuts for using the CenterTrans and AlignTrans // macros with objects. #macro Center_Object(Object, Axis) object {Object Center_Trans(Object, Axis)} #end #macro Align_Object(Object, Axis, Pt) object {Object Align_Trans(Object, Axis, Pt)} #end // A simple beveled text macro. The parameters are: // Font: the name of the font file. // String: the text string the text object is composed of. // Cuts: the number of times excess material is cut off, to form the bevel. // More cuts will give smoother results, but take longer to render. // BevelAng: the angle of the bevel. // BevelDepth: the depth of the bevelled portion of the text. // Depth: the total depth of the text object. // Offset: the offset value for the text object. Since the front faces of each // letter need to be in the same plane, z values are ignored. #macro Bevelled_Text(Font, String, Cuts, BevelAng, BevelDepth, Depth, Offset, UseMerge) #local BBox = text {ttf Font, String Depth, Offset*(x+y)} #if(UseMerge) merge { #else union { #end text {ttf Font, String Depth-BevelDepth, Offset*(x+y)} intersection { #local J=0; #while(J/CA) } #local J=J+1; #end } translate z*BevelDepth bounded_by {box {min_extent(BBox), max_extent(BBox)}} } #end // Constants used for the text macros #declare Align_Left = 1; #declare Align_Right = 2; #declare Align_Center = 3; /* Text_Space( Font, String, Size, Spacing ) Computes the width of a text string, including "white space". It returns the advance widths of all n letters. Text_Space gives the space a text or a glyph occupies in regard to its surroundings. Font: The font to use (see the documentation for the text object) String: The text for which we want to know the width Size: The size to which the text should be scaled Spacing: The amount of space to add between the letters. */ #macro Text_Space(Font, String, Size, Spacing) #local TO = text {ttf Font concat("|",String,"|") 1 Spacing*x scale } #local SO = text {ttf Font "||" 1 Spacing*x scale } ((max_extent(TO).x-min_extent(TO).x)-(max_extent(SO).x-min_extent(SO).x)) #end /* Text_Width( Font, String, Size, Spacing ) Computes the width of a text string. It returns the advance widths of the first n-1 letters, plus the glyph width of the last letter. Text_Width gives the "fysical" width of the text and if you use only one letter the "fysical" width of one glyph. Font: The font to use (see the documentation for the text object) String: The text for which we want to know the width Size: The size to which the text should be scaled Spacing: The amount of space to add between the letters. */ #macro Text_Width(Font, String, Size, Spacing) #local TO = text {ttf Font String 1 Spacing*x scale } (max_extent(TO).x-min_extent(TO).x) #end // Circle_Text author: Ron Parker /* Circle_Text( Font, Text, Size, Spacing, Thickness, Radius, Inverted, Justification, Angle ) Creates a text object with the bottom (or top) of the character cells aligned with all or part of a circle. This macro should be used inside an object{...} block. Font: The font to use (see the documentation for the text object) Text: The text string to be created Size: The height of the text string, as you would use to scale a standard text object Spacing: The amount of space to add between the letters. Thickness: The thickness of the letters (see the documentation for the text object) Radius: The radius of the circle along which the letters are aligned Inverted: If this parameter is nonzero, the tops of the letters will point toward the center of the circle. Otherwise, the bottoms of the letters will do so. Justification: One of the constants Align_Left, Align_Right, or Align_Center Angle: The point on the circle from which rendering will begin. The +x direction is 0 and the +y direction is 90 (i.e. the angle increases anti-clockwise. */ #macro Circle_Text(F, T, S, Sp, Th, R, I, J, A) #local FW = Text_Width(F, T, S, Sp); #local TO = text {ttf F T 1 0 scale} #local TH = max_extent(TO).y; #local C = array[strlen(T)] #if(FW > 2*pi*R) #error concat("\n\n**** Text string \"", T, "\" is too long for a circle of the specified radius.\n\n\n") #end #local AW = -FW*180/pi/R; #local SA = A; #local EA = A + AW; #if(((J = Align_Right) & !I)|((J = Align_Left) & I)) #local SA = A - AW; #local EA = A; #else #if(J = Align_Center) #local SA = A - AW/2; #local EA = A + AW/2; #end #end #local CI = 1; #while(CI <= strlen(T)) #local OE = Text_Width(F, substr(T,CI,1), S, Sp); #local LW = Text_Width(F, substr(T,1,CI), S, Sp) - OE; #local LA = SA + AW*LW/FW + OE/2/FW*AW; #if(I) #local LA = EA - (LA - SA); #end #local TO = text {ttf F substr(T, CI, 1) Th 0 scale} #if(I) #local C[CI-1] = object {TO rotate 180*z translate rotate -90*z translate R*x rotate LA*z } #else #local C[CI-1] = object {TO translate -OE/2*x rotate -90*z translate R*x rotate LA*z } #end #local CI = CI + 1; #end // Create the final object, a union of individual text object letters. union { #local CI=0; #while(CI < strlen(T)) object {C[CI]} #local CI = CI + 1; #end } #end #macro Wedge(Angle) #local A = clamp(Angle, 0, 360); #if(A < 180) difference { plane {-x, 0} plane {-x, 0 rotate y*A} } #else #if(A = 180) plane {-x, 0} #else intersection { plane {x, 0} plane {-x, 0 rotate y*A} inverse } #end #end #end #macro Spheroid(Center, Radius) sphere { 0, 1 scale Radius translate Center } #end #macro Supertorus(RMj, RMn, MajorControl, MinorControl, Accuracy, MaxGradient) #local CP = 2/MinorControl; #local RP = 2/MajorControl; isosurface { function { pow( pow(abs(pow(pow(abs(x),RP) + pow(abs(z),RP), 1/RP) - RMj),CP) + pow(abs(y),CP) ,1/CP) - RMn } threshold 0 contained_by {box {<-RMj-RMn,-RMn,-RMj-RMn>, < RMj+RMn, RMn, RMj+RMn>}} #if(MaxGradient >= 1) max_gradient MaxGradient #else evaluate 1, 10, 0.1 #end accuracy Accuracy } #end // Supercone author: Juha Nieminen // A cone object where each end is an ellipse, you specify two radii // for each end. // SuperCone function: (x^2/a^2+y^2/b^2-1)*(1-z) + (x^2/c^2+y^2/d^2-1)*z = 0 // // camera { location <6,5,-10> look_at 0 angle 35 } // light_source { <100,100,-20>,1 } // plane { y,-1.5 pigment { checker rgb 1, rgb .5 } } // object { SuperCone(<0,-1.5,0>,1,2, <0,1.5,0>,1,.5) // pigment { rgb x } finish { specular .5 } // } #macro Supercone(PtA, A, B, PtB, C, D) intersection { quartic { <0, 0, 0, 0, 0, 0, 0, B*B-2*B*D+D*D, 2*(B*D-B*B), B*B, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, A*A-2*A*C+C*C, 2*(A*C-A*A), A*A, 0, 0, 0, 0, -(A*A-2*A*C+C*C)*(B*B-2*B*D+D*D), -(2*((B*D-B*B)*(A*A-2*A*C+C*C)+(A*C-A*A)*(B*B-2*B*D+D*D))), -(B*B*(A*A-2*A*C+C*C)+4*(A*C-A*A)*(B*D-B*B)+A*A*(B*B-2*B*D+D*D)), -(2*(B*B*(A*C-A*A)+A*A*(B*D-B*B))), -A*A*B*B> sturm } cylinder {0, z, max(max(abs(A), abs(B)), max(abs(C), abs(D)))} bounded_by {cone {0, max(abs(A), abs(B)), z, max(abs(C), abs(D))}} #local Dirv = PtB - PtA; scale <1,1,vlength(Dirv)> #local Dirv = vnormalize(Dirv); #if(vlength(Dirv-<0,0,-1>)=0) scale <1,1,-1> #else Reorient_Trans(z, Dirv) #end translate PtA } #end // Connect two spheres with a cylinder. // Derived from Connect() macro by John VanSickle #macro Connect_Spheres(PtA, RadiusA, PtB, RadiusB) #local Axis = PtB - PtA; #local RadDif = RadiusA - RadiusB; #local Len = VDist(PtA, PtB); #local D2 = sqrt(f_sqr(Len) - f_sqr(RadDif)); cone { PtA + Axis/Len*RadDif*RadiusA/Len, RadiusA*D2/Len, PtB + Axis/Len*RadDif*RadiusB/Len, RadiusB*D2/Len } #end #macro Wire_Box_Union(A, B, WireRadius) Wire_Box(A, B, WireRadius, no) #end #macro Wire_Box_Merge(A, B, WireRadius) Wire_Box(A, B, WireRadius, yes) #end #macro Wire_Box(A, B, WireRadius, UseMerge) #local AA = ; #local BB = ; #local Delta=abs(BB.x-AA.x)/2; #if (Delta; #local BB = ; #else #local AA = ; #local BB = ; #end #local Delta=abs(BB.y-AA.y)/2; #if (Delta; #local BB = ; #else #local AA = ; #local BB = ; #end #local Delta=abs(BB.z-AA.z)/2; #if (Delta; #local BB = ; #else #local AA = ; #local BB = ; #end #local LBF = AA; #local RBF = < BB.x, AA.y, AA.z>; #local RBB = < BB.x, AA.y, BB.z>; #local LBB = < AA.x, AA.y, BB.z>; #local LTF = < AA.x, BB.y, AA.z>; #local RTF = < BB.x, BB.y, AA.z>; #local RTB = BB; #local LTB = < AA.x, BB.y, BB.z>; #if(UseMerge) merge { #else union { #end sphere {LBF, WireRadius} #if (AA.x != BB.x) sphere {RBF, WireRadius} #end #if ((AA.x != BB.x) & (AA.z != BB.z)) sphere {RBB, WireRadius} #end #if (AA.z != BB.z) sphere {LBB, WireRadius} #end #if (AA.y != BB.y) sphere {LTF, WireRadius} #end #if ((AA.x != BB.x) & (AA.y != BB.y)) sphere {RTF, WireRadius} #end #if ((AA.x != BB.x) & (AA.y != BB.y) & (AA.z != BB.z)) sphere {RTB, WireRadius} #end #if ((AA.y != BB.y) & (AA.z != BB.z)) sphere {LTB, WireRadius} #end #if (AA.x != BB.x) cylinder {LBF, RBF, WireRadius} cylinder {LBB, RBB, WireRadius} cylinder {LTB, RTB, WireRadius} cylinder {LTF, RTF, WireRadius} #end #if (AA.y != BB.y) cylinder {LBF, LTF, WireRadius} cylinder {RBF, RTF, WireRadius} cylinder {RBB, RTB, WireRadius} cylinder {LBB, LTB, WireRadius} #end #if (AA.z != BB.z) cylinder {LTB, LTF, WireRadius} cylinder {LBB, LBF, WireRadius} cylinder {RTB, RTF, WireRadius} cylinder {RBB, RBF, WireRadius} #end } #end #macro Round_Box_Union(A, B, EdgeRadius) Round_Box(A, B, EdgeRadius, no) #end #macro Round_Box_Merge(A, B, EdgeRadius) Round_Box(A, B, EdgeRadius, yes) #end #macro Round_Box(A, B, EdgeRadius, UseMerge) #local AA = ; #local BB = ; #local Delta=abs(BB.x-AA.x)/2; #if (Delta; #local BB = ; #else #local AA = ; #local BB = ; #end #local Delta=abs(BB.y-AA.y)/2; #if (Delta; #local BB = ; #else #local AA = ; #local BB = ; #end #local Delta=abs(BB.z-AA.z)/2; #if (Delta; #local BB = ; #else #local AA = ; #local BB = ; #end #local LBF = AA; #local RBF = < BB.x, AA.y, AA.z>; #local RBB = < BB.x, AA.y, BB.z>; #local LBB = < AA.x, AA.y, BB.z>; #local LTF = < AA.x, BB.y, AA.z>; #local RTF = < BB.x, BB.y, AA.z>; #local RTB = BB; #local LTB = < AA.x, BB.y, BB.z>; #if(UseMerge) merge { #else union { #end sphere {LBF, EdgeRadius} #if (AA.x != BB.x) sphere {RBF, EdgeRadius} #end #if ((AA.x != BB.x) & (AA.z != BB.z)) sphere {RBB, EdgeRadius} #end #if (AA.z != BB.z) sphere {LBB, EdgeRadius} #end #if (AA.y != BB.y) sphere {LTF, EdgeRadius} #end #if ((AA.x != BB.x) & (AA.y != BB.y)) sphere {RTF, EdgeRadius} #end #if ((AA.x != BB.x) & (AA.y != BB.y) & (AA.z != BB.z)) sphere {RTB, EdgeRadius} #end #if ((AA.y != BB.y) & (AA.z != BB.z)) sphere {LTB, EdgeRadius} #end #if (AA.x != BB.x) cylinder {LBF, RBF, EdgeRadius} cylinder {LBB, RBB, EdgeRadius} cylinder {LTB, RTB, EdgeRadius} cylinder {LTF, RTF, EdgeRadius} #end #if (AA.y != BB.y) cylinder {LBF, LTF, EdgeRadius} cylinder {RBF, RTF, EdgeRadius} cylinder {RBB, RTB, EdgeRadius} cylinder {LBB, LTB, EdgeRadius} #end #if (AA.z != BB.z) cylinder {LTB, LTF, EdgeRadius} cylinder {LBB, LBF, EdgeRadius} cylinder {RTB, RTF, EdgeRadius} cylinder {RBB, RBF, EdgeRadius} #end box {AA-EdgeRadius*x, BB+EdgeRadius*x} box {AA-EdgeRadius*y, BB+EdgeRadius*y} box {AA-EdgeRadius*z, BB+EdgeRadius*z} } #end #macro Round_Cylinder_Union(A, B, Radius, EdgeRadius) Round_Cylinder(A, B, Radius, EdgeRadius, no) #end #macro Round_Cylinder_Merge(A, B, Radius, EdgeRadius) Round_Cylinder(A, B, Radius, EdgeRadius, yes) #end #macro Round_Cylinder(A, B, Radius, EdgeRadius, UseMerge) #if(UseMerge) merge { #else union { #end #if(Radius RadiusB) #local RA = RadiusB; #local RB = RadiusA; #local PA = PtB; #local PB = PtA; #else #local RA = RadiusA; #local RB = RadiusB; #local PA = PtA; #local PB = PtB; #end #local Axis = vnormalize(PB - PA); #local Len = VDist(PA, PB); #local SA = atan2(RB - RA, Len); #if(UseMerge) merge { #else union { #end #local R1 = RA - EdgeRadius*tan(pi/4 - SA/2); #local R2 = RB - EdgeRadius/tan(pi/4 - SA/2); torus {R1, EdgeRadius Point_At_Trans(Axis) translate PA + Axis*EdgeRadius } torus {R2, EdgeRadius Point_At_Trans(Axis) translate PB - Axis*EdgeRadius } #local D1 = EdgeRadius - EdgeRadius*sin(SA); #local D2 = EdgeRadius + EdgeRadius*sin(SA); cone { PA + Axis*D1, R1 + EdgeRadius*cos(SA), PB - Axis*D2, R2 + EdgeRadius*cos(SA) } cone {PA, R1, PB, R2} } #end // Cones with spherical caps // Sphere-capped cone object with spheres centered on end points. // Derived from Connect() macro by John VanSickle #macro Round_Cone2_Union(PtA, RadiusA, PtB, RadiusB) Round_Cone2(PtA, RadiusA, PtB, RadiusB, no) #end #macro Round_Cone2_Merge(PtA, RadiusA, PtB, RadiusB) Round_Cone2(PtA, RadiusA, PtB, RadiusB, yes) #end #macro Round_Cone2(PtA, RadiusA, PtB, RadiusB, UseMerge) #local Axis = PtB - PtA; #local RadDif = RadiusA - RadiusB; #local Len = VDist(PtA, PtB); #local D2 = f_sqr(Len) - f_sqr(RadDif); #if(D2<0) #error "Round_Cone2() macro called with parameters that can't be handled correctly" #end #local D2 = sqrt(D2); #if(UseMerge) merge { #else union { #end sphere {PtA, RadiusA} sphere {PtB, RadiusB} cone { PtA + Axis/Len*RadDif*RadiusA/Len, RadiusA*D2/Len, PtB + Axis/Len*RadDif*RadiusB/Len, RadiusB*D2/Len } } #end // Sphere-capped cone object with spheres moved and resized // to fit ends of cone. // The cone portion is identical to what you would get using // a cone object with the same parameters, but the spheres are // not centered on the endpoints of the cone, but are moved // to give a smooth transition with the surface #macro Round_Cone3_Union(PtA, RadiusA, PtB, RadiusB) Round_Cone3(PtA, RadiusA, PtB, RadiusB, no) #end #macro Round_Cone3_Merge(PtA, RadiusA, PtB, RadiusB) Round_Cone3(PtA, RadiusA, PtB, RadiusB, yes) #end #macro Round_Cone3(PtA, RadiusA, PtB, RadiusB, UseMerge) #local Axis = vnormalize(PtB - PtA); #local Len = VDist(PtA, PtB); #local SA = atan2(RadiusB - RadiusA, Len); #if(UseMerge) merge { #else union { #end cone {PtA, RadiusA, PtB, RadiusB} sphere {PtA + Axis*tan(SA)*RadiusA, RadiusA/cos(SA)} sphere {PtB + Axis*tan(SA)*RadiusB, RadiusB/cos(SA)} } #end // Two-triangle quad // A---B // |\ | // | \ | // | \| // D---C #macro Quad(A, B, C, D) triangle {A, B, C} triangle {A, C, D} #end #macro Smooth_Quad(A, NA, B, NB, C, NC, D, ND) smooth_triangle {A, NA, B, NB, C, NC} smooth_triangle {A, NA, C, NC, D, ND} #end // HF Macros author: Rune S. Johansen // Optimizations by: Wlodzimierz ABX Skiba // There are several HF macros in shapes.inc, which generate meshes in various shapes. // See more information in the help file. #macro HF_Square (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,MnExt,MxExt) #local WriteFile = (strlen(FileName) > 0); #local xRes = (< 1, 1>*Res).x; #local zRes = (< 1, 1>*Res).y; #local UVheight = (UseUVheight=1); #local UVtex = (UseUVtexture=1); #local Smooth = (Smooth=1); #local Ext = MxExt-MnExt; // CALCULTION OF POINT GRID // Note that the grid extents one element further in all directions // if a smooth heightfield is calculated. This is to ensure correct // normal calculation later on. #local PArr = array[xRes+1+Smooth][zRes+1+Smooth] #local J = 1-Smooth; #while (J; #local P = (UV*Ext*<1,0,1> + MnExt); #if (UVheight) #local H = Function(UV.x, UV.z, 0); #else #local H = Function(P.x, P.y, P.z); #end #declare PArr[J][K] = P + H*Ext*y; #declare K = K+1; #end #declare J = J+1; #end HFCreate_() #end #macro HF_Sphere (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,Center,Radius,Depth) #local WriteFile = (strlen(FileName) > 0); #local xRes = (< 1, 1>*Res).x; #local zRes = (< 1, 1>*Res).y; #local UVheight = (UseUVheight=1); #local UVtex = (UseUVtexture=1); #local Smooth = (Smooth=1); // CALCULTION OF POINT GRID // Note that the grid extents one element further in all directions // if a smooth heightfield is calculated. This is to ensure correct // normal calculation later on. #local PArr = array[xRes+1+Smooth][zRes+1+Smooth] #local J = 1-Smooth; #while (J; #local Dir = vrotate( vrotate(x,(-89.9999+179.9998*UV.z)*z), -360*UV.x*y ); #local P = Center + Dir * Radius; #if (UVheight) #local H = Function(UV.x, UV.z, 0); #else #local H = Function(P.x, P.y, P.z); #end #declare PArr[J][K] = P + H*Dir*Depth; #declare K = K+1; #end #declare J = J+1; #end HFCreate_() #end #macro HF_Cylinder (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,EndA,EndB,Radius,Depth) #local WriteFile = (strlen(FileName) > 0); #local xRes = (< 1, 1>*Res).x; #local zRes = (< 1, 1>*Res).y; #local UVheight = (UseUVheight=1); #local UVtex = (UseUVtexture=1); #local Smooth = (Smooth=1); #local Axis = EndB-EndA; #local Base = VPerp_To_Vector(Axis); // CALCULTION OF POINT GRID // Note that the grid extents one element further in all directions // if a smooth heightfield is calculated. This is to ensure correct // normal calculation later on. #local PArr = array[xRes+1+Smooth][zRes+1+Smooth] #local J = 1-Smooth; #while (J; #local Dir = vaxis_rotate(Base,Axis,-360*UV.x-90); #local P = EndA+Axis*UV.z+Dir*Radius; #if (UVheight) #local H = Function(UV.x, UV.z, 0); #else #local H = Function(P.x, P.y, P.z); #end #declare PArr[J][K] = P + H*Dir*Depth; #declare K = K+1; #end #declare J = J+1; #end HFCreate_() #end #macro HF_Torus (Function,UseUVheight,UseUVtexture,Res,Smooth,FileName,Major,Minor,Depth) #local WriteFile = (strlen(FileName) > 0); #local xRes = (< 1, 1>*Res).x; #local zRes = (< 1, 1>*Res).y; #local UVheight = (UseUVheight=1); #local UVtex = (UseUVtexture=1); #local Smooth = (Smooth=1); // CALCULTION OF POINT GRID // Note that the grid extents one element further in all directions // if a smooth heightfield is calculated. This is to ensure correct // normal calculation later on. #local PArr = array[xRes+1+Smooth][zRes+1+Smooth] #local J = 1-Smooth; #while (J; #local Dir = vrotate(vrotate(-x,360*UV.z*z),-360*UV.x*y); #local P = vrotate(Major*x,-360*UV.x*y)+Dir*Minor; #if (UVheight) #local H = Function(UV.x, UV.z, 0); #else #local H = Function(P.x, P.y, P.z); #end #declare PArr[J][K] = P + H*Dir*Depth; #declare K = K+1; #end #declare J = J+1; #end HFCreate_() #end // Internal macro - not intended to be called by user. #macro HFCreate_ () #if(WriteFile) #fopen _HFMACRO_OUTPUT_FILE FileName write #write(_HFMACRO_OUTPUT_FILE,"mesh2 {\nvertex_vectors {\n",xRes*zRes,",\n", #else mesh2 {vertex_vectors{xRes*zRes, #end #local J = 1; #while (J<=xRes) #local K = 1; #while (K<=zRes) #if(WriteFile) #if(J=zRes & K=zRes) PArr[J][K],"\n", #else PArr[J][K],",\n", #end #else PArr[J][K], #end #declare K = K+1; #end #declare J = J+1; #end #if(WriteFile) "}\n") #else } #end #if (Smooth) #if(WriteFile) #write(_HFMACRO_OUTPUT_FILE,"normal_vectors {\n",xRes*zRes,",\n", #else normal_vectors{xRes*zRes, #end // CALCULATION OF NORMAL VECTOR // We don't vnormalize the vectors from the current center point // to its neightbor points because we want a weighted average // where bigger areas contribute more. This also means that the // center point can be left out completely of the calculations: #local J = 1; #while (J<=xRes) #local K = 1; #while (K<=zRes) #if(WriteFile) #if(J=zRes & K=zRes) vnormalize(vcross(PArr[J][K+1]-PArr[J][K-1], PArr[J+1][K]-PArr[J-1][K])),"\n", #else vnormalize(vcross(PArr[J][K+1]-PArr[J][K-1], PArr[J+1][K]-PArr[J-1][K])),",\n", #end #else vnormalize(vcross(PArr[J][K+1]-PArr[J][K-1], PArr[J+1][K]-PArr[J-1][K])), #end #declare K = K+1; #end #declare J = J+1; #end #if(WriteFile) "}\n") #else } #end #end #if (UVtex) #if(WriteFile) #write(_HFMACRO_OUTPUT_FILE,"uv_vectors {\n",xRes*zRes,",\n", #else uv_vectors{xRes*zRes, #end #local J = 1; #while (J<=xRes) #local K = 1; #while (K<=zRes) #if(WriteFile) #if(J=zRes & K=zRes) <(J-1)/(xRes-1),(K-1)/(zRes-1)>,"\n", #else <(J-1)/(xRes-1),(K-1)/(zRes-1)>,",\n", #end #else <(J-1)/(xRes-1),(K-1)/(zRes-1)>, #end #declare K = K+1; #end #declare J = J+1; #end #if(WriteFile) "}\n") #else } #end #end #if(WriteFile) #write(_HFMACRO_OUTPUT_FILE,"face_indices {\n",(xRes-1)*(zRes-1)*2",\n", #else face_indices{(xRes-1)*(zRes-1)*2, #end #local F1 = <0,zRes,zRes+1>; #local F2 = <0,zRes+1,1>; #local J = 0; #while (J