Don't click here unless you want to be banned.

LSL Wiki : LibraryBezierCurveDemo

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are crawl338.us.archive.org

Bezier Curve Demo version 1.0.1

Author: LionelForager
This set of scripts will draw a bezier curve segment controlled by 3 control points.

It tries to be the foundation of a more general graph server in SL.

The idea and first version of the bezier curve demo script, was given to me by CatherineOmega. She had a working script that rezzed small spheres in each point position.
Then i added some modifications and the possibility of drawing segments made up of cylinders to rezz a line instead of just points.

The complete bezier server consists of several parts:

A description of each part will be given.


Server object


Description


The server object is the one which calculates the point coordinates interpolated using a bezier curve.
It rezzes the segments or point marks.
It shows the positions of the three control points in its display text.
Commands to it are given in the channel 1.
It receives the positions in that channel from the control points, and when it has all three, draws the curve.
When a new position is received from a control point, it clears the previously rezzed objects and draws new ones.
The server knows which control point has moved by the name of the object that has said the command it receives.

Commands - How to use


Once you have rezzed the bezier server, you can say commands to it through channel #1 to control its behaviour.
Commands are:

How to create de server


To create the bezier demo server object, create a prim that you like and put the following script in its inventory.
Then you should create the segment and point mark objects (see descriptions below) and put them in the inventory of the server object too (see bellow).

// Bezier Curve Demo 1.0.1
// Catherine Omega Heavy Industries
// Modified by Lionel Forager 29-12-2006: Added segment drawing between points. Corrected minor bugs.

key gOwner; // object owner

float diam=0.05; //diameter of line segments.

list gPoints; // list of vectors to feed to bezier3
string gObject="Point Mark"; // name of object to rez as the mark in each point
string gSegment="Segment"; // name of object to rez as the mark in each point

integer gNumSegments = 10; // number of segments to rez
integer gMarks= TRUE; //Draw marks in the point positions.
integer gLines= FALSE; //Don't draw segments between points.

float gMu;
vector gPos1;
vector gPos2;
vector gPos3;

//codes a line number and a segment in a unique ID integer
integer codeID(integer line, integer segment)
{
    //Code de id: line# in 16 most significant bits, segment# in 16 less significant bits
    return ( (line << 16) + segment);
}
//Rezz a segment between 2 given points and assigning it to a line # and a segment #
drawLineSegment(vector p1,vector p2,integer line,integer seg)
{
    //Calc the position of the center of the segment
    vector center= (p1+p2)/2.;
    vector localZ= p2 - p1;
    //Get distance between points
    float distance= llVecMag(localZ);
    //Normalize the vector
    localZ= localZ / distance;

    vector xAxis;
    //Let's choose as x local axis the direction of a vector normal to localZ and
    //contained in the plain given by localZ and global X or global Y, the one that is less parallel to localZ
    if ( localZ.x < localZ.y ) xAxis= <1.,0.,0.>;
    else xAxis= <0.,1.,0.>;
    //localX is the one contain in the plane given by localZ and xAxis that is normal to localZ.
    //that's it localX= xAxis - localZ*xaxis*localZ. We should normalize the vector to get a unitary one.
    vector localX= xAxis - (localZ * xAxis) * localZ;
    localX= llVecNorm(localX);
    //Now get the rotation to put axis Z oriented pointing to the direction between the two given points.
    rotation rot= llAxes2Rot(localX,localZ % localX, localZ);    
    //Crear el objeto en la posici?n del server.
    llRezObject(gSegment,center,ZERO_VECTOR,rot,codeID(line,seg));
    llSay(line,"segInit "+(string)seg+","+(string)diam+"," + (string)distance);

}
//Three control point Bezier interpolation
//mu ranges from 0 to 1, start to end of the curve
vector bezier3(vector p1, vector p2, vector p3, float mu)
{
   float mum1;
   float mum12;

   float mu2;
   vector p;

   mu2 = mu * mu;
   mum1 = 1 - mu;
   mum12 = mum1 * mum1;
   
   // p= (1-mu)^2 p1 + 2*mu*(1-mu)*p2 + mu^2 * p3
   p.x = p1.x * mum12 + 2. * p2.x * mum1 * mu + p3.x * mu2;
   p.y = p1.y * mum12 + 2. * p2.y * mum1 * mu + p3.y * mu2;
   p.z = p1.z * mum12 + 2. * p2.z * mum1 * mu + p3.z * mu2;

   return(p);
}

