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

LSL Wiki : XyText

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are ec2-54-87-93-157.compute-1.amazonaws.com

XyText v1.0.2 -- llSetText Alternative

XyText is a really cool script / set of scripts written by XylorBaysklef that displays 6 characters on a single prim using textures with 2-character pairs on 3 faces.

These scripts were originally posted in the official Second Life forums here. Xylor kindly granted permission to repost them here.

Yah know what would be cool? A XyText version history. I want to know what changed between versions 1.0.2 and 0.9.7, just to see if I need to update my screens or not. -Chris, the Obsessive Compulsive Updater

i have an old version of 0.9.7. The major change was that a format change on the handling of SET_CELL_INFO. but i haven't looked at the old version carefully to look at what else had changed. BW

Added the 1.2 version of XYScripts off on a second page. Major improvements: The basic primitive is now a truncated hollowed prism which allows 5 characters per prim using only one texture, and 10 characters using the two-character texture set. Fade in/out, color settings and resizing have been added to the control linkmessages. I found this new version at Odysseus Fairymeadow's store. He has an information kiosk that hands out the latest XyText scripts on a notecard. SM


Quick Instructions


  1. Setup the prim by placing the Prim Setup script into it - it will modify the prim and then delete itself from it.
  2. Place the XyText v1.0.2 script into the prim.
  3. Create a new script and add the Message Map lines to your script.
  4. Send a link message to the prim with DISPLAY_STRING in the integer and the text in the string parameters, example:
    llMessageLinked(LINK_SET, DISPLAY_STRING, "Xylor", "");
  5. If you want more than 6 characters, read the detailed instructions below (SET_CELL_INFO).




General Info


This script displays 6 characters on a prim. It can show any typeable character in SL, plus 15 extended characters. Prims can be chained together, and act as 'cells', allowing much larger display systems (as in the freely available XyText Notecard Reader).

The display works by displaying characters in pairs on 3 viewable texture faces. Use the following script to 'set up' your prim to be the correct shape:

////////////////////////////////////////////
// XyText Prim Setup
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// Transparent texture key.
string  TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";

default {
    state_entry() {
        // Set up the prim to be the correct shape.
        vector Scale = llGetScale();
        llSetPrimitiveParams([
            // Set the top size so this shows 3 faces at once.
            PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <0.333333, 1, 0>, ZERO_VECTOR,

            // Display the string "XyText" for now.
            PRIM_TEXTURE, 4, "0e47c89e-de4a-6233-a2da-cb852aad1b00",
                    <0.1, 0.1, 0>,  <0.200000, -0.450000, 0.000000>, PI_BY_TWO,

            PRIM_TEXTURE, 0, "20b24b3a-8c57-82ee-c6ed-555003f5dbcd",
                    <0.1, 0.1, 0>, <-0.200000, -0.450000, 0.000000>, 0.0,

            PRIM_TEXTURE, 2, "2f713216-4e71-d123-03ed-9c8554710c6b",
                    <0.1, 0.1, 0>, <-0.050000, -0.350000, 0.000000>, -PI_BY_TWO,

            // Show transparent textures for the other sides.
            PRIM_TEXTURE, 1, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,
            PRIM_TEXTURE, 3, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,
            PRIM_TEXTURE, 5, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,

            // Set the correct aspect ratio.
            PRIM_SIZE, <Scale.x, Scale.x / 3, 0.01>
            ]);

        // Remove ourselves from inventory.
        llRemoveInventory(llGetScriptName());
    }
}


Also, here is a chart of all displayable characters in the system. Drag this script onto a prim:

////////////////////////////////////////////
// XyText Chart Creator
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// Texture keys.
string     CHART = "f2959ca0-6d05-e1df-7ced-aadc9e5e4022";
string     LOGO  = "f74e6424-229f-3223-9077-fccf1a0c3087";
string     BLANK = "5748decc-f629-461c-9a36-a35a221fe21f";

default {
    state_entry() {
        // Set up the prim to be the correct shape, and textures.
        llSetPrimitiveParams([
                PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <1, 1, 0>, ZERO_VECTOR,
                PRIM_SIZE, <5.0, 0.01, 2.5>,
                PRIM_COLOR,   0, <0.30588, 0.50980, 0.94902>, 1.0,
                PRIM_COLOR,   1, <1.00000, 1.00000, 1.00000>, 1.0,
                PRIM_COLOR,   2, <0.30588, 0.50980, 0.94902>, 1.0,
                PRIM_COLOR,   3, <1.00000, 1.00000, 1.00000>, 1.0,
                PRIM_COLOR,   4, <0.30588, 0.50980, 0.94902>, 1.0,
                PRIM_COLOR,   5, <0.30588, 0.50980, 0.94902>, 1.0,
                PRIM_TEXTURE, 0, BLANK, <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 1, CHART, <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 2, BLANK, <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 3, LOGO,  <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 4, BLANK, <1, 1, 0>, ZERO_VECTOR, 0.0,
                PRIM_TEXTURE, 5, BLANK, <1, 1, 0>, ZERO_VECTOR, 0.0
            ]);

        llSetObjectName("XyText Chart");

        // Remove ourselves from inventory.
        llRemoveInventory(llGetScriptName());
    }
}


