@version 2.2 @warnings // by Steve Hurley // steve@hurleyworks.com // version 1.15 updated to work with 6.5b on 5/31/2001 main { //======= get a list of surfaces for the requestor ======== if(!editbegin()) error("Need some geometry in this layer"); editend(); surfaces = get_surf_list("ALL SURFACES"); //======== get a list of WeightMaps for the requestor ============= total_maps = 0; vmap = VMap(VMWEIGHT) ; vmapnames += "NONE"; while(vmap && vmap.type == VMWEIGHT) { vmapnames += vmap.name; vmap = vmap.next(); total_maps = total_maps + 1; } //================== build requestor =========================== leftmargin = 25; column1 = 265; row1 = 0; reqbegin(" Splinerator version 1.15 5/31/2001"); reqsize(598,500); s2 = ctlsep(); ctlposition(s2,0,row1+45); c2 = ctlpopup("Select Surface ",1,surfaces); ctlposition(c2,leftmargin,row1+60); c3 = ctlpopup("WeightMap",1,vmapnames); ctlposition(c3,column1,row1+60); c4 = ctlcheckbox("Invert Map",false); ctlposition(c4,column1+230,row1 + 60); s3 = ctlsep(); ctlposition(s3,0,row1+100); c5 = ctlminislider("Visible segments",3,1,100); ctlposition(c5,leftmargin+49,row1+115); c6= ctlminislider("Hidden segments",1,1,100); ctlposition(c6,column1+3,row1+115); c7 = ctlminislider("Number of guides",100,1,10000); ctlposition(c7,leftmargin+44,row1+155); c8 = ctlstring("Knot base name","segment"); ctlposition(c8,column1+8,row1 + 155); s4 = ctlsep(); ctlposition(s4,0,row1+195); c9 = ctlminislider("Length (centimeters)",65,0,1000); ctlposition(c9,leftmargin+30,row1+210); c10= ctlpopup("WeightMap",1,vmapnames); ctlposition(c10,column1,row1+210); c11 = ctlcheckbox("Invert Map",false); ctlposition(c11,column1+230,row1 + 210); c12 = ctlminislider("Length vary",0,0,100); ctlposition(c12,leftmargin+71,row1+250); c13 = ctlpopup("WeightMap",1,vmapnames); ctlposition(c13,column1,row1+250); c14 = ctlcheckbox("Invert Map",false); ctlposition(c14,column1+230,row1 + 250); c15= ctlminislider("Droop",50,-100,100); ctlposition(c15,leftmargin+97,row1+290); c16= ctlpopup("WeightMap",1,vmapnames); ctlposition(c16,column1,row1+290); c17 = ctlcheckbox("Invert Map",false); ctlposition(c17,column1+230,row1 + 290); s5 = ctlsep(); ctlposition(s5,0,row1+330); c18= ctlminislider("Comb X",0,-100,100); ctlposition(c18,leftmargin+90,row1+345); c19 = ctlpopup("WeightMap",1,vmapnames); ctlposition(c19,column1,row1+345); c20 = ctlcheckbox("Invert Map",false); ctlposition(c20,column1+230,row1 + 345); c21= ctlminislider("Comb Y",0,-100,100); ctlposition(c21,leftmargin+90,row1+385); c22 = ctlpopup("WeightMap",1,vmapnames); ctlposition(c22,column1,row1+385); c23 = ctlcheckbox("Invert Map",false); ctlposition(c23,column1+230,row1+385); c24= ctlminislider("Comb Z",0,-100,100); ctlposition(c24,leftmargin+90,row1+425); c25 = ctlpopup("WeightMap",1,vmapnames); ctlposition(c25,column1,row1+425); c26 = ctlcheckbox("Invert Map",false); ctlposition(c26,column1+230,row1+425); c27 = ctltext(""," For generating long hair spline guides for Worley Lab's Sasquatch plugin"); ctlposition(c27,0,2); if(reqpost()) { sel = getvalue(c2); // surface name hair_surf = surfaces[sel]; visible = getvalue(c5); // number of visible segments hidden = getvalue(c6); // number of hidden segments knotname = getvalue(c8); hairs = getvalue(c7); hairlength = getvalue(c9)/100; // cause we want centimeters lengthvary = getvalue(c12); droop = getvalue(c15)/50; // dividing by 100 seems too small for these guys comb_X = getvalue(c18)/50; comb_Y = getvalue(c21)/50; comb_Z = getvalue(c24)/50; density_map = getvalue(c3); invert_density_map = getvalue(c4); length_map = getvalue(c10); invert_length_map = getvalue(c11); comb_mapX = getvalue(c19); invert_comb_mapX = getvalue(c20); comb_mapY = getvalue(c22); invert_comb_mapY = getvalue(c23); comb_mapZ = getvalue(c25); invert_comb_mapZ = getvalue(c26); droop_map = getvalue(c16); invert_droop_map = getvalue(c17); len_vary_map = getvalue(c13); invert_len_vary_map = getvalue(c14); } else return; //================ map stuff >> make a list of unique maps used ========================== combX_mode = 0; combY_mode = 0; combZ_mode = 0; lmap_mode = 0; dmap_mode = 0; droop_mode = 0; len_vary_mode = 0; maps = 0; un_maps = 0; matches = 0; var maps_used[7]; if(density_map > 1) { dmap_mode = 1; matches = 0; if(un_maps == 0) { un_maps = un_maps + 1; maps_used[un_maps] = density_map; } else { for(x = 1; x <= un_maps; ++x) if(density_map == maps_used[x]) matches = 1; if(matches == 0) { un_maps = un_maps + 1; maps_used[un_maps] += density_map; } } } if(length_map > 1) { lmap_mode = 1; matches = 0; if(un_maps == 0) { un_maps = un_maps + 1; maps_used[un_maps] = length_map; } else { for(x = 1; x <= un_maps; ++x) if(length_map == maps_used[x]) matches = 1; if(matches == 0) { un_maps = un_maps + 1; maps_used[un_maps] = length_map; } } } if(comb_mapX > 1) { combX_mode = 1; matches = 0; if(un_maps == 0) { un_maps = un_maps + 1; maps_used[un_maps] = comb_mapX; } else { for(x = 1; x <= un_maps; ++x) if(comb_mapX == maps_used[x]) matches = 1; if(matches == 0) { un_maps = un_maps + 1; maps_used[un_maps] = comb_mapX; } } } if(comb_mapY> 1) { combY_mode = 1; matches = 0; if(un_maps == 0) { un_maps = un_maps + 1; maps_used[un_maps] = comb_mapY; } else { for(x = 1; x <= un_maps; ++x) if(comb_mapY == maps_used[x]) matches = 1; if(matches == 0) { un_maps = un_maps + 1; maps_used[un_maps] = comb_mapY; } } } if(comb_mapZ > 1) { combZ_mode = 1; matches = 0; if(un_maps == 0) { un_maps = un_maps + 1; maps_used[un_maps] = comb_mapZ; } else { for(x = 1; x <= un_maps; ++x) if(comb_mapZ == maps_used[x]) matches = 1; if(matches == 0) { un_maps = un_maps + 1; maps_used[un_maps] = comb_mapZ; } } } if(droop_map > 1) { droop_mode = 1; matches = 0; if(un_maps == 0) { un_maps = un_maps + 1; maps_used[un_maps] = droop_map; } else { for(x = 1; x <= un_maps; ++x) if(droop_map == maps_used[x]) matches = 1; if(matches == 0) { un_maps = un_maps + 1; maps_used[un_maps] = droop_map; } } } if(len_vary_map > 1) { len_vary_mode = 1; matches = 0; if(un_maps == 0) { un_maps = un_maps + 1; maps_used[un_maps] = len_vary_map; } else { for(x = 1; x <= un_maps; ++x) if(len_vary_map == maps_used[x]) matches = 1; if(matches == 0) { un_maps = un_maps + 1; maps_used[un_maps] = len_vary_map; } } } //================= segment stuff =========== segments = visible + hidden; segment_length = hairlength/visible; lengthvary = 1-(lengthvary/100); small_length = segment_length*lengthvary; //============ check for some points ================= count = editbegin(); if(count == 0) { error("Need some geometry in this layer"); return; } editend(); //=====set the knot names..... 1st is "root" ..rest is user========== knots = segments + 1; pointnames[1] = "Root (don't move!)"; for(x= 2; x <= knots; ++x) { pointnames[x] = string(knotname,"__",x - 1); if(x <= hidden + 1) pointnames[x] = pointnames[x] + "--(hidden)"; } pointnames[knots] = pointnames[knots] + "--(tip point)"; //==================== layer stuff ==================== fg = lyrfg(); bg = lyrbg(); empty = lyrempty(); worklayer = empty[1]; seedlayer = empty[2]; plantlayer = empty[3]; lyrsetfg(fg); //=======select hair surface or whole object and copy it to the work layer and triple ========= // added because of some undocumented change in LW9 // this means that the surface polys you want the quides // to grow from must be selected first selmode(USER); if(hair_surf || "All SURFACES") selpolygon(SET,SURFACE,hair_surf); copy(); lyrsetfg(worklayer); paste(); triple(); //============= find out how much hair goes where ================ moninit(4,"creating splines"); monstep(1); //==== send out for the area of each triangle and calc total surface area of mesh ======= totalarea = 0; polys = polycount(); totalpolys = polys[1]; editbegin(); for(x = 1; x <= totalpolys; ++x) { polyID = polygons[x]; area[x] = tri_area(polyID); totalarea = totalarea + area[x]; } //============= make each area a percentage of the whole =========== for(x = 1; x <= totalpolys; ++x) { area[x] = (area[x])/totalarea; } //==if maps are used then calculate the average Vmap weight per polygon for each map that's used==================== if(un_maps > 0) { for(w = 1; w <= un_maps; ++w) { vmap = VMap(vmapnames[maps_used[w]]); for(x = 1; x <= totalpolys; ++x) { polyID = polygons[x]; pcount = polypointcount(polyID); ppoints = polyinfo(polyID); total = 0; for(k = 2;k <= pcount + 1; ++k) { values = 0; // so if it has *no* map value it will be set to 0 for this weight map if(vmap.isMapped(ppoints[k])) { values = vmap.getValue(ppoints[k],1); } total = total + values; } ave_weight[x] = total/pcount; // average weight for this polygon for this map //============ give out the ave weight for this map on this polygon to the maps that want it ==================== if(density_map == maps_used[w]) { dense_ave_weight[x] = ave_weight[x]; if(invert_density_map) dense_ave_weight[x] = 1 - ave_weight[x]; } if(length_map == maps_used[w]) { length_weight[x] = ave_weight[x]; if(invert_length_map) length_weight[x] = 1 - ave_weight[x]; } if(comb_mapX == maps_used[w]) { combX_weight[x] = ave_weight[x]; if(invert_comb_mapX) combX_weight[x] = 1 - ave_weight[x]; } if(comb_mapY == maps_used[w]) { combY_weight[x] = ave_weight[x]; if(invert_comb_mapY) combY_weight[x] = 1 - ave_weight[x]; } if(comb_mapZ == maps_used[w]) { combZ_weight[x] = ave_weight[x]; if(invert_comb_mapZ) combZ_weight[x] = 1 - ave_weight[x]; } if(droop_map == maps_used[w]) { droop_weight[x] = ave_weight[x]; if(invert_droop_map) droop_weight[x] = 1 - ave_weight[x]; } if(len_vary_map == maps_used[w]) { len_vary_weight[x] = ave_weight[x]; if(invert_len_vary_map) len_vary_weight[x] = 1 - ave_weight[x]; } //====if it's a density map, then continue below -===== } //===== density maps need special treatment ........make each area a percentage of the whole============ if(dmap_mode) { var weight_area = 0; for(x = 1; x <= totalpolys; ++x) { area[x] = area[x] * dense_ave_weight[x]; weight_area = weight_area + area[x]; } for(x = 1; x <= totalpolys; ++x) { area[x] = area[x]/weight_area; } } } } //============== end of map mode =============== //====find random locations in this mesh with poly IDs weighted by surface area ========== pick = .618034; // the magic number for(x = 1; x <= hairs; ++x) { //====== roll the dice ===== y = 0; match = 0; while(pick >= match) { ++y; match = match + area[y]; } polyID = polygons[y]; norm[x] = polynormal(polyID); growhere[x] = find_random_point(polyID); //============== give each hair it's appropriate weight(per map) for this poly ============== if(lmap_mode) weighted_length[x] = length_weight[polyID]; if(combX_mode) weighted_combX[x] = combX_weight[polyID]; if(combY_mode) weighted_combY[x] = combY_weight[polyID]; if(combZ_mode) weighted_combZ[x] = combZ_weight[polyID]; if(droop_mode) weighted_droop[x] = droop_weight[polyID]; if(len_vary_mode) weighted_len_vary[x] = len_vary_weight[polyID]; //=================================================================================== // update random number pick = pick + .618034; if(pick >= 1) pick = pick - 1; } editend(); //============ distribute the guides ============== comb_strength = 4; gravity = droop; low = min(small_length,segment_length); high = segment_length; lyrsetfg(seedlayer); for(x = 1; x <= hairs; ++x) // the big loop thru all the guides { editbegin(); if(len_vary_mode) // apply weight if map is on low = segment_length - (low * weighted_len_vary[x]); segment_length = random(low,high); // length vary comb_X_strength = comb_X; comb_Y_strength = comb_Y; comb_Z_strength = comb_Z; //=============== add weight map influences ==================== if(lmap_mode) segment_length = segment_length * weighted_length[x]; // from the weight length map if(combX_mode) comb_X_strength = comb_X * weighted_combX[x]; if(combY_mode) comb_Y_strength = comb_Y * weighted_combY[x]; if(combZ_mode) comb_Z_strength = comb_Z * weighted_combZ[x]; if(droop_mode) gravity = droop * weighted_droop[x]; comb_direction[x] = ; //=========== generate the points for the spline ======== for(z = 1; z <= knots; ++z) { //========== pile the hidden points on top of each other ===== if(z <= hidden) { addpoint(growhere[x]); } else { //========== plot the visible points ============ addpoint(growhere[x]); norm[x].x = norm[x].x + comb_strength/segments * comb_direction[x].x; norm[x].y = norm[x].y + comb_strength/segments * comb_direction[x].y - gravity/segments; norm[x].z = norm[x].z + comb_strength/segments * comb_direction[x].z; growhere[x].x = growhere[x].x + segment_length * norm[x].x; growhere[x].y = growhere[x].y + segment_length * norm[x].y; growhere[x].z = growhere[x].z + segment_length * norm[x].z; a = norm[x].x; b = norm[x].y; c = norm[x].z; norm[x] = normalize(); } } editend(); //=======get the pointID's and make the spline add 1 point polys for nameing========== count = editbegin(); for(p = 1; p <= count; ++p) { pts[p] = points[p]; surf = pointnames[p]; // add 1point polys for names addpolygon(pts[p], surf); } addcurve(pts); editend(); //============== cut the spline, paste it plant layer and return for the next one ========= cut(); lyrsetfg(plantlayer); paste(); lyrsetfg(seedlayer); } //====== clean up the mess... end up with splines in the foreground and the mesh in the background===== lyrsetfg(worklayer); cut(); lyrsetfg(plantlayer); cut(); lyrsetfg(worklayer); paste(); lyrsetbg(fg); monend(); //========================== end of main ================================= } //============= calculate the area of a triangle ====================== tri_area: polyID { pointcount = polypointcount(polyID); // get # of points for this poly ppoints = polyinfo(polyID); // get their IDs for(x =1;x <= pointcount; ++x) where[x] = pointinfo(ppoints[x+1]); // cause of surface name from polyinfo() // ========================find length of each side ======================= sum = 0; where[4] = where[1]; // so we can find all 3 sides easier for(x = 1; x <=3; x++) { dis[x] = extent(where[x],where[x+1]); length[x] = vmag(dis[x]); sum = sum + .5*length[x]; } //================calculate area and send it back ========================= area = sqrt(sum*(sum-length[1])*(sum-length[2])*(sum-length[3])); return(area); } //=========== function to create a random point in the triangle ========== find_random_point: polyID { pointcount = polypointcount(polyID); // get # of points for this poly ppoints = polyinfo(polyID); // get their IDs for(x =1;x <= pointcount; ++x) v[x] = pointinfo(ppoints[x+1]); // cause of surface name from polyinfo() // =========== calculate random weight ============ s = randu(); t = randu(); if(s + t > 1) { s = 1 - s; t = 1 - t; } newspot = s*v[1] + t*v[2] + (1 - s - t)*v[3]; return(newspot); } // ============== return list of surfaces .... add "All SURFACES" the the list ====== get_surf_list: surf1 { surf_list[1] = surf1; surfs = nextsurface(); surf_list[2] = surfs; count = 2; while(surfs) { ++count; surfs = nextsurface(surfs); surf_list[count] = surfs; } surf_list.trunc(); surf_list.sortA(); return(surf_list); }