Spiral Staircase Generator

From Second Life Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
A screenshot of a staircase generated by the Spiral Staircase Generator.

Rezzes a spiral staircase according to configuration parameters.

Generator

Drop this in the generator prim, along with the step, banister, and baluster.

////////////////////////////////
// Spiral Staircase Generator //
// By Meyermagic Salome       //
////////////////////////////////
// Staircase Configuration //
float wh = 2.5;	//Height of one wave (in meters)
float wc = 4.0;	//Full waves in staircase

float r_max = 10.0;	//Radius maximum, (0.1 -> 10.0)
float r_min = 7.5;	//Radius minimum, (0.1 -> 10.0)

integer landing = 5;	//Number of non-rising stairs at top of staircase (not included in wc)

//Step Configuration
float s_h = 0.01;	//Step height, (0.01 -> 0.99)
float s_s = 0.2;	//Vertical stair spacing, (0.01 -> 10.00)

float l_max = 1.0;	//Maximum stair length, (0.01 -> 10.00)
float l_min = 0.5;	//Minimum stair length, (0.01 -> 10.00)

//Balustrade Configuration
float bal_d = 0.05;	//Baluster diameter, (0.001 -> 1.000)
float bal_h = 0.5;	//Height of baluster / height of banister, (0.01 -> 1.00)

float ban_d = 0.05;	//Banister diameter, (0.001 -> 1.000)
float ban_h = 2.0;	//Banister height, (0.01 -> 10.00)

// Functions //
string str_repeat(string src, integer count)
{
    if(count > 0)
    {
        string output;
        integer i;
        for(i = 0; i < count; i++)
        {
            output += src;
        }
        return output;
    }
    else
    {
        return "";
    }
}

string pad_left(string str, string pad, integer len)//Left-pads string str to length len with character pas
{
    return str_repeat(pad, len - llStringLength(str)) + str;
}
list segment(vector a, vector b)//Returns the parameters for a cylinder connecting points a and b
{//Based on http://lslwiki.net/lslwiki/wakka.php?wakka=LibraryBezierCurveDemo
    vector mid = (a + b) / 2.0;
    vector localZ = b - a;

    float len = llVecMag(localZ);

    localZ = localZ / len;

    vector xAxis;

    if(localZ.x < localZ.y)
    {
        xAxis = <1.0, 0.0, 0.0>;
    }
    else
    {
        xAxis= <0.0, 1.0, 0.0>;
    }

    vector localX = xAxis - (localZ * xAxis) * localZ;
    localX = llVecNorm(localX);

    rotation rot = llAxes2Rot(localX, localZ % localX, localZ);

    return [len, mid, rot];
}

//Staircase-specific Functions
float wave(integer i)//Return i as portion of full wave (2PI)
{
    return PI * (float)i * s_s / wh;
}
float radius(integer i)//Return the stair radius at step i
{
    return llFabs(((llCos(wave(i)) + 1.0) * ((r_max - r_min) / 2)) + r_min);
}
float len(integer i)//Returns the length of stair at step i
{
    return llFabs(((llCos(wave(i)) + 1.0) * ((l_max - l_min) / 2)) + l_min);
}
float rot(integer i)//Returns the rotation of stair at step i
{
    return llAcos(1 - llPow(len(i), 2.0) / (2 * llPow(radius(i), 2)));
}

integer s_encode(integer i)
{
    return (integer)("1" + pad_left((string)llRound(s_h * 100.0), "0", 2) + pad_left((string)llRound(radius(i) * 10.0), "0", 3) + pad_left((string)llRound(len(i) * 100.0), "0", 4));
}
integer p_encode(float h)
{
    return (integer)("1" + pad_left((string)llRound(bal_d * 1000.0), "0", 4) + pad_left((string)llRound(h * 1000.0), "0", 5));
}
integer r_encode(float l)
{
    return (integer)("1" + pad_left((string)llRound(ban_d * 1000.0), "0", 4) + pad_left((string)llRound(l * 1000.0), "0", 5));
}