Here is the main script, which goes on each prim you wish to use as a display.

////////////////////////////////////////////
// XyText v1.0.2 Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;

// This is an extended character escape sequence.
string  ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string  EXTENDED_INDEX  = "123456789abcdef";

// Face numbers.
integer LEFT_FACE       = 4;
integer MIDDLE_FACE     = 0;
integer RIGHT_FACE      = 2;

// This is a list of textures for all 2-character combinations.
list    CHARACTER_GRID  = [
        "00e9f9f7-0669-181c-c192-7f8e67678c8d",
        "347a5cb6-0031-7ec0-2fcf-f298eebf3c0e",
        "4e7e689e-37f1-9eca-8596-a958bbd23963",
        "19ea9c21-67ba-8f6f-99db-573b1b877eb1",
        "dde7b412-cda1-652f-6fc2-73f4641f96e1",
        "af6fa3bb-3a6c-9c4f-4bf5-d1c126c830da",
        "a201d3a2-364b-43b6-8686-5881c0f82a94",
        "b674dec8-fead-99e5-c28d-2db8e4c51540",
        "366e05f3-be6b-e5cf-c33b-731dff649caa",
        "75c4925c-0427-dc0c-c71c-e28674ff4d27",
        "dcbe166b-6a97-efb2-fc8e-e5bc6a8b1be6",
        "0dca2feb-fc66-a762-db85-89026a4ecd68",
        "a0fca76f-503a-946b-9336-0a918e886f7a",
        "67fb375d-89a1-5a4f-8c7a-0cd1c066ffc4",
        "300470b2-da34-5470-074c-1b8464ca050c",
        "d1f8e91c-ce2b-d85e-2120-930d3b630946",
        "2a190e44-7b29-dadb-0bff-c31adaf5a170",
        "75d55e71-f6f8-9835-e746-a45f189f30a1",
        "300fac33-2b30-3da3-26bc-e2d70428ec19",
        "0747c776-011a-53ce-13ee-8b5bb9e87c1e",
        "85a855c3-a94f-01ca-33e0-7dde92e727e2",
        "cbc1dab2-2d61-2986-1949-7a5235c954e1",
        "f7aef047-f266-9596-16df-641010edd8e1",
        "4c34ebf7-e5e1-2e1a-579f-e224d9d5e71b",
        "4a69e98c-26a5-ad05-e92e-b5b906ad9ef9",
        "462a9226-2a97-91ac-2d89-57ab33334b78",
        "20b24b3a-8c57-82ee-c6ed-555003f5dbcd",
        "9b481daa-9ea8-a9fa-1ee4-ab9a0d38e217",
        "c231dbdc-c842-15b0-7aa6-6da14745cfdc",
        "c97e3cbb-c9a3-45df-a0ae-955c1f4bf9cf",
        "f1e7d030-ff80-a242-cb69-f6951d4eae3b",
        "ed32d6c4-d733-c0f1-f242-6df1d222220d",
        "88f96a30-dccf-9b20-31ef-da0dfeb23c72",
        "252f2595-58b8-4bcc-6515-fa274d0cfb65",
        "f2838c4f-de80-cced-dff8-195dfdf36b2c",
        "cc2594fe-add2-a3df-cdb3-a61711badf53",
        "e0ce2972-da00-955c-129e-3289b3676776",
        "3e0d336d-321f-ddfa-5c1b-e26131766f6a",
        "d43b1dc4-6b51-76a7-8b90-38865b82bf06",
        "06d16cbb-1868-fd1d-5c93-eae42164a37d",
        "dd5d98cf-273e-3fd0-f030-48be58ee3a0b",
        "0e47c89e-de4a-6233-a2da-cb852aad1b00",
        "fb9c4a55-0e13-495b-25c4-f0b459dc06de",
        "e3ce8def-312c-735b-0e48-018b6799c883",
        "2f713216-4e71-d123-03ed-9c8554710c6b",
        "4a417d8a-1f4f-404b-9783-6672f8527911",
        "ca5e21ec-5b20-5909-4c31-3f90d7316b33",
        "06a4fcc3-e1c4-296d-8817-01f88fbd7367",
        "130ac084-6f3c-95de-b5b6-d25c80703474",
        "59d540a0-ae9d-3606-5ae0-4f2842b64cfa",
        "8612ae9a-f53c-5bf4-2899-8174d7abc4fd",
        "12467401-e979-2c49-34e0-6ac761542797",
        "d53c3eaa-0404-3860-0675-3e375596c3e3",
        "9f5b26bd-81d3-b25e-62fe-5b671d1e3e79",
        "f57f0b64-a050-d617-ee00-c8e9e3adc9cb",
        "beff166a-f5f3-f05e-e020-98f2b00e27ed",
        "02278a65-94ba-6d5e-0d2b-93f2e4f4bf70",
        "a707197d-449e-5b58-846c-0c850c61f9d6",
        "021d4b1a-9503-a44f-ee2b-976eb5d80e68",
        "0ae2ffae-7265-524d-cb76-c2b691992706",
        "f6e41cf2-1104-bd0b-0190-dffad1bac813",
        "2b4bb15e-956d-56ae-69f5-d26a20de0ce7",
        "f816da2c-51f1-612a-2029-a542db7db882",
        "345fea05-c7be-465c-409f-9dcb3bd2aa07",
        "b3017e02-c063-5185-acd5-1ef5f9d79b89",
        "4dcff365-1971-3c2b-d73c-77e1dc54242a"
          ];

