string data; float timeout = 1.5; // Time to wait until we complain that we're been forgotten about. // Chan to tell display on. integer dchan; // Size of display integer xsize; integer ysize; // FOV of camera float fov; integer myid=-1; integer x; integer y; integer numrender; integer reflectmax; integer fsaa; float bright; vector light; // Position of the light source vector mypos; rotation myrot; // Color for no collision vector space = <0.002,.08,.12>; // shapes! integer OBJ_CUBE = 0; integer OBJ_SPHERE = 1; integer OBJ_OTHER = 2; // Sensor'd data list obj_pos; list obj_rot; list obj_key; list obj_name; list obj_min; //bbox list obj_max; //bbox list obj_color; list obj_reflect; list obj_diffuse; list obj_shape; integer obj_count; integer czIsInPoly(vector pos, list poly) { integer l = llGetListLength(poly); poly += llList2Vector(poly,0); // Close the poly integer i; float angle; for (i = 0; i < l; i++) { float a = llVecNorm(llList2Vector(poly,i) - pos) * llVecNorm(llList2Vector(poly,i+1) - pos); if ((a < 1) && (a > -1)) angle += llAcos(a); } return (angle >= 6.283); } // plane : pn = k // line : P = P1 + u(P2 - P1) vector czLinePlane(vector start, vector slope, vector normal, vector point) { return start + (((point * normal - (start * normal)) / (slope * normal)) * slope); } integer linesphere_didcollide = 0; vector linesphere_normal; // p1, d = start, slope vector czLineSphere(vector p1, vector d, vector pos, rotation rot, vector min, vector max) { vector scale = ; scale /=2; vector orig_d = d; vector orig_p1 = p1; // pos.x += min.x + max.x; // pos.y += min.y + max.y; // pos.z += min.z + max.z; p1 -= pos; p1 /= rot; d /= rot; float a = (llPow(d.x,2) / llPow(scale.x,2)) + (llPow(d.y,2) / llPow(scale.y,2)) + (llPow(d.z,2) / llPow(scale.z,2)); float b = ((2*p1.x*d.x)/llPow(scale.x,2)) + ((2*p1.y*d.y)/llPow(scale.y,2)) + ((2*p1.z*d.z)/llPow(scale.z,2)); float c = (llPow(p1.x,2)/llPow(scale.x,2)) + (llPow(p1.y,2)/llPow(scale.y,2)) + (llPow(p1.z,2)/llPow(scale.z,2)) - 1; float s = llPow(b,2) - (4*a*c); if (s < 0) { linesphere_didcollide = 0; return ZERO_VECTOR; } float u1 = (-b + llSqrt(s)) / (2*a); float u2 = (-b - llSqrt(s)) / (2*a); vector i1 = (p1 + (u1 * d))*rot; vector i2 = (p1 + (u2 * d))*rot; vector n1 = llVecNorm(i1); vector n2 = llVecNorm(i2); i1 += pos; i2 += pos; linesphere_didcollide = 1; if (s == 0) { // Tangent. return i1; } else { // We now have two points. See if we collide with the front point. p1 *= rot; float i1dist = llVecDist(orig_p1,i1); float i2dist = llVecDist(orig_p1,i2); if (i1dist < i2dist) { if (i1dist < .01) { // Point on the sphere if (orig_d * n1 < 0) { // If we're looking into the face, it hits. linesphere_normal = n1; return i1; } } else if (orig_d * llVecNorm(i1 - orig_p1) > -.1) { // Point not on the sphere linesphere_normal = n1; return i1; } } else { // First point didn't hit. Try the second. if (i2dist < .01) { // Point on the sphere if (orig_d * n2 < 0) { // If we're looking into the face, it hits. linesphere_normal = n2; return i2; } } else if (orig_d * llVecNorm(i2 - orig_p1) > -.1) { // Point not on the sphere linesphere_normal = n2; return i2; } } linesphere_didcollide=0; // Nevermind! return ZERO_VECTOR; } } vector czReflectRay(vector ray, vector normal) { return ray - ((2*normal) * (normal * ray)); } // returns: Distance, intersect point, surface normal list czGetIntersect(vector cpos, vector cfwd, vector pos, rotation rot, vector min, vector max, integer obj_num) { list face = [20000,ZERO_VECTOR, ZERO_VECTOR]; // Default face, really far away. if (llList2Integer(obj_shape, obj_num) == OBJ_SPHERE) { vector intersect = czLineSphere(cpos, cfwd, pos, rot, min, max); if (linesphere_didcollide) { if (llVecDist(intersect,cpos) < llList2Float(face,0)) { face = [llVecDist(intersect,cpos), intersect, linesphere_normal]; } } } else { // OBJ_CUBE and others. Use bounding box // 1,0,0 (FWD) if ( (<1,0,0> * rot) * cfwd < 0) { vector intersect = czLinePlane(cpos,cfwd,(<-1,0,0> * rot), pos+ (*rot)); if (llVecDist(cpos, intersect) < llList2Float(face,0) && (llVecNorm(cfwd) * llVecNorm(intersect - cpos)) >= 0) { if (czIsInPoly(intersect, [ * rot + pos, * rot + pos, * rot + pos, * rot + pos ] )) { face = [llVecDist(cpos,intersect), intersect, <1,0,0>*rot]; } } } // 0,1,0 (LEFT) if ( (<0,1,0> * rot) * cfwd < 0) { vector intersect = czLinePlane(cpos,cfwd,(<-0,1,0> * rot), pos+ (<0,max.y,0>*rot)); if (llVecDist(cpos, intersect) < llList2Float(face,0) && (llVecNorm(cfwd) * llVecNorm(intersect - cpos)) >= 0) { if (czIsInPoly(intersect, [ * rot + pos, * rot + pos, * rot + pos, * rot + pos ] )) { face = [llVecDist(cpos,intersect), intersect, <0,1,0>*rot]; } } } // -1,0,0 (BACK) if ( (<-1,0,0> * rot) * cfwd < 0) { vector intersect = czLinePlane(cpos,cfwd,(<-1,0,0> * rot), pos+ (*rot)); if (llVecDist(cpos, intersect) < llList2Float(face,0) && (llVecNorm(cfwd) * llVecNorm(intersect - cpos)) >= 0) { if (czIsInPoly(intersect, [ * rot + pos, * rot + pos, * rot + pos, * rot + pos ] )) { face = [llVecDist(cpos,intersect), intersect, <-1,0,0>*rot]; } } } // 0,-1,0 (RIGHT) if ( (<0,-1,0> * rot) * cfwd < 0) { vector intersect = czLinePlane(cpos,cfwd,(<0,-1,0> * rot), pos+ (<0,min.y,0>*rot)); if (llVecDist(cpos, intersect) < llList2Float(face,0) && (llVecNorm(cfwd) * llVecNorm(intersect - cpos)) >= 0) { if (czIsInPoly(intersect, [ * rot + pos, * rot + pos, * rot + pos, * rot + pos ] )) { face = [llVecDist(cpos,intersect), intersect, <0,-1,0>*rot]; } } } // 0,0,1 (UP) if ( (<0,0,1> * rot) * cfwd < 0) { vector intersect = czLinePlane(cpos,cfwd,(<0,0,1> * rot), pos+ (<0,0,max.z>*rot)); if (llVecDist(cpos, intersect) < llList2Float(face,0) && (llVecNorm(cfwd) * llVecNorm(intersect - cpos)) >= 0) { if (czIsInPoly(intersect, [ * rot + pos, * rot + pos, * rot + pos, * rot + pos ] )) { face = [llVecDist(cpos,intersect), intersect, <0,0,1>*rot]; } } } // 0,0,-1 (DOWN) if ( (<0,0,-1> * rot) * cfwd < 0) { vector intersect = czLinePlane(cpos,cfwd,(<0,0,-1> * rot), pos+ (<0,0,min.z>*rot)); if (llVecDist(cpos, intersect) < llList2Float(face,0) && (llVecNorm(cfwd) * llVecNorm(intersect - cpos)) >= 0) { if (czIsInPoly(intersect, [ * rot + pos, * rot + pos, * rot + pos, * rot + pos ] )) { face = [llVecDist(cpos,intersect), intersect, <0,0,-1>*rot]; } } } } // OBJ_CUBE return face; } // returns: Distance, intersect point, surface normal, object name, sensor # // Must be called within a sensor. list czDrawRay(vector campos, vector camfwd) { list surface = [100000, ZERO_VECTOR, ZERO_VECTOR, "nothing", 0]; integer i; for (i = 0; i < obj_count; i++) { if (llVecDist(campos, llList2Vector(obj_pos,i)) - 10 < llList2Float(surface,0)) { // Ignore really far stuff. list x = czGetIntersect(campos,camfwd, llList2Vector(obj_pos,i), llList2Rot(obj_rot,i), llList2Vector(obj_min,i), llList2Vector(obj_max,i), i); if (llList2Float(x,0) < llList2Float(surface,0)){ surface = x; surface += [llList2String(obj_name,i), i]; } } } return surface; } // Returns 0 through 1 for the amount of shadow we're in/ float czShadow(vector pos, vector normal) { vector tolight = llVecNorm(light-pos); // // returns: Distance, intersect point, surface normal, object name, sensor # list l = czDrawRay(pos, tolight); float direct; float intensity; float dist = llVecDist(light,pos); if (llList2Float(l,0) < llVecDist(light,pos)) { direct = .0; // Shadowed (ambient light only) intensity = 1.0; //llRezObject("myline",(pos + llList2Vector(l,1))/2,ZERO_VECTOR,llRotBetween(<0,0,1>,tolight),llRound(llVecDist(llList2Vector(l,1),pos)*1000)); } else { direct = 1; // Directly lit //llRezObject("myline",(pos + light)/2,ZERO_VECTOR,llRotBetween(<0,0,1>,tolight),llRound(llVecDist(light,pos)*1000)); float dv = tolight * normal; if (dv < 0) dv = 0; // Fixes a rare crash intensity = llSqrt(dv) / llPow(llVecDist(light,pos)/bright,2);; if (intensity < 0) intensity = 0; } float s = direct*intensity; if (s < 0) s = 0; if (s > 1) s = 1; s = (s*0.8) + 0.2; return s; } vector drawpoint; vector drawnormal; integer drawing; list msgs; DEBUG(string msg) { llMessageLinked(LINK_SET, myid, "DEBUG", (key)msg); llSleep(.05); } vector czGetRayColor(vector mypos, vector fwd, integer reflectsleft) { list ray = czDrawRay(mypos, fwd); drawpoint = llList2Vector(ray,1); drawnormal = llList2Vector(ray,2); vector col = space; if (llList2Float(ray,0) < 5000) { // Too far to be sensed, it's not a hit. if (llList2Vector(obj_color, llList2Integer(ray,4)) == <-1,-1,-1>) { if ( ((integer)drawpoint.x + (integer)drawpoint.y + (integer)drawpoint.z) % 2 < 1) { col = <1,1,1>; } else { col = <0,0,0>; } } else { // Not a textured object, give it random color. col = llList2Vector(obj_color,llList2Integer(ray,4)); } col *= czShadow(drawpoint,drawnormal); // Now to calculate a reflection! vector reflect = czReflectRay(fwd,drawnormal); // Noise. Disabled because it looks cheap. float r = llList2Float(obj_diffuse,llList2Integer(ray,4)); reflect = llVecNorm(reflect + ); // Phong! float phong = reflect*llVecNorm(light-drawpoint); if (phong > .85) { phong -= .85; col += *2; } if (reflectsleft) { // Reflected image. vector reflection = czGetRayColor(drawpoint, reflect, --reflectsleft); float rf = llList2Float(obj_reflect,llList2Integer(ray,4)); col = (col*(1.0 - rf)) + (reflection*(rf)); } return col; } else { return space; } } vector czDrawXY(integer x, integer y) { // llResetTime(); // Calculate float dist = 1/llTan(fov/2.0*DEG_TO_RAD); float aspect = (float)xsize/(float)ysize; vector color = ZERO_VECTOR; integer i; // fsaa-X integer j; // fsaa-Y for (i = 0; i < fsaa; i++) { for (j = 0; j < fsaa; j++) { float x2; float y2; if (fsaa <= 1 ) { x2 = x; y2 = y; } else { x2 = (float)x + ( (float)(i+1) / (float)(fsaa+1) ); y2 = (float)y + ( (float)(j+1) / (float)(fsaa+1) ); } vector fwd = llVecNorm() * myrot; color += czGetRayColor(mypos,fwd,reflectmax); } } color /= llPow(fsaa,2); return color; } default { state_entry() { llMessageLinked(LINK_SET, 0, "getinfo", NULL_KEY); } link_message(integer snum, integer num, string msg, key dat) { if (num == 0) { if (msg == "getrenderers") { string name = llGetScriptName(); integer p; while ( (p = llSubStringIndex(name," ")) != -1) { name = llGetSubString(name,p+1,-1); } myid = (integer)name+1; llSleep(.05*(float)myid); llMessageLinked(LINK_SET, 0, "amrenderer", NULL_KEY); } else if (msg == "info") { list dat = llParseString2List((string)dat, ["|^|"], []); xsize = (integer)llList2String(dat,0); ysize = (integer)llList2String(dat,1); myrot = (rotation)llList2String(dat,2); mypos = (vector)llList2String(dat,3); fov = (float)llList2String(dat,4); light = (vector)llList2String(dat,5); obj_count = (integer)llList2String(dat,6); dchan = (integer)llList2String(dat,7); bright = (float)llList2String(dat,8); reflectmax = (integer)llList2String(dat,9); fsaa = (integer)llList2String(dat,10); obj_pos = []; obj_rot = []; obj_key = []; obj_name = []; obj_color = []; obj_reflect = []; obj_diffuse = []; obj_shape = []; obj_min = []; obj_max = []; } else if (msg == "objinfo") { list dat = llParseString2List((string)dat, ["|^|"], []); obj_pos += (vector)llList2String(dat,0); obj_rot += (rotation)llList2String(dat,1); obj_key += (key)llList2String(dat,2); obj_name += (string)llList2String(dat,3); obj_color += (vector)llList2String(dat,4); obj_reflect += (float)llList2String(dat,5); obj_diffuse += (float)llList2String(dat,6); obj_shape += (integer)llList2Integer(dat,7); obj_min += (vector)llList2String(dat,8); obj_max += (vector)llList2String(dat,9); } else if (msg == "done") { llSetTimerEvent(0); } } else if (num == myid) { if (llGetSubString(msg,0,6) == "render ") { list dat = llCSV2List(llGetSubString(msg,7,-1)); integer x = (integer)llList2String(dat,0); integer y = (integer)llList2String(dat,1); integer chunksize = (integer)llList2String(dat,2); integer i; list chunk = []; integer startx = x; integer starty = y; for (i = 0; i < chunksize ; i++) { vector pix = czDrawXY(x,y); chunk += pix; x++; if (x >= xsize) { x = 0; y++; if (y >= ysize) { // Lucky! We finished early. llSetTimerEvent(0); data = llDumpList2String([startx,starty] + chunk, "|^|"); llMessageLinked(LINK_SET, myid, "next", data); return; } } } data = llDumpList2String([startx,starty] + chunk, "|^|"); llMessageLinked(LINK_SET, myid, "next", data); llSetTimerEvent(timeout); } } } timer() { DEBUG("Script #" + (string)myid + " has been idle for " + (string)timeout + " sec... Requesting new block."); llSetTimerEvent(0); if (llStringLength(data)) { llMessageLinked(LINK_SET, myid, "next", data); } else { llMessageLinked(LINK_SET, myid, "next", NULL_KEY); } } }