start(integer segments)
{
     if (gPos1 != ZERO_VECTOR && gPos2 != ZERO_VECTOR && gPos3 != ZERO_VECTOR)
    {
        gNumSegments = segments;
        float increment = 1.0 / gNumSegments;
        
        float current_mu;
        
        integer i;
        vector oldPos= gPos1;
        vector pos;

        //Render the mark at the position of the first point
        if( gMarks ) llRezObject(gObject,gPos1,ZERO_VECTOR,ZERO_ROTATION,0);

        for (i = 1; i <= gNumSegments; i++)
        {
            current_mu= i * increment;
            pos = bezier3(gPos1,gPos2,gPos3,current_mu);
            if(gMarks) llRezObject(gObject,pos,ZERO_VECTOR,ZERO_ROTATION,0);
            if( gLines ) drawLineSegment(oldPos,pos,1,i);
            oldPos= pos;
        }
    }
}

updateText()
{
    vector color = <1,0,0>;
    if (gPos1 != ZERO_VECTOR && gPos2 != ZERO_VECTOR && gPos3 != ZERO_VECTOR)
    {
        color = <0,1,0>;
    }
    
    string output = (string)gPos1 + "\n" + (string)gPos2  + "\n" + (string)gPos3;
    llSetText(output, color, 1.0);
}

// get gCommand and gSubCommand (now improved to handle null gSubCommands.)
string gCommand;
string gSubCommand;

string gCommandLower;
string gSubCommandLower;

parseCommands(string input, string divider)
{
    integer command_divide = llSubStringIndex(input,divider);
    if (command_divide == -1)
    {
        gCommand = llGetSubString(input, 0, command_divide);
        gSubCommand = "";
    }
    else
    {
        gCommand = llGetSubString(input, 0, command_divide - 1);
        gSubCommand = llGetSubString(input, command_divide + 1, llStringLength(input));
    }
    
    gCommandLower = llToLower(gCommand);
    gSubCommandLower = llToLower(gSubCommand);
}

default
{
    state_entry()
    {
        gOwner = llGetOwner();
        //gObject = llGetInventoryName(INVENTORY_OBJECT,0);
        
        updateText();
        
        llListen(1,"","","");
        llSay(0,"Online.");
    }
    
    listen(integer channel, string name, key id, string m)
    {
        if (llGetOwnerKey(id) == gOwner)
        {
            if (id == gOwner)
            {
                parseCommands(m," ");
                
                if (gCommandLower == "mu")
                {
                    vector pos = bezier3(gPos1,gPos2,gPos3,(float)gSubCommand);
                    llRezObject(gObject,pos,ZERO_VECTOR,ZERO_ROTATION,0);
                }
                
                else if (gCommandLower == "start")
                {
                    start((integer)gSubCommand);
                    
                    llSay(0,"Done.");
                }
                
                else if (gCommandLower == "segments")
                {
                    gNumSegments = (integer)gSubCommand;
                }
                else if (gCommandLower == "markers")
                {
                    if( gSubCommand != "off" ) {
                        gMarks= TRUE;
                        llSay(0,"Markers on");
                    }
                    else {
                        gMarks=FALSE;
                        llSay(0,"Markers off");
                    }

                }
                else if (gCommandLower == "lines")
                {
                    if( gSubCommand != "off" ) {
                        gLines= TRUE;
                        llSay(0,"Lines on");
                    }
                    else {
                        gLines=FALSE;
                        llSay(0,"Lines on");
                    }
                }
            }
            
            else
            {
                // make sure it's actually a vector
                if ((vector)m != ZERO_VECTOR)
                {
                    if (llToLower(name) == "p1")
                    {
                        gPos1 = (vector)m;
                        llSay(1,"clear");
                        updateText();
                        start(gNumSegments);
                    }
                    
                    else if (llToLower(name) == "p2")
                    {
                        gPos2 = (vector)m;
                        llSay(1,"clear");
                        updateText();
                        start(gNumSegments);                        
                    }
                    
                    else if (llToLower(name) == "p3")
                    {
                        gPos3 = (vector)m;
                        llSay(1,"clear");
                        updateText();
                        start(gNumSegments);
                    }
                }
            }
        }
    }
    
    on_rez(integer start_param)
    {
        llResetScript();
    }
}


Control Points

Description


The Control Point objects are the prims that you can position in place to control the shape of the bezier curve.
A Bezier curve has three control points (p1,p2, and p3).
The curve passes through p1 and p3 and p2 controls the curvature and tangent direction of the curve in p1 and p3.
When a control point is moved or you touch it, it says its position through channel #1 to the bezier server.