///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// All displayable characters.  Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel      = -1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition = 0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
    gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
    // \" <-- Fixes LSL syntax highlighting bug.
    gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
    gCharIndex += "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
}

vector GetGridPos(integer index1, integer index2) {
    // There are two ways to use the lookup table...
    integer Col;
    integer Row;
    if (index1 >= index2) {
        // In this case, the row is the index of the first character:
        Row = index1;
        // And the col is the index of the second character (x2)
        Col = index2 * 2;
    }
    else { // Index1 < Index2
        // In this case, the row is the index of the second character:
        Row = index2;
        // And the col is the index of the first character, x2, offset by 1.
        Col = index1 * 2 + 1;
    }
    return < Col, Row, 0>;
}

string GetGridTexture(vector grid_pos) {
    // Calculate the texture in the grid to use.
    integer GridCol = llRound(grid_pos.x) / 20;
    integer GridRow = llRound(grid_pos.y) / 10;

    // Lookup the texture.
    key Texture = llList2Key(CHARACTER_GRID, GridRow * (GridRow + 1) / 2 + GridCol);
    return Texture;
}

vector GetGridOffset(vector grid_pos) {
    // Zoom in on the texture showing our character pair.
    integer Col = llRound(grid_pos.x) % 20;
    integer Row = llRound(grid_pos.y) % 10;

    // Return the offset in the texture.
    return <-0.45 + 0.05 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3) {
    // Set the primitive textures directly.
    llSetPrimitiveParams( [
        PRIM_TEXTURE, LEFT_FACE,   GetGridTexture(grid_pos1), <0.1, 0.1, 0>, GetGridOffset(grid_pos1), PI_BY_TWO,
        PRIM_TEXTURE, MIDDLE_FACE, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
        PRIM_TEXTURE, RIGHT_FACE,  GetGridTexture(grid_pos3), <0.1, 0.1, 0>, GetGridOffset(grid_pos3), -PI_BY_TWO
        ]);
}

RenderString(string str) {
    // Get the grid positions for each pair of characters.
    vector GridPos1 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
    vector GridPos2 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
    vector GridPos3 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)),
                                  llSubStringIndex(gCharIndex, llGetSubString(str, 5, 5)) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3);
}

RenderExtended(string str) {
    // Look for escape sequences.
    list Parsed       = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
    integer ParsedLen = llGetListLength(Parsed);

    // Create a list of index values to work with.
    list Indices;
    // We start with room for 6 indices.
    integer IndicesLeft = 6;

    integer i;
    string Token;
    integer Clipped;
    integer LastWasEscapeSequence = FALSE;
    // Work from left to right.
    for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
        Token = llList2String(Parsed, i);

        // If this is an escape sequence, just set the flag and move on.
        if (Token == ESCAPE_SEQUENCE) {
            LastWasEscapeSequence = TRUE;
        }
        else { // Token != ESCAPE_SEQUENCE
            // Otherwise this is a normal token.  Check its length.
            Clipped = FALSE;
            integer TokenLength = llStringLength(Token);
            // Clip if necessary.
            if (TokenLength > IndicesLeft) {
                Token = llGetSubString(Token, 0, IndicesLeft - 1);
                TokenLength = llStringLength(Token);
                IndicesLeft = 0;
                Clipped = TRUE;
            }
            else
                IndicesLeft -= TokenLength;

            // Was the previous token an escape sequence?
            if (LastWasEscapeSequence) {
                // Yes, the first character is an escape character, the rest are normal.

                // This is the extended character.
                Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

                // These are the normal characters.
                integer j;
                for (j = 1; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }
            else { // Normal string.
                // Just add the characters normally.
                integer j;
                for (j = 0; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }

            // Unset this flag, since this was not an escape sequence.
            LastWasEscapeSequence = FALSE;
        }
    }

    // Use the indices to create grid positions.
    vector GridPos1 = GetGridPos( llList2Integer(Indices, 0), llList2Integer(Indices, 1) );
    vector GridPos2 = GetGridPos( llList2Integer(Indices, 2), llList2Integer(Indices, 3) );
    vector GridPos3 = GetGridPos( llList2Integer(Indices, 4), llList2Integer(Indices, 5) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3);
}

integer ConvertIndex(integer index) {
    // This converts from an ASCII based index to our indexing scheme.
    if (index >= 32) // ' ' or higher
        index -= 32;
    else { // index < 32
        // Quick bounds check.
        if (index > 15)
            index = 15;

        index += 94; // extended characters
    }

    return index;
}

default {
    state_entry() {
        // Initialize the character index.
        ResetCharIndex();

        //llSay(0, "Free Memory: " + (string) llGetFreeMemory());
    }

    link_message(integer sender, integer channel, string data, key id) {
        if (channel == DISPLAY_STRING) {
            RenderString(data);
            return;
        }
        if (channel == DISPLAY_EXTENDED) {
            RenderExtended(data);
            return;
        }
        if (channel == gCellChannel) {
            // Extract the characters we are interested in, and use those to render.
            RenderString( llGetSubString(data, gCellCharPosition, gCellCharPosition + 5) );
            return;
        }
        if (channel == REMAP_INDICES) {
            // Parse the message, splitting it up into index values.
            list Parsed = llCSV2List(data);
            integer i;
            // Go through the list and swap each pair of indices.
            for (i = 0; i < llGetListLength(Parsed); i += 2) {
                integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
                integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );

                // Swap these index values.
                string Value1 = llGetSubString(gCharIndex, Index1, Index1);
                string Value2 = llGetSubString(gCharIndex, Index2, Index2);

                gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
                gCharIndex = llInsertString(gCharIndex, Index1, Value2);

                gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
                gCharIndex = llInsertString(gCharIndex, Index2, Value1);
            }
            return;
        }
        if (channel == RESET_INDICES) {
            // Restore the character index back to default settings.
            ResetCharIndex();
            return;
        }
        if (channel == SET_CELL_INFO) {
            // Change the channel we listen to for cell commands, and the
            // starting character position to extract from.
            list Parsed = llCSV2List(data);
            gCellChannel        = (integer) llList2String(Parsed, 0);;
            gCellCharPosition   = (integer) llList2String(Parsed, 1);
            return;
        }
    }
}