default
{
    touch_start(integer num_detected)
    {
        if(llDetectedKey(0) == llGetOwner())
        {
            integer i = 0;
            float t = 0.0;

            //Rez main rising steps
            for(i = 0; i < (integer)(wc * wh / s_s); ++i)
            {
                llSetPos(llGetPos() + <0, 0, s_s>);//I'll have it do this every ~10m when I get around to it

                float c_rot = rot(i);
                float c_radius = radius(i);
                float n_radius = radius(i + 1);
                vector m_pos = llGetPos();

                //Rez step
                llRezObject("Step",
                    m_pos + <llCos(t + c_rot / 2.0) * c_radius / 2.0, llSin(t + c_rot / 2.0) * c_radius / 2.0, 0>,
                    ZERO_VECTOR,
                    llEuler2Rot(<PI_BY_TWO, t + c_rot / 2.0 + 4.7123889, 0>),
                    s_encode(i));

                //Get balustrade data
                list b_data = segment(m_pos + <llCos(t) * c_radius, llSin(t) * c_radius, ban_h + (s_h / 2.0)>,
                    m_pos + <llCos(t + c_rot) * n_radius, llSin(t + c_rot) * n_radius, ban_h + (s_h / 2.0) + s_s>);
                if(i == (integer)(wc * wh / s_s) - 1)
                {
                    b_data = segment(m_pos + <llCos(t) * c_radius, llSin(t) * c_radius, ban_h + (s_h / 2.0)>,
                        m_pos + <llCos(t + c_rot) * n_radius, llSin(t + c_rot) * n_radius, ban_h + (s_h / 2.0)>);
                }
                vector b_pos = llList2Vector(b_data, 1);

                //Rez banister
                llRezObject("Banister",
                    b_pos,
                    ZERO_VECTOR,
                    llList2Rot(b_data, 2),
                    r_encode(llList2Float(b_data, 0)));

                //Rez baluster
                llRezObject("Baluster",
                    <b_pos.x, b_pos.y, m_pos.z + ((b_pos.z - m_pos.z) / 2.0) + (s_h / 2.0)>,
                    ZERO_VECTOR,
                    ZERO_ROTATION,
                    p_encode((b_pos.z - m_pos.z) * bal_h));

                //Increment rotation
                t += rot(i);
            }

            //Rez non-rising steps
            for(i = (integer)(wc * wh / s_s); i < (integer)(wc * wh / s_s) + landing; ++i)
            {
                float c_rot = rot(i);
                float c_radius = radius(i);
                float n_radius = radius(i + 1);
                vector m_pos = llGetPos();

                //Rez step
                llRezObject("Step",
                    m_pos + <llCos(t + c_rot / 2.0) * c_radius / 2.0, llSin(t + c_rot / 2.0) * c_radius / 2.0, 0>,
                    ZERO_VECTOR,
                    llEuler2Rot(<PI_BY_TWO, t + c_rot / 2.0 + 4.7123889, 0>),
                    s_encode(i));

                //Get balustrade data
                list b_data = segment(m_pos + <llCos(t) * c_radius, llSin(t) * c_radius, ban_h + (s_h / 2.0)>,
                    m_pos + <llCos(t + c_rot) * n_radius, llSin(t + c_rot) * n_radius, ban_h + (s_h / 2.0)>);
                vector b_pos = llList2Vector(b_data, 1);

                //Rez banister
                llRezObject("Banister",
                    b_pos,
                    ZERO_VECTOR,
                    llList2Rot(b_data, 2),
                    r_encode(llList2Float(b_data, 0)));

                //Rez baluster
                llRezObject("Baluster",
                    <b_pos.x, b_pos.y, m_pos.z + ((b_pos.z - m_pos.z) / 2.0) + (s_h / 2.0)>,
                    ZERO_VECTOR,
                    ZERO_ROTATION,
                    p_encode((b_pos.z - m_pos.z) * bal_h));

                //Increment rotation
                t += rot(i);
            }
        }
    }
}

Conf_Step

Drop this in a prim named "Step". (Box with x-taper 1.0 is recommended).

default
{
    on_rez(integer s)
    {
        if(s != 0)
        {
            string d = (string)s;
            float y = ((float)llGetSubString(d, 1, 2)) / 100.0;//Height
            float z = ((float)llGetSubString(d, 3, 5)) / 10.0;//Radius
            float x = ((float)llGetSubString(d, 6, 9)) / 100.0;//Length
            llSetScale(<x, y, z>);
            llRemoveInventory(llGetScriptName());
        }
    }
}

Conf_Banister

Drop this in a cylinder named "Banister".

default
{
    on_rez(integer s)
    {
        if(s != 0)
        {
            string d = (string)s;
            float z = ((float)llGetSubString(d, 5, 9)) / 1000.0;//Length
            float y = ((float)llGetSubString(d, 1, 4)) / 1000.0;//Diameter
            float x = y;
            llSetScale(<x, y, z>);
            llRemoveInventory(llGetScriptName());
        }
    }
}

Conf_Baluster

Drop this in a cylinder named "Baluster".

default
{
    on_rez(integer s)
    {
        if(s != 0)
        {
            string d = (string)s;
            float z = ((float)llGetSubString(d, 5, 9)) / 1000.0;//Height
            float y = ((float)llGetSubString(d, 1, 4)) / 1000.0;//Diameter
            float x = y;
            llSetScale(<x, y, z>);
            llRemoveInventory(llGetScriptName());
        }
    }
}