To use them, just rezz one object of each type (p1,p2 and p3) and move it to the position you want. The curve should be drawn if you have rezzed the bezier server first.

Commands - How to use

The control points don't obey any command. They just have a simple script to say their position to the bezier server trough channel #1

How to Create the Control Points


Create three prims (may be a simple sphere prim) with different colors and name them p1, p2 and p3 (be sure to name it that way: the server uses the name to differentiate which point has been moved or touched).

Then put the following script in each control point.

reportPos()
{
    vector pos = llGetPos();
    llSay(1, (string)pos);
}

default
{
    touch_start(integer total_number)
    {
        if (llDetectedKey(0) == llGetOwner())
        {
            reportPos();
        }
    }
    
    moving_end()
    {
        reportPos();
    }
}


Point Mark

Description


The Point Marks are the prims drawn in each interpolated point of the bezier curve when markers mode is active.
You can use any prim, as a small sphere for example.
The Point Mark object should be in the inventory of the bezier server object, as it is rezzed from the script of that object.
The Point Marks are very simple, as they don't need to communicate with other scripts.
For convenience, they just listen in channel # 1 for clear commands, to delete themselves.

Commands - How to use


The point marks listen in channel #1 for clear commands to delete themselves.
say "/1 clear": deletes all point marks.

How to Create the Point Mark object


Create the prim or object you want to be rezzed in each point position. Name it Point Mark. Then put the following script in it.
Take the object to your inventory and copy it from there to the inventory of the bezier server object.

default
{
    state_entry()
    {
        llListen(1,"","","");
    }

    listen(integer channel, string name, key id, string m)
    {
        if (llGetOwnerKey(id) == llGetOwner())
        {
            if (m == "clear")
            {
                llDie();
            }
        }
    }
}


Segment

Description


The Segments are the prims drawn to make the lines of the bezier curve when you set lines mode on.
The prim should be a cylindre.

The Segment object should be in the inventory of the bezier server object, as it is rezzed from the script of that object.
The bezier server object rezzed the cylindre, rotates it to orient it properly in the direction of the line and then gives it a command to initialize its size correctly (as the size of an object cannot be stablished when rezzed and the parameters of a prim can only be altered by itshelf).

Commands - How to use


Each segment object listens in the channel corresponding to its line number for clear commands to delete themselves.
say "/line# clear": deletes all point marks.
The segment object listen for seginit commands from the bezier server object in its line# channel addressed to its segment number.
The channel number and segment number are coded in an integer that is passed to the segment when rezzed.
In this example the line # is always 1.
The command is:
"/line# segment#,diam,size" where segment# is the number of the segment to be initilized, diam is the diameter fo the cylindre and size is its lenght.

How to Create the Segment Object


Create a cylindre of any size, set a blank texture and choose its color. Save it with name Segment.
Put the following script in it.
Then, take the object to your inventory and copy it from there to the inventory of the bezier server object.


//Author: Lionel Forager (c) 2007
//segmentServer 1.0.1
//This script is designed to work in conjunction with graphics sever 1.0
//It renders the segments in a line, resizing them, changing colors, etc.

//#### internal variables ###

//Line number that the segment belongs to.
//It is used as a line # id and it is the channel # to comunicate with the graphics server
integer lineNum;
integer segmentNum;
list parseCommands(string msg)
{
    msg= llToLower(msg);
    return llParseString2List(msg,[" ",","],[]);
}
default
{
    on_rez(integer param)
    {
        //get the line# and sement# coded in the param
        //line# is in 16 most significant bits
        lineNum= param>>16;
        //segment# is in 16 less significant bits. Clear 16 most significant bits.
        segmentNum= param^(lineNum<<16);

        if( (lineNum == 0)||(segmentNum==0) )
            llWhisper(0,"This object is not designed to be created directly, it works in collaboration with graphics server.");
        else llListen(lineNum,"",NULL_KEY,"");
    }
    
    listen(integer channel, string name, key id, string m)
    {
        //ignore commands not given by the owner.
        if (llGetOwnerKey(id) != llGetOwner()) return;
        list cmds= parseCommands(m);
        m= llList2String(cmds,0);
        if( m == "clear")
        {
            llDie();
        }
        else if (m=="seginit" ) {
            integer seg= llList2Integer(cmds,1);
            //if command is not for current segment, just ignore it.
            if( seg != segmentNum) return;
            float diam= llList2Float(cmds,2);
            float size= llList2Float(cmds,3);
            llSetScale(<diam,diam,size>);
        }
    }
}
There are 2 comments on this page. [Display comments/form]