Basic Usage


The XyText script listens for link messages on various channels (Note, these are *link* messages, not listen events and the channels have nothing to do with chat). Here is the message map, which you can cut and paste into scripts which use the system:

// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;



If you plan to use XyText in a simple situation of say, 1 prim displaying 6 characters, then using DISPLAY_STRING would be the easiest way to accomplish your task. Here is an example use:

llMessageLinked(LINK_SET, DISPLAY_STRING, "Hello", "");




Here is the obligatory "Hello World!" script =P

////////////////////////////////////////////
// Hello World! Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
///////////// END CONSTANTS ////////////////

default {
    state_entry() {
        llMessageLinked(LINK_SET, DISPLAY_STRING, "Hello ", "");
        llSleep(10.0);
        llMessageLinked(LINK_SET, DISPLAY_STRING, "World!", "");
    }
}



Advanced Usage


Here is a breakdown of what each channel in the message map does:


DISPLAY_STRING


This channel takes a string to render as its parameter. Only the first 6 characters of this string are rendered.

llMessageLinked(LINK_SET, DISPLAY_STRING, "Xylor", "");


This would render the string "Xylor" on all XyText prims linked to this script.

Example:


integer i;
// Loop through and display the link number on the first 10 links of this object.
for (i = 1; i <= 10; i++)
llMessageLinked(i, DISPLAY_STRING, (string) i, "");



This would display the link number of each prim with the XyText script attached to it (up to link 10; change as needed). This is useful for a quick check of how the object was linked.

Note the changing link numbers in the llMessageLinked calls, instead of LINK_SET. Using DISPLAY_STRING and/or DISPLAY_EXTENDED messages with different link numbers is one way to 'chain' together different XyText displays, however there is a much more efficient way to do this, which will be discussed later (See: SET_CELL_INFO).


DISPLAY_EXTENDED


This channel also takes a string to render as its parameter. However, this string is parsed to search for extended character sequences. Each character sequence starts with a "\e", and is followed by a one character extended code.

Extended character codes are shown in the top row of the XyText Chart (see XyText Chart Creator script, above). Extended codes are shown in yellow, below the index value.

Here is a list of the current extended character codes and the characters they display, if you would rather not create a chart:

code character
1 pi
2 theta
3 x (times symbol)
4 o (degrees)
5 square root symbol
6 ^2 (squared)
7 ^3 (cubed)
8 1/4
9 1/2
a 3/4
b section symbol
c copyright symbol
d trademark symbol
e bullet
f ellipses (...)

Example:

llMessageLinked(llGetLinkNumber(), DISPLAY_EXTENDED, "A\\e6\\e3B\\e7", "");


Note the use of double backslashes. This would render "A^2xB^3" (A squared times B cubed, using appropriate symbols) on the prim this script is attached to.


REMAP_INDICES


This allows you to remap the index values of any character. Using this feature, you could swap a commonly used extended character with a normal character, letting you use the faster DISPLAY_STRING channel instead of DISPLAY_EXTENDED, and avoid the use of escape sequences entirely. This is also the only way to display extended characters using the cell channel set up by SET_CELL_INFO, which will be discussed soon. This channel takes a CSV string (comma separated values), and swaps indices in pairs.

In order to use this, you will need to know the indexing scheme used by XyText. The index values are initially in ASCII order for non-extended characters. Extended characters start at index 1. Please consult the XyText Chart (see the XyText Chart Creator script, above) where index values for each character are shown in white. Here is an outline of the index map for reference:

index character comment
0 reserved
1 pi extended values start here
2 theta
.
.
.
14 bullet
15 ellipses
16 reserved
17 reserved
.
.
.
30 reserved
31 reserved
32 SPACE normal ASCII values start here
33 !
34 "
35 #
.
.
.
125 }
126 ~


RESET_INDICES


