The following is a set of scripts for rezzing surfaces from arbitrary parametric equations.
Pictures:
1,
2,
3.
//Parametric Surface Rezzer
//by Seifert Surface
//Nov 2006
//////PARAMETERS//////
//Example surface, WARNING: rezzes 200 prims
float scale = 1.0; //scales the surface in all directions. If this is 2.0 the surface will be twice as large as if it is 1.0
float depth = 0.01; //thickness of triangles rezzed
float min_u = -4.0;
float max_u = 4.0;
integer steps_u = 10;
float min_v = -4.0;
float max_v = 4.0;
integer steps_v = 10;
vector f(float u, float v) //the parametric function
{
return <u, v, 2.0 * llSin(u) * llSin(v)>;
}
//////END OF PARAMETERS//////
//Description:
//This collection of scripts rezzes a surface defined by a parametric function. The surface is made out of
//triangles, one prim per triangle. There will be at most 2*steps_u*steps_v triangles in the final surface
//(it is possible to have fewer triangles as the script does not rez degenerate triangles). To change the
//surface rezzed, edit the PARAMETERS section above.
//Do whatever you want with this script. I'll be annoyed if someone tries to sell it to newbies, but I think the
//chances of that are slim!
//Instructions:
//There are three scripts. The scripts "surface_rezzer" (this script), and "triangle_detailer" go in the
//prim that will do the building. Another script, "triangle_rezzee" goes inside another prim, which must be
//called "triangle". The prim "triangle" goes in the inventory of the builder prim.
//To build the surface, touch the builder prim. The surface will be built centered on the location of the
//builder prim when touched. Be careful when rezzing surfaces near the ground, as the builder can get stuck
//trying to move to a position to rez something when that position is underground.
//Once the surface is rezzed, the following commands can be shouted on channel 347:
//"die" - Deletes all rezzed triangles (in range of your shout).
//"clear" - removes scripts from all rezzed triangles (in range of your shout).
//If you prefer, you can have the triangles remove their scripts as soon as they are in position, there is
//a commented-out line in the triangle rezzee script which can be uncommented to do this.
//Have fun!
// Seifert Surface
// 2G!tGLf 2nLt9cG
integer comm_channel = -61347732;
integer rez_counter;
vector origin;
float delta = 0.01;
rez_triangle(vector a, vector b, vector c)
{
a = origin + scale * a;
b = origin + scale * b;
c = origin + scale * c;
//check for 2 vertices in the same place
if(llVecDist(a,b) < delta || llVecDist(b,c) < delta || llVecDist(c,a) < delta)
{ //if so, don't try to rez the triangle
return;
}
float cosA = (b - a) * (c - a);
float cosB = (c - b) * (a - b);
float cosC = (a - c) * (b - c); //signs -ve means obtuse angle
if(cosA < 0.0) //so the angle at a is obtuse, meaning the opposite edge must be the base of the prim
{
triangle(a, b, c, TRUE);
}
else if(cosB < 0.0)
{
triangle(b, c, a, TRUE);
}
else if(cosC < 0.0)
{
triangle(c, a, b, TRUE);
}
else //all acute angles... so we can choose which way to base the prim, so as to minimise the
{ //error introduced by the shear value having a resolution of only 0.01
float error1 = triangle(a,b,c,FALSE);
float error2 = triangle(b,c,a,FALSE);
float error3 = triangle(c,a,b,FALSE);
if(error1 < error2)
{
if(error1 < error3)
{
triangle(a,b,c,TRUE);
}
else
{
triangle(c,a,b,TRUE);
}
}
else
{
if(error2 < error3)
{
triangle(b,c,a,TRUE);
}
else
{
triangle(c,a,b,TRUE);
}
}
}
}
float triangle(vector a, vector b, vector c, integer rez)
{
float width = llVecDist(b, c);
vector left = llVecNorm(b - c);
vector fwd = llVecNorm(left % (a - c));
vector up = fwd % left;
float height = (a - c) * up;
float y_shear = 0.5 - ((b-a) * left) / width;
if(rez)
{
vector center = 0.5 * ( (b+c) + (height * up) );
vector scale = <depth, width, height>;
rotation rot = llAxes2Rot(fwd, left, up);
llMessageLinked(LINK_THIS, comm_channel + rez_counter, (string)scale + "|" + (string)y_shear, NULL_KEY);
while(llVecDist(llGetPos(), center) > 9.5)
{
llSetPos(center);
}
llRezObject("triangle", center, ZERO_VECTOR, rot, comm_channel + rez_counter);
rez_counter += 1;
return -1.0;
}
else
{
y_shear = (float)llRound(y_shear * 100.0) / 100.0;
return llVecDist(a, 0.5 * (b + c) + height * up + y_shear * width * left);
//error between where the vertex of the triangle should be and would be...
}
}
rez_surface()
{
integer i;
integer j;
float scale_u = (max_u - min_u) / (float)steps_u;
float scale_v = (max_v - min_v) / (float)steps_v;
for(i=0; i<steps_u; i+=1)
{
for(j=0; j<steps_u; j+=1)
{
vector A = f(min_u + scale_u * i, min_v + scale_v * j);
vector B = f(min_u + scale_u * (i + 1), min_v + scale_v * j);
vector C = f(min_u + scale_u * i , min_v + scale_v * (j + 1));
vector D = f(min_u + scale_u * (i + 1) , min_v + scale_v * (j + 1));
rez_triangle(A, B, C);
rez_triangle(B, D, C);
//llSleep(0.2); //in some very laggy situations this may be necessary to add, shouldn't be necessary
//only use if the triangle_detailer gets backed up on its list.
}
}
}
default
{
touch_start(integer num)
{
if(llDetectedKey(0) == llGetOwner())
{
origin = llGetPos();
rez_surface();
while(llVecDist(llGetPos(), origin) > 9.5) //go back to the start
{
llSetPos(origin);
}
llSetPos(origin);
llOwnerSay("Done!");
}
}
}
//Triangle Detailer
//by Seifert Surface
//Nov 2006
integer comm_channel2 = 61347732;
list rezzees;
list tri_data;
default
{
state_entry()
{
llListen(comm_channel2, "", NULL_KEY, "");
}
link_message(integer sender, integer num, string str, key id)
{
rezzees += [num];
tri_data += [str];
if(llGetListLength(rezzees) > 10)
{
llSay(DEBUG_CHANNEL, "Detailer list getting long...");
}
}
listen(integer chan, string name, key id, string msg)
{
//llOwnerSay((string)llGetFreeMemory() + " " + (string)llGetListLength(rezzees)); //in case you want to see if the detailer is getting overloaded
integer who = llListFindList(rezzees, [(integer)msg]);
if(who != -1)
{
llShout((integer)msg, llList2String(tri_data, who));
rezzees = llDeleteSubList(rezzees, who, who);
tri_data = llDeleteSubList(tri_data, who, who);
}
}
}
//Triangle Rezzee
//by Seifert Surface
//Nov 2006
integer comm_channel = -61347732;
integer comm_channel2 = 61347732;
integer me;
integer listen_control;
integer times_to_ask = 10;
default
{
state_entry()
{
llListen(347, "", "", "");
}
on_rez(integer param)
{
if(param != 0)
{
me = param;
llSetObjectName((string)(me - comm_channel));
listen_control = llListen(me, "", "", "");
llShout(comm_channel2, (string)me);
llSetTimerEvent(5.0);
}
}
listen(integer chan, string name, key id, string msg)
{
if(msg == "die")
{
llDie();
}
else if(msg == "clear")
{
llRemoveInventory(llGetScriptName());
}
else if(chan == me)
{
list data = llParseStringKeepNulls(msg, ["|"], []);
vector scale = (vector)llList2String(data, 0);
if(scale.x < 0.01)
{
scale.x = 0.01;
}
if(scale.y < 0.01)
{
scale.y = 0.01;
}
if(scale.z < 0.01)
{
scale.z = 0.01;
}
float y_shear = (float)llList2String(data, 1);
llSetPrimitiveParams([PRIM_TYPE, PRIM_TYPE_BOX, 0, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <1.0, 0.0, 0.0>, <0.0, y_shear, 0.0>, PRIM_SIZE, scale]);
//llRemoveInventory(llGetScriptName()); //if you want to remove scripts as soon as possible, uncomment this.
llListenRemove(listen_control);
llSetTimerEvent(0.0);
}
}
timer()
{
llShout(comm_channel2, (string)me);
times_to_ask -= 1;
if(times_to_ask <= 0)
{
llSetTimerEvent(0.0);
llSay(DEBUG_CHANNEL, "no reply after 10 tries");
}
}
}
Here are some example parametric equations to plug in. The number of prims used can be reduced by lowering the steps_u and steps_v variables, though the surfaces will not look as good. It is also a good idea to lower the scale number as well, to avoid prims trying (and failing) to scale to above 10m along any dimension. The parametric equations are from:
http://vmm.math.uci.edu/3D-XplorMath/Surface/gallery.html
//Sphere, with a slight twist
//224 prims
float depth = 0.01;
float scale = 3.0;
float min_u = 0.0;
float max_u = TWO_PI;
integer steps_u = 16;
float min_v = 0.0;
float max_v = PI;
integer steps_v = 8;
vector f(float u, float v)
{
return <llSin(u + 0.5 * v) * llSin(v), llCos(u + 0.5 * v) * llSin(v), llCos(v)>;
}
//Klein Bottle
//800 prims, large (rez in a sandbox)
float depth = 0.01;
float scale = 5.0;
float min_u = 0.0;
float max_u = TWO_PI;
integer steps_u = 20;
float min_v = 0.0;
float max_v = TWO_PI;
integer steps_v = 20;
float a = 2.0;
vector f(float u, float v)
{
float temp = (a + llCos(u * 0.5) * llSin(v) - llSin(u*0.5)*llSin(2*v));
return < temp * llCos(u), temp * llSin(u), llSin(0.5*u)*llSin(v) + llCos(0.5*u) * llSin(2*v)>;
}
//Dini's Surface
//800 prims, large (rez in a sandbox)
float depth = 0.01;
float scale = 10.0;
float min_u = -5.0;
float max_u = 5.0;
integer steps_u = 20;
float min_v = -3.14159;
float max_v = PI;
integer steps_v = 20;
vector f(float u, float v)
{
float psi = 1.2; //this can be from 0 to PI
float sinpsi = llSin(psi);
float cospsi = llCos(psi);
float g = (u - cospsi * v) / sinpsi;
float s = llPow(2.7182818, g);
float r = (2 * sinpsi) / (s + 1 / s);
float t = r * (s - 1 / s) * 0.5;
return <u - t, r * llCos(v), r * llSin(v)>;
}
//Boy's surface
//1250 prims, large
float depth = 0.01;
float scale = 25.0;
float min_u = 0.0;
float max_u = PI;
integer steps_u = 25;
float min_v = 0.0;
float max_v = PI;
integer steps_v = 25;
float E = 2.718281828;
float Exp(float x)
{
return llPow(E, x);
}
float Cosh(float x)
{
return 0.5 * (Exp(x) + Exp(-x));
}
float Sinh(float x)
{
return 0.5 * (Exp(x) - Exp(-x));
}
vector f(float u, float v)
{
float x = llCos(u) * llSin(v);
float y = llSin(u) * llSin(v);
float z = llCos(v);
float ef = 0.5 * ( (2 * x*x - y*y - z*z) + 2*y*z*(y*y - z*z) + z*x*(x*x - z*z) + x*y*(y*y - x*x) );
float g = llSqrt(3.0) * 0.5 * ( (y*y - z*z) + z*x*(z*z - x*x) + x*y*(y*y - x*x) );
float h = (x+y+z) * ( llPow(x+y+z, 3.0) + 4 * (y - x)*(z - y)*(x - z) );
return <ef,g,h * 0.125>;
}