Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Library: XyText v1.0 -- llSetText Alternative

Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
04-06-2004 22:31
Tired of llSetText? Want to display text on prims themselves, but don't want to waste a prim for each character? If so, try this script =).

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:

CODE
////////////////////////////////////////////
// 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,
<0.0, 0.0, 0.0>, <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:

CODE
////////////////////////////////////////////
// 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,
<0.0, 0.0, 0.0>, <1, 1, 0>, ZERO_VECTOR,
PRIM_SIZE, <5.0, 0.01, 2.5>,
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
]);
llSetPrimitiveParams([
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
]);

llSetObjectName("XyText Chart");

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


Continued on next reply...

[edit: Fixed formatting bug in setting gCharIndex... the word-wrap in the forums was inserting an extra character.]

[edit: Modified the prim setup script to show the string "XyText" instead of being completely transparent.]

[edit: Added syntax coloring]

[edit: Removed temporary LSL bug workaround, since bug was fixed.]

[edit: Fixed llSetPrimitiveParams call.]

Xylor
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
04-08-2004 23:13
Here is the main script, which goes on each prim you wish to use as a display.

CODE

////////////////////////////////////////////
// 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;
}
}
}



See next reply for usage information...
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
04-08-2004 23:38
=========
Basic Usage
=========

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

CODE

// 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:

CODE

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



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

////////////////////////////////////////////
// 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.

CODE

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


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

Example:
CODE

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:

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:

CODE

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:

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:

CODE

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:

CODE

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:
CODE

// 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.

------------------------------------------------------------------

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
Odys Bukowski
Junior Member
Join date: 30 Jan 2004
Posts: 5
04-13-2004 15:23
Man this rocks!

I am not even going to *try* to beat that hehe. Really nice job! And thanks for the documentation as well. A lot of coders "forget" that ;)

Cheers!
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
04-16-2004 04:33
Thanks Odys =).

btw, flicker is now gone. I removed the temporary workaround, since llSetPrimitiveParams is now working correctly (version bumped to 1.0.2). Should be slightly faster.

Xylor
Rade Galatea
Junior Member
Join date: 22 Apr 2004
Posts: 7
04-27-2004 21:40
So why in particular does it only display 6 chars per prim? I think your notecard viewer takes up something like 240 prims by default; that's more than I can have on a 512 plot of land ;-) I see that I can reduce the number of rows and require the user to scroll more, which is a compromise I'm willing to make to a degree, but I'm wondering if there could be a more fundamental solution.

My particular use-case is that I want to update a medium-sized piece of text on one of these things not more than a couple times a day (I want to publish my blog in SL, you see). So I'm wondering why the old programming law of "there is no such number as 2, only 0, 1, and N" doesn't apply (i.e., if I can have more than 1 char per prim, why can't I have 3000 chars per prim?). I assume it's some kind of compromise for some optimization, but a little clarification would be helpful.
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
04-29-2004 16:13
You are correct, it would be possible to 'stuff' more characters onto a prim. However, the function of possible character combinations goes up exponentially per character added, and the number of textures required goes up as a quadratic.

Assuming we want to display 100 different characters (SL can display 96), and that we can 'stuff' 10x10 combinations per texture, then

p = 100^c

Where:

c = Number of characters we want to be able to show.
p = Number of possible combinations.

From that, we have:

t = [ sqrt(p) / 10 * (sqrt(p) / 10 + 1) ] / 2

Where t is the number of textures required for the system.


As an example, here are the requirements for the first few:

CODE

=============================
c | p | t
-----------------------------
1 | 100 | 1
2 | 10000 | 55
3 | 1000000 | 5050
4 | 100000000 | 500500
5 | 10000000000 | 50005000
=============================


As you can see, it become pretty unmanagable after 2 combinations on each face. (The reason we can display 6, is because the way the prim is shaped; we can display 3 faces on each prim)


I hope that answers your question =)

Xylor
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
05-06-2004 00:18
Here is a useful little tool. You just drag this script onto an object, and it display's its own name using XyText. Good for making a quick little static text display, and easy to use for the scripting-challenged.

For example: To create a display that says "Hello", just create a prim, name it "Hello", and put this script on it.

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

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

// 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;
/////////// 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);
}
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,
<0.0, 0.0, 0.0>, <0.333333, 1, 0>, ZERO_VECTOR,
// Show transparent textures all sides.
PRIM_TEXTURE, ALL_SIDES, TRANSPARENT, <0.1, 0.1, 0>, ZERO_VECTOR, 0.0,
// Set the correct aspect ratio.
PRIM_SIZE, <Scale.x, Scale.x / 3, 0.01>
]);


// Initialize the character index.
ResetCharIndex();

// Just render the object's name.
RenderExtended(llGetObjectName());
// Remove ourselves from inventory.
llRemoveInventory(llGetScriptName());
}
}


[edit: Fixed llSetPrimitiveParams call.]
Rade Galatea
Junior Member
Join date: 22 Apr 2004
Posts: 7
05-06-2004 16:55
Indeed! I had no idea that's how the textures on XyText worked. Ow.

I've already cast my vote for the "render text on prims" feature request :-)
Garth FairChang
~ Mr FairChang ~
Join date: 24 Jun 2003
Posts: 275
05-11-2004 01:32
I am amazed !!!

Will try this later. Great work indeed Xylor.
_____________________
Garth FairChang ~Cheeky Brit~
' Have a nice day ;) '

http://www.fairchang.com
Jade Lily
Cat Herder
Join date: 9 Oct 2003
Posts: 219
06-28-2004 08:23
Yeah, the first time I saw XyText, I couldn't believe my eyes. Pure genius.
Codswallop Cassidy
Registered User
Join date: 30 Dec 2003
Posts: 53
07-05-2004 16:12
This is by far probably the most amazing script Ive ever seen in here. Amazing job.