This reverts all character indices back to default settings (those listed in the above table).

Example:


llMessageLinked(LINK_SET, REMAP_INDICES, "14,126", "");
llMessageLinked(LINK_SET, DISPLAY_STRING, "A~B~C", "");



The first line would swap the tilde character (~) with the Bullet character. The second line would render "A(bullet)B(bullet)C", since the tilde character now represents a Bullet.

Example:


llMessageLinked(LINK_SET, RESET_INDICES, "", "");
llMessageLinked(LINK_SET, REMAP_INDICES, "32,15,6,94", "");



The first line resets all index values back to 'default' settings. The second line swaps the SPACE character with the Ellipse character, and the 'squared' character with the ^ character, both in one call. Using two calls like this at script initialization is a good idea, since you don't know if the XyText script has indices set to a non-default setting from a previous run.


SET_CELL_INFO


This allows XyText scripts to act as 'cells' in a larger display. This message lets you set the channel that a particular XyText cell listens to for render messages, as well as which position in the message to start rendering on. This channel takes a CSV string (comma separated values), the first value being the channel to listen on, the second being the position in the string to start rendering on.

This feature is of high importance in creating large displays; be sure you understand its use.

Example:


// Channel to set the line with
integer SET_LINE_CHANNEL = 100100;

...

integer ThisLink = llGetLinkNumber();
// Set up each prim to listen on the same channel, but to render only a part of the string.
llMessageLinked(ThisLink , SET_CELL_INFO, llList2CSV([SET_LINE_CHANNEL, 0]), "");
llMessageLinked(ThisLink + 1, SET_CELL_INFO, llList2CSV([SET_LINE_CHANNEL, 6]), "");
llMessageLinked(ThisLink + 2, SET_CELL_INFO, llList2CSV([SET_LINE_CHANNEL, 12]), "");

...

llMessageLinked(LINK_SET, SET_LINE_CHANNEL, "Long line of text.", "");



This would set up 3 prims with the XyText script attached to act as a unit. Each 'cell' in this unit would listen to the same channel (SET_LINE_CHANNEL in this example). However they would render different portions of the string sent. The last line utilizes these cells, and sends a string to the channel set up earlier. In this case, the string would be split up into "Long l", "ine of" and " text.". Note that the link numbers of each XyText cell is assumed in this case, and the order in which the prims of the object were linked would matter. There are various ways to obtain link numbers for this purpose, but that is left for the user to provide.


SET_FONT_TEXTURE

Single character "XyText" version 1.1 or later only.

This changes the texture that the single character "XyText" uses for the font. There is no similar call for the double character version. In order to work, a font must be layed out appropriately for "XyText" to use. For more information on setting up a font, see LibraryXyTextFonts.


Font Information


The font used in the current system is ProFontWindows, which is the font included in SL for the script editing window. Any font used by the system in future versions would also be fixed-width.

Adding another font would take a couple hours (and cost at least $660 to upload, assuming no more extended characters enter the system) but if enough people want more fonts (and provide me with upload costs ^_~) I might be convinced to work on them. I have my eye on Courier, and Lucida Console, actually...

There seems to be enough memory left in the XyText script for one more row of extended characters (this works out to 10 more characters, indices 16-25). If anyone has suggestions as to which extended characters they would like added, please let me know.


I hope some of you find this system useful. If there is any information on its use that you could not figure out from the above info, please let me know so I can add it here. Thanks =)

Xylor




XyText Single Texture



This is a version which displays only one character per face, which means 3 per prim. The advantage of this is that it's using only one texture instead of 66 and thus doesn't require a huge amount of textures to be streamed to the client before all characters are readable.


////////////////////////////////////////////
// XyText v1.1 Script (Single Texture)
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;
integer SET_FONT_TEXTURE    = 204005;
integer SET_THICKNESS       = 204006;

// This is an extended character escape sequence.
string  ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string  EXTENDED_INDEX  = "12345";

// Face numbers.
integer LEFT_FACE       = 4;
integer MIDDLE_FACE     = 0;
integer RIGHT_FACE      = 2;

// Used to hide the text after a fade-out.
key     TRANSPARENT     = "701917a8-d614-471f-13dd-5f4644e36e3c";
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// This is the key of the font we are displaying.
key     gFontTexture        = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
// All displayable characters.  Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel        = -1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition   = 0;
// This is whether or not to use the fade in/out special effect.
integer gCellUseFading      = FALSE;
// This is how long to display the text before fading out (if using
// fading special effect).
// Note: < 0  means don't fade out.
float   gCellHoldDelay      = 1.0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
    gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
    // \" <-- Fixes LSL syntax highlighting bug.
    gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
    gCharIndex += "\n\n\n\n\n";
}

vector GetGridOffset(integer index) {
    // Calculate the offset needed to display this character.
    integer Row = index / 10;
    integer Col = index % 10;

    // Return the offset in the texture.
    return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_offset1, vector grid_offset2, vector grid_offset3) {
    // Set the primitive textures directly.
    llSetPrimitiveParams( [
        PRIM_TEXTURE, LEFT_FACE,   (string) gFontTexture, <0.05, 0.1, 0>, grid_offset1, PI_BY_TWO,
        PRIM_TEXTURE, MIDDLE_FACE, (string) gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
        PRIM_TEXTURE, RIGHT_FACE,  (string) gFontTexture, <0.05, 0.1, 0>, grid_offset3, -PI_BY_TWO
        ]);
}

