home *** CD-ROM | disk | FTP | other *** search
- /* CMD: Random Surface Points
- * and Lines
- * Generate a set of points randomly distributed across the surface of
- * an object by pricking it with many tiny needles.
- *
- * Redone in 08/94 to create hair by Gonzalo Garramuo
- * To create Hair: cut all the polygons of your head that should have
- * hair to a new layer and run this program. To create
- * realistic hair you should use pretty high numbers
- * (2000-10000) in the Number of Points Requester.
- * With this high numbers, it can take a while.
- * After the macro finishes, you may still want to
- * retouch the hair a little bit, to avoid it to go
- * through the ears of your character, to "comb" it, etc.
- * The "hair" are made of a huge number of lines (2-point
- * particles or polygons), therefore, you should use
- * shadows when rendering. Why? Because these 2-
- * sided polygons will receive light a little different
- * than normal polygons: they won't be shaded when they
- * should be.
- * version 2.1 10/94 by Gonzalo Garramuo
- * added segmentation to lines (to allow "combing")
- * version 2.2 10/94 by Gonzalo Garramuo
- * added rotation center and sizing center
- */
-
- syscode = "Random Surface Points"
- statfil = 'T:RandomPoints.state'
- version = 'Prick v2.2'
-
- /* Boilerplate.*/
-
- mxx = "LWModelerARexx.port"
- mxx_add = addlib(mxx,0)
- signal on error
- signal on syntax
- call addlib("rexxmathlib.library",0,-30,0)
- lines = 0
- call main
- if (mxx_add) then call remlib(mxx)
- exit
-
- syntax:
- error:
- t = Notify(1,'!Rexx Script Error','@'ErrorText(rc),'Line 'SIGL)
- if (mxx_add) then call remlib(mxx)
- exit
-
- main:
-
- fg = curlayer()
- emp = emptylayers()
- if (words(emp) < 2) then do
- call notify(1,"!Need two background layers for scratch work.")
- return
- end
- temp = word(emp,1)
- temp2 = word(emp,2)
-
- /* Get bounding box for fg data. */
- parse value boundingbox() with n x1 x2 y1 y2 z1 z2 .
- if (n < 3) then do
- call notify(1,"!I need at least a 3-sided polygon to work on.")
- return
- end
-
- center = (x1+x2)/2 (y1+y2)/2 (z1+z2)/2
-
- if (setup() = 0) then return
-
- lo = x1 y1 z1
- hi = x2 y2 z2
- dx = x2 - x1
- dy = y2 - y1
- dz = z2 - z1
-
- /* Compute our slightly expanded box for the ends of the needles. */
- exb = max(dx, dy, dz) / 20
- xx1 = x1 - exb
- xx2 = x2 + exb
- yy1 = y1 - exb
- yy2 = y2 + exb
- zz1 = z1 - exb
- zz2 = z2 + exb
-
- /* Compute distribution cell size. We want about "num" points
- * distributed along the surface of the bounding box, so take that
- * surface area, divide by num. Square root of that is the cell
- * edge size (s). Also compute min allowed distance (md) and
- * tiny delta for needle construction (d).
- */
- sa = 2 * (dx * dy + dy * dz + dz * dx)
- s = sqrt(sa / num)
- md = s / 4
- d = md / 20
- say "Cell edge size:" s " Merge dist:" md
-
- /* Goto background layer and build our lattice of needles. */
- call setlayer(temp)
- call add_begin()
-
- nx = dx % s
- ny = dy % s
- nz = dz % s
- say "Needle grid" nx ny nz
- call meter_begin(nx * ny + nx * nz + ny * nz, syscode)
-
- do xi = 1 to nx
- x0 = x1 + (x2 - x1) / nx * (xi - 0.5)
- do yi = 1 to ny
- y0 = y1 + (y2 - y1) / ny * (yi - 0.5)
- xa = x0 + (randu()-0.5) * s
- xb = x0 + (randu()-0.5) * s
- ya = y0 + (randu()-0.5) * s
- yb = y0 + (randu()-0.5) * s
- i1 = add_point(xa ya zz1)
- i2 = add_point(xa ya+d zz1)
- i3 = add_point(xb yb zz2)
- call add_polygon(i1 i2 i3)
- call meter_step()
- end yi
- end xi
-
- do xi = 1 to nx
- x0 = x1 + (x2 - x1) / nx * (xi - 0.5)
- do zi = 1 to nz
- z0 = z1 + (z2 - z1) / nz * (zi - 0.5)
- xa = x0 + (randu()-0.5) * s
- xb = x0 + (randu()-0.5) * s
- za = z0 + (randu()-0.5) * s
- zb = z0 + (randu()-0.5) * s
- i1 = add_point(xa yy1 za)
- i2 = add_point(xa yy1 za+d)
- i3 = add_point(xb yy2 zb)
- call add_polygon(i1 i2 i3)
- call meter_step()
- end zi
- end xi
-
- do yi = 1 to ny
- y0 = y1 + (y2 - y1) / ny * (yi - 0.5)
- do zi = 1 to nz
- z0 = z1 + (z2 - z1) / nz * (zi - 0.5)
- ya = y0 + (randu()-0.5) * s
- yb = y0 + (randu()-0.5) * s
- za = z0 + (randu()-0.5) * s
- zb = z0 + (randu()-0.5) * s
- i1 = add_point(xx1 ya za)
- i2 = add_point(xx1 ya za+d)
- i3 = add_point(xx2 yb zb)
- call add_polygon(i1 i2 i3)
- call meter_step()
- end zi
- end yi
-
- call meter_end()
- call add_end()
-
- /* Perform the slice and leave only the points. */
- call setblayer(fg)
- call soliddrill(SLICE)
- call removepols()
-
- /* Delete the points that lie outside the bounding box of the
- * original object, leaving the ones that intersected the interior
- * surfaces. Then merge points using our min distance. */
- call sel_mode(USER)
- call sel_point(SET)
- d = exb / 2
- call sel_point(CLEAR, VOL, x1-d y1-d z1-d, x2+d y2+d z2+d)
- call cut()
- call mergepoints(md)
-
- if (lines) then call hair
-
- if same = 1 then call CUT()
- call SETLAYER(temp2)
-
- call CUT()
- if same = 1 then do
- call SETLAYER(fg)
- end
- call PASTE()
-
- return
-
-
- setup:
-
- /* Setup state variables, reading stored ones, if any. */
- num = 500
- lines = 1
- size = 1.05
- rot = 0
- rotcenter = center
- sizcenter= center
- segments = 2
- axis = 1
- jitter = ((x1-word(center,1))/10) ((y1-word(center,2))/10)
- jitter = jitter||((z1-word(center,3))/10)
- jittertype = 1
- same = 1
-
- if (exists(statfil)) then do
- if (~open(state, statfil, 'R')) then break
- if (readln(state) ~= version) then break
- num = readln(state)
- lines = readln(state)
- segments = readln(state)
- size = readln(state)
- sizcenter = readln(state)
- rot = readln(state)
- rotcenter = readln(state)
- axis = readln(state)
- jitter = readln(state)
- jittertype = readln(state)
- same = readln(state)
- call close state
- end
-
- /* Query user for their function and area to evaluate. */
- call req_begin syscode
-
- id_num = req_addcontrol("Max. Number of Points", 'N')
- id_lines= req_addcontrol("Create lines", 'B')
- id_segments = req_addcontrol("Number of Segments", 'N')
- id_size= req_addcontrol("Line Size (Scaling)", 'N')
- id_sizcenter= req_addcontrol("Sizing Center","V",1)
- id_rot= req_addcontrol("Line Angle (Rotation)", 'N')
- id_rotcenter= req_addcontrol("Rotation Center", 'V', 1)
- id_axis= req_addcontrol("Rotation Axis", C, "X Y Z")
- id_jitter= req_addcontrol("Jitter Amount", V, 1)
- id_jittertype= req_addcontrol("Jitter Type", C, "Uniform Gaussian
- Normal Radial")
- id_same= req_addcontrol("Put Results on Original Layer", 'B')
-
- call req_setval id_num, num
- call req_setval id_lines, lines
- call req_setval id_segments, segments
- call req_setval id_size, size
- call req_setval id_sizcenter, sizcenter
- call req_setval id_rot, rot
- call req_setval id_rotcenter, rotcenter
- call req_setval id_axis, axis
- call req_setval id_jitter, jitter
- call req_setval id_jittertype, jittertype
- call req_setval id_same, same
-
- if (~req_post()) then do
- call req_end
- return 0
- end
-
- num = req_getval(id_num) % 1
- lines = req_getval(id_lines)
- segments = req_getval(id_segments)
- size = req_getval(id_size)
- sizcenter = req_getval(id_sizcenter)
- rot = req_getval(id_rot)
- rotcenter = req_getval(id_rotcenter)
- axis = req_getval(id_axis)
- jitter= req_getval(id_jitter)
- jittertype = req_getval(id_jittertype)
- same = req_getval(id_same)
-
- call req_end
-
- /* Save state now, in case something fails. */
- if (open(state, statfil, 'W')) then do
- call writeln state, version
- call writeln state, num
- call writeln state, lines
- call writeln state, segments
- call writeln state, size
- call writeln state, sizcenter
- call writeln state, rot
- call writeln state, rotcenter
- call writeln state, axis
- call writeln state, jitter
- call writeln state, jittertype
- call writeln state, same
- call close state
- end
-
- return 1
-
- /* How does it work? It copies all the points created by prick, sizes
- them, rotates them, and jitters them. Finally it draws a line
- connecting each of the old points with the new, modified copies of
- themselves. If you use good values, the results are good hair. */
- hair:
-
- if size >= 1 then do
- sizepreset = (size - 1) / segments
- end
- else do
- sizepreset = -((1 - size) / segments)
- end
-
- size = 1 + sizepreset
-
- if rot > 0 then do
- rot = rot / segments
- end
-
- do l = 1 to segments
- call COPY()
- call SCALE(size size size,sizcenter)
- IF rot~=0 then do
- call ROTATE(rot,translate(axis,"XYZ","123"),rotcenter)
- end
- select
- when jittertype=1 then jittertype="Gaussian"
- when jittertype=2 then jittertype="Uniform"
- when jittertype=3 then jittertype="Normal"
- otherwise jittertype="Radial"
- end
- /* Lightwave 3.0-3.1 users, type "call JITTER(jitter)" instead of
- next line. */
- call JITTER(jitter,jittertype)
- call PASTE()
-
- n = xfrm_begin()
- if n=0 then return
-
- /* The meters can get a little corrupted, but it is okay */
-
- call meter_begin n, 'Drawing 'n/2' Lines', "Step 1"
-
- do i = 1 to n
- Point.i= xfrm_getpos(i)
- call meter_step
- end
- call xfrm_end()
- call CUT()
-
- call meter_end()
-
- /* Modeler works very strangely here. It seems the COPY() and PASTE()
- also copy the point order. So that when you paste, let's say, point #2
- and there is already point #2 in that layer, it makes the new point
- point #3, and the old point number #3 becomes #4 (ie. it inserts
- points in the middle of the points already in the layer).
- If COPY() and PASTE() worked as it should be expected, so that if you
- have 300 points in the layer, and copy other 200 to it, these new 200
- points would go from 301-500, then this loop would be different. It
- would go from 1 to n/2 and the second point added would be i=i+n,
- insted of i=i+1. Weird! It took me three hours to figure this out.
- This is not mentioned in the Arexx docs. */
-
- call meter_begin n/2, 'Drawing 'n/2' Lines', "Step 2"
- call add_begin()
- s=0
- do i=1 to n
- s=s+1
- call add_point Point.i
- i=i+1
- call add_point Point.i
- s=s+1
- call add_polygon s-1 s
- call meter_step
- end i
- call add_end()
- call meter_end()
-
- call CUT()
- call setlayer(temp2)
- call PASTE()
- call MERGEPOINTS()
-
- call setlayer(temp)
-
- call add_begin()
- do i=1 to n-1
- i=i+1
- call add_point Point.i
- end
- call add_end()
-
- end l
-
- return
-
- /* End of Macro */
-