how did you even get the idea?
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
07-08-2004 10:08
Well I had wanted some sort of text system for a very long time. I actually wrote an older version that does TrueTypeFonts (it has around 6 fonts available) as a proof of concept. However it was too prim intensive (1 char per prim) and was too slow.

Actually, I think it was Christopher Omega that showed me how to display 3 textures next to each other on a prim, and it was just a matter of texture manipulation to get 2 to show up on each face. Thanks Chris ^_~.

Still though, I *really* am hoping for an in-game method to display text on a prim, preferably with a way to draw simple graphics as well. We'll see how things go =D.

Xylor
Eggy Lippmann
Wiktator
Join date: 1 May 2003
Posts: 7,939
07-11-2004 18:56
Whow! You learned that from Chris?
I TAUGHT that to Chris! Woohoo, I feel important :)
AyAn4m1 Noir
Member
Join date: 16 Jan 2004
Posts: 19
07-11-2004 19:13
Yeah, they should add GDI+ support for each prim, you'd construct it like:
Graphics GraphicObject == Me.CreateGraphics

Then, all we'd need is the DrawWhatever methods and bam, prim-based drawing.

Here's to wishful thinking.
splat1 Edison
Registerd Nut
Join date: 6 Sep 2004
Posts: 353
09-09-2004 20:05
slight bug i think has crept in (or its my noob-ness)

PRIM_TYPE, PRIM_TYPE_BOX, <0, 1, 0>, 0.0, 0.0, <0.333333, 1, 0>, ZERO_VECTOR,

does not work, giveing the error

"PRIM_TYPE_BOX has missing or bad hole type"

all my atepmts at trying to fix this have failed :( any one else abel to sort this ?
Gwyneth Llewelyn
Winking Loudmouth
Join date: 31 Jul 2004
Posts: 1,336
Changes needed to Prim Setup for 1.5.+
09-10-2004 05:15
Hello,

Since 1.5 changed some things, the Prim Setup script fails because of the changes in the way holes and twist are handled now.

I tried simply to change the start of llSetPrimitiveParams to:

llSetPrimitiveParams([
// Set the top size so this shows 3 faces at once.
PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <0.333333, 1, 0>, ZERO_VECTOR,
[...]

and this seems to do the trick!

(also added this as a comment on the BadGeometry LSL Wiki)
_____________________

Gwyneth Llewelyn
Winking Loudmouth
Join date: 31 Jul 2004
Posts: 1,336
09-12-2004 02:19
spalt1, read my earlier post :)

llSetPrimitiveParams([
// Set the top size so this shows 3 faces at once.
PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <0.333333, 1, 0>, ZERO_VECTOR,

will work for me.
_____________________

Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
11-08-2004 12:02
Sorry for not updating this sooner; I haven't had much time to play SL lately. I have updated the previous posts to use the correct llSetPrimitiveParams call. =)

Also, here is another version of XyText which only uses 1 texture. This is useful to people who would rather not use a lot of textures, since the display can be fuzzy until all textures load up. The tradeoff, of course, is that it can only display 3 characters per prim, as opposed to 6.

I have also added some special effects (fading in/out), as requested for use in the Prim Attack! game by the Lindens. The fade effects are set by SET_CELL_INFO; read the global variable comments for more info. Also, since only 1 texture is needed for a font, I added a SET_FONT_TEXTURE call...I have a few fonts keys I might post later, once I dig them up. The SET_THICKNESS call was used to help scale cells up and down, but I didn't get a chance to test it much. If anyone comes up with improvements, feel free to make additions and post them here.

Here is the new code:

CODE
////////////////////////////////////////////
// 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;
}
}
}


Xylor
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
02-17-2005 19:56
Xylor, is there a reason for why you're using 7 seperate conditional chains in your link_message event rather then 1 long chain?
==Chris
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-18-2005 16:52
Probably he wants a faster return. Also it's easier to debug and less prone to compiler failures. Also a slight possability it will run a bit faster on execution of the logic.

I'm putting my money on programing style.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Prester Shaftoe
Flank Steak
Join date: 24 Feb 2005
Posts: 3
Quick question(s)..
03-28-2005 10:47
Xylor, how did you come up with the keys for the character textures? Are these just keys to objects that you've rezzed somewhere, or exist in your library? Also, in 1.1 you seem to have switched to 1 character texture. Are you using 1 combined texture and using offsets to display only one portion at a time for each character?

I've been thinking of trying something like that myself. I've already got some fixed-width fonts ready from an old JavaScript side-scroller with a bit more "eye candy" appeal. IM me if you're interested.

Thanks!
Prester Shaftoe
Flank Steak
Join date: 24 Feb 2005
Posts: 3
03-28-2005 23:25
Xylor, 2 quick questions for you-

First off, where did you get the keys for the font textures? Are those keys to textures in your library somehwere?

Also, in 1.1 it looks like you switched to 1 key for the fonts- it looks at a glance like you're using the entire character set in 1 texture, and using offsets to display the character you're looking for, is that right? Is there any tradeoff with this technique vs using a number of smaller individual textures?
Nekokami Dragonfly
猫神
Join date: 29 Aug 2004
Posts: 638
1.6 double-byte support?
03-30-2005 09:58
Has anyone had a chance to try this in the 1.6 preview with double-byte characters (e.g. Chinese?)

neko
Harris Hare
Second Life Resident
Join date: 5 Nov 2004
Posts: 301
03-31-2005 10:35
Thanks for the single texture version, Xylor. I was actually working on a single texture marque myself since I too thought the 6 character XyText used too many textures and was too slow to render. Your making this saves me the trouble (even if I had already spent a few hours coding mine.)
1 2 3