RenderString(string str) {
    // Get the grid positions for each pair of characters.
    vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
    vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
    vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridOffset1, GridOffset2, GridOffset3);
}

RenderWithEffects(string str) {
    // Get the grid positions for each pair of characters.
    vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
    vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
    vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );

    // First set the alpha to the lowest possible.
    llSetAlpha(0.05, ALL_SIDES);

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridOffset1, GridOffset2, GridOffset3);   
    
     // Now turn up the alpha until it is at full strength.
     float Alpha;
     for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
        llSetAlpha(Alpha, ALL_SIDES);
        
    // See if we want to fade out as well.
    if (gCellHoldDelay < 0.0)
        // No, bail out. (Just keep showing the string at full strength).
        return;
        
    // Hold the text for a while.
    llSleep(gCellHoldDelay);
    
    // Now fade out.
    for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
        llSetAlpha(Alpha, ALL_SIDES);
        
    // Make the text transparent to fully hide it.
    llSetTexture(TRANSPARENT, ALL_SIDES);
}

RenderExtended(string str) {
    // Look for escape sequences.
    list Parsed       = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
    integer ParsedLen = llGetListLength(Parsed);

    // Create a list of index values to work with.
    list Indices;
    // We start with room for 3 indices.
    integer IndicesLeft = 3;

    integer i;
    string Token;
    integer Clipped;
    integer LastWasEscapeSequence = FALSE;
    // Work from left to right.
    for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
        Token = llList2String(Parsed, i);

        // If this is an escape sequence, just set the flag and move on.
        if (Token == ESCAPE_SEQUENCE) {
            LastWasEscapeSequence = TRUE;
        }
        else { // Token != ESCAPE_SEQUENCE
            // Otherwise this is a normal token.  Check its length.
            Clipped = FALSE;
            integer TokenLength = llStringLength(Token);
            // Clip if necessary.
            if (TokenLength > IndicesLeft) {
                Token = llGetSubString(Token, 0, IndicesLeft - 1);
                TokenLength = llStringLength(Token);
                IndicesLeft = 0;
                Clipped = TRUE;
            }
            else
                IndicesLeft -= TokenLength;

            // Was the previous token an escape sequence?
            if (LastWasEscapeSequence) {
                // Yes, the first character is an escape character, the rest are normal.

                // This is the extended character.
                Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

                // These are the normal characters.
                integer j;
                for (j = 1; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }
            else { // Normal string.
                // Just add the characters normally.
                integer j;
                for (j = 0; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }

            // Unset this flag, since this was not an escape sequence.
            LastWasEscapeSequence = FALSE;
        }
    }

    // Use the indices to create grid positions.
    vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0));
    vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
    vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridOffset1, GridOffset2, GridOffset3);
}

integer ConvertIndex(integer index) {
    // This converts from an ASCII based index to our indexing scheme.
    if (index >= 32) // ' ' or higher
        index -= 32;
    else { // index < 32
        // Quick bounds check.
        if (index > 15)
            index = 15;

        index += 94; // extended characters
    }

    return index;
}

default {
    state_entry() {
        // Initialize the character index.
        ResetCharIndex();
    }

    link_message(integer sender, integer channel, string data, key id) {
        if (channel == DISPLAY_STRING) {
            RenderString(data);
            return;
        }
        if (channel == DISPLAY_EXTENDED) {
            RenderExtended(data);
            return;
        }
        if (channel == gCellChannel) {
            // Extract the characters we are interested in, and use those to render.
            string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 2);
            
            // See if we need to show special effects.
            if (gCellUseFading)
                RenderWithEffects( TextToRender );
            else // !gCellUseFading
                RenderString( TextToRender );
            return;
        }
        if (channel == REMAP_INDICES) {
            // Parse the message, splitting it up into index values.
            list Parsed = llCSV2List(data);
            integer i;
            // Go through the list and swap each pair of indices.
            for (i = 0; i < llGetListLength(Parsed); i += 2) {
                integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
                integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );

                // Swap these index values.
                string Value1 = llGetSubString(gCharIndex, Index1, Index1);
                string Value2 = llGetSubString(gCharIndex, Index2, Index2);

                gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
                gCharIndex = llInsertString(gCharIndex, Index1, Value2);

                gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
                gCharIndex = llInsertString(gCharIndex, Index2, Value1);
            }
            return;
        }
        if (channel == RESET_INDICES) {
            // Restore the character index back to default settings.
            ResetCharIndex();
            return;
        }
        if (channel == SET_CELL_INFO) {
            // Change the channel we listen to for cell commands, the
            // starting character position to extract from, and
            // special effect attributes.
            list Parsed = llCSV2List(data);
            gCellChannel        = (integer) llList2String(Parsed, 0);
            gCellCharPosition   = (integer) llList2String(Parsed, 1);
            gCellUseFading      = (integer) llList2String(Parsed, 2);
            gCellHoldDelay      = (float)   llList2String(Parsed, 3);
            return;
        }
        if (channel == SET_FONT_TEXTURE) {
            // Use the new texture instead of the current one.
            gFontTexture = id;
            // Change the currently shown texture.
            llSetTexture(gFontTexture, LEFT_FACE);
            llSetTexture(gFontTexture, MIDDLE_FACE);
            llSetTexture(gFontTexture, RIGHT_FACE);
            return;
        }
        if (channel == SET_THICKNESS) {
            // Set our z scale to thickness, while staying fixed
            // in position relative the prim below us.
            vector Scale    = llGetScale();
            float Thickness = (float) data;
            
            // Reposition only if this isn't the root prim.
            integer ThisLink = llGetLinkNumber();
            if (ThisLink != 0 || ThisLink != 1) {
                // This is not the root prim.
                vector Up = llRot2Up(llGetLocalRot());
                float DistanceToMove = Thickness / 2.0 - Scale.z / 2.0;
                vector Pos = llGetLocalPos();
                llSetPos(Pos + DistanceToMove * Up);
            }
            
            // Apply the new thickness.
            Scale.z = Thickness;
            llSetScale(Scale);
            return;
        }
    }
}




XyText Single Texture

( Newer Version to above )

////////////////////////////////////////////
// XyText v1.1.1 Script (Single Texture)
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// XyText Message Map.
integer DISPLAY_STRING      = 204000;
integer DISPLAY_EXTENDED    = 204001;
integer REMAP_INDICES       = 204002;
integer RESET_INDICES       = 204003;
integer SET_CELL_INFO       = 204004;
integer SET_FONT_TEXTURE    = 204005;
integer SET_THICKNESS       = 204006;

// This is an extended character escape sequence.
string  ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string  EXTENDED_INDEX  = "12345";

// Face numbers.
integer LEFT_FACE       = 4;
integer MIDDLE_FACE     = 0;
integer RIGHT_FACE      = 2;

// Used to hide the text after a fade-out.
key     TRANSPARENT     = "701917a8-d614-471f-13dd-5f4644e36e3c";
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// This is the key of the font we are displaying.
key     gFontTexture        = "b2e7394f-5e54-aa12-6e1c-ef327b6bed9e";
// All displayable characters.  Default to ASCII order.
string gCharIndex;
// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel        = -1;
// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition   = 0;
// This is whether or not to use the fade in/out special effect.
integer gCellUseFading      = FALSE;
// This is how long to display the text before fading out (if using
// fading special effect).
// Note: < 0  means don't fade out.
float   gCellHoldDelay      = 1.0;
/////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() {
   gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
   // \" <-- Fixes LSL syntax highlighting bug.
   gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
   gCharIndex += "\n\n\n\n\n";
}

vector GetGridOffset(integer index) {
   // Calculate the offset needed to display this character.
   integer Row = index / 10;
   integer Col = index % 10;

   // Return the offset in the texture.
   return <-0.45 + 0.1 * Col, 0.45 - 0.1 * Row, 0.0>;
}

ShowChars(vector grid_offset1, vector grid_offset2, vector grid_offset3) {
   // Set the primitive textures directly.
   llSetPrimitiveParams( [
       PRIM_TEXTURE, LEFT_FACE,   (string) gFontTexture, <0.05, 0.1, 0>, grid_offset1, PI_BY_TWO,
       PRIM_TEXTURE, MIDDLE_FACE, (string) gFontTexture, <0.05, 0.1, 0>, grid_offset2, 0.0,
       PRIM_TEXTURE, RIGHT_FACE,  (string) gFontTexture, <0.05, 0.1, 0>, grid_offset3, -PI_BY_TWO
       ]);
}

RenderString(string str) {
   // Get the grid positions for each pair of characters.
   vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
   vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
   vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );

   // Use these grid positions to display the correct textures/offsets.
   ShowChars(GridOffset1, GridOffset2, GridOffset3);
}

RenderWithEffects(string str) {
   // Get the grid positions for each pair of characters.
   vector GridOffset1 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)) );
   vector GridOffset2 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
   vector GridOffset3 = GetGridOffset( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)) );

   // First set the alpha to the lowest possible.
   llSetAlpha(0.05, ALL_SIDES);

   // Use these grid positions to display the correct textures/offsets.
   ShowChars(GridOffset1, GridOffset2, GridOffset3);          // Now turn up the alpha until it is at full strength.
    float Alpha;
    for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
       llSetAlpha(Alpha, ALL_SIDES);
          // See if we want to fade out as well.
   if (gCellHoldDelay < 0.0)
       // No, bail out. (Just keep showing the string at full strength).
       return;
          // Hold the text for a while.
   llSleep(gCellHoldDelay);
      // Now fade out.
   for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
       llSetAlpha(Alpha, ALL_SIDES);
          // Make the text transparent to fully hide it.
   llSetTexture(TRANSPARENT, ALL_SIDES);
}

RenderExtended(string str) {
   // Look for escape sequences.
   list Parsed       = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
   integer ParsedLen = llGetListLength(Parsed);

   // Create a list of index values to work with.
   list Indices;
   // We start with room for 3 indices.
   integer IndicesLeft = 3;

   integer i;
   string Token;
   integer Clipped;
   integer LastWasEscapeSequence = FALSE;
   // Work from left to right.
   for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
       Token = llList2String(Parsed, i);

       // If this is an escape sequence, just set the flag and move on.
       if (Token == ESCAPE_SEQUENCE) {
           LastWasEscapeSequence = TRUE;
       }
       else { // Token != ESCAPE_SEQUENCE
           // Otherwise this is a normal token.  Check its length.
           Clipped = FALSE;
           integer TokenLength = llStringLength(Token);
           // Clip if necessary.
           if (TokenLength > IndicesLeft) {
               Token = llGetSubString(Token, 0, IndicesLeft - 1);
               TokenLength = llStringLength(Token);
               IndicesLeft = 0;
               Clipped = TRUE;
           }
           else
               IndicesLeft -= TokenLength;

           // Was the previous token an escape sequence?
           if (LastWasEscapeSequence) {
               // Yes, the first character is an escape character, the rest are normal.

               // This is the extended character.
               Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

               // These are the normal characters.
               integer j;
               for (j = 1; j < TokenLength; j++)
                   Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
           }
           else { // Normal string.
               // Just add the characters normally.
               integer j;
               for (j = 0; j < TokenLength; j++)
                   Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
           }

           // Unset this flag, since this was not an escape sequence.
           LastWasEscapeSequence = FALSE;
       }
   }

   // Use the indices to create grid positions.
   vector GridOffset1 = GetGridOffset( llList2Integer(Indices, 0));
   vector GridOffset2 = GetGridOffset( llList2Integer(Indices, 1) );
   vector GridOffset3 = GetGridOffset( llList2Integer(Indices, 2) );

   // Use these grid positions to display the correct textures/offsets.
   ShowChars(GridOffset1, GridOffset2, GridOffset3);
}

integer ConvertIndex(integer index) {
   // This converts from an ASCII based index to our indexing scheme.
   if (index >= 32) // ' ' or higher
       index -= 32;
   else { // index < 32
       // Quick bounds check.
       if (index > 15)
           index = 15;

       index += 94; // extended characters
   }

   return index;
}

default {
   state_entry() {
       // Initialize the character index.
       ResetCharIndex();
   }

   link_message(integer sender, integer channel, string data, key id) {
       if (channel == DISPLAY_STRING) {
           RenderString(data);
           return;
       }
       if (channel == DISPLAY_EXTENDED) {
           RenderExtended(data);
           return;
       }
       if (channel == gCellChannel) {
           // Extract the characters we are interested in, and use those to render.
           string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 2);
                      // See if we need to show special effects.
           if (gCellUseFading)
               RenderWithEffects( TextToRender );
           else // !gCellUseFading
               RenderString( TextToRender );
           return;
       }
       if (channel == REMAP_INDICES) {
           // Parse the message, splitting it up into index values.
           list Parsed = llCSV2List(data);
           integer i;
           // Go through the list and swap each pair of indices.
           for (i = 0; i < llGetListLength(Parsed); i += 2) {
               integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
               integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );

               // Swap these index values.
               string Value1 = llGetSubString(gCharIndex, Index1, Index1);
               string Value2 = llGetSubString(gCharIndex, Index2, Index2);

               gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
               gCharIndex = llInsertString(gCharIndex, Index1, Value2);

               gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
               gCharIndex = llInsertString(gCharIndex, Index2, Value1);
           }
           return;
       }
       if (channel == RESET_INDICES) {
           // Restore the character index back to default settings.
           ResetCharIndex();
           return;
       }
       if (channel == SET_CELL_INFO) {
           // Change the channel we listen to for cell commands, the
           // starting character position to extract from, and
           // special effect attributes.
           list Parsed = llCSV2List(data);
           gCellChannel        = (integer) llList2String(Parsed, 0);
           gCellCharPosition   = (integer) llList2String(Parsed, 1);
           gCellUseFading      = (integer) llList2String(Parsed, 2);
           gCellHoldDelay      = (float)   llList2String(Parsed, 3);
           return;
       }
       if (channel == SET_FONT_TEXTURE) {
           // Use the new texture instead of the current one.
           gFontTexture = id;
           // Change the currently shown texture.
           llSetTexture(gFontTexture, LEFT_FACE);
           llSetTexture(gFontTexture, MIDDLE_FACE);
           llSetTexture(gFontTexture, RIGHT_FACE);
           return;
       }
       if (channel == SET_THICKNESS) {
           // Set our z scale to thickness, while staying fixed
           // in position relative the prim below us.
           vector Scale    = llGetScale();
           float Thickness = (float) data;
                      // Reposition only if this isn't the root prim.
           integer ThisLink = llGetLinkNumber();
           if (ThisLink != 0 || ThisLink != 1) {
               // This is not the root prim.
               vector Up = llRot2Up(llGetLocalRot());
               float DistanceToMove = Thickness / 2.0 - Scale.z / 2.0;
               vector Pos = llGetLocalPos();
               llSetPos(Pos + DistanceToMove * Up);
           }
                      // Apply the new thickness.
           Scale.z = Thickness;
           llSetScale(Scale);
           return;
       }
   }
}

Continued on XyText1.2


Library
There are 10 comments on this page. [Display comments/form]