From Second Life Wiki
Revision as of 15:47, 8 December 2012 by Salahzar Stenvaag (talk | contribs)
Jump to navigation Jump to search

Scripting tools to allow display of text on non meshed prims: XyText 1.5 , XyzzyText, XyyyyzText, XyText-UTF8, XyzzyText-UTF8, ZZText

Introduction Why a new ssText device for Meshes viewers

At the end of 2012, after a year of Meshed products, and stating that people are using Meshes with the majority viewers including all major 1.x Viewers with the possible notable exception of old Imprudence, is now time to update Boards to take advantage of Meshes to have better quality and a reduction of land Impact. In fact standard xyText cells use 1 prim to display either 5 or 10 characters. A board 10 Lines x 40 character costs at minimum 400/5=80 prims using 5char/prim solution and 40 prims using 10char/prim solution. 80 prims are usually a bit too much expensive for a land owner (it actually drains almost all of the 117 prim limits offered for a basic land), while low resolution solution has a very low quality and is almost impossible to script different fonts with gimp (we have lost original scripts from Xylor Baysklef 10 char/prim and what I developed is only an aproximation).

What are the advantage of Meshes vs traditional xylike textual boards?

  • A cell can be crafted in blender to show 8 faces to observer completely flat and with NO distortion. This is obtained having 8 materials assigned to each square and a very simple UVMap for it. Mesh cell has only 18 vertices so it is quite lightweighted component.
  • When loaded you can choose to have most of lods at the same high quality so that board can be seen in distance without being distorted
  • When linked each cell counts for 0.5 landimpact => a board made of 400 chars will impact only for 400/16= 25 prims which is a very good improvement compared to standard techniques.
  • Reduces lag. I tried to use standard xytext boards made a 1000 char-board as HUD having an object of 178 linked prim. When editing and loading this with various viewer I had to wait for a long period of time each time I touched on Edit waiting for viewer to loads characteristics of 178 prims. When used with meshed version viewer reacted almost instantly simplifying the life for developers and for final users.

I don't see any disadvantage except the marginal problem that people entering SL with Imprudence or some very other old viewer not mesh aware cannot see this.

Scripts have been tested on OpenSim and they are perfectly working with just a minor and trivial editing so this is adaptable to every OpenSimulator virtual world

These scripts are still on xytext scripts from Xylor Baysklef plus modifications I added in other versions UTF8 to allow for International Characters.

How to have prefabricated objects and scripts

I've prepared a zip containing a kit including current mesh plus scripts and other files needed to prepare inworld a working board you can download them from here:

You can find there the following files zipped:

The Gimp pythonfu script which can be used to produce different fonts (see also


Some fonts created with the previous gimp script (if you want a different font to be used either set it to font and insert in the main root inventory, or modify the script lsl

  • font-codenamecoderfree4fbold.png
  • font-fabrika 4f medium italic.png
  • font.png


The lsl scripts sample-client just writes on row 1000

  • sample-client.lsl

One slave script which can handle linked_message sent to rows 1000 upto 9999 and each row is long 5 cells (cells containing each 8 chars)

  • sstext-1000-9999-5.lsl

The blender cell in blend and Collada format (remember when importing in sl to specify copy LOD from previous for all the lods to have best results)

  • xysaltext.blend
  • xysaltext.dae

Scripts used

For sake of simplicity I'm also writing here the two scripts:

Here is the slave <lsl> //////////////////////////////////////////// // SALTEXT Salahzar Stenvaag November 2012 // // Inspired by Xylor Baysklef, Kermitt Quirk see original // // // more info at // // // ////////////////////////////////////////////

debug(string x) {} //_debug(string x) { llMessageLinked(LINK_SET,-2,llGetTimestamp()+" "+llGetScriptName()+":"+x,NULL_KEY); } //#define debug(x) // llOwnerSay(x)

// integer NCELLS=6; /////////////// CONSTANTS ///////////////////

// number of characters for each "line" in texture integer knum=10; integer start=0;

// adjusting offset + multipliers for columns and rows vector kcol=<-0.44,0.10,0>; // offset + multiplier for cols vector krow=<0.419,-0.05,0>; // offset + multiplier for rows vector kmul=<0.07,0.05,0>;

list decode = [ "%C3%87", "%C3%BC", "%C3%A9", "%C3%A2", "%C3%A4", "%C3%A0", "%C3%A5", "%C3%A7", "%C3%AA", "%C3%AB", // è ï î ì Ä Å É æ Æ BLACK RIGHT-POINTING TRIANGLE "%C3%A8", "%C3%AF", "%C3%AE", "%C3%AC", "%C3%84", "%C3%85", "%C3%89", "%C3%A6", "%C3%AE", "%E2%96%B6", // ö ò û ù ÿ Ö Ü ¢ £ ¥ "%C3%B6", "%C3%B2", "%C3%BB", "%C3%B9", "%C3%BF", "%C3%96", "%C3%9C", "%C2%A2", "%C2%A3", "%C2%A5", // A^ copyright á í ó ú ñ Ñ ª º" "%C3%82", "%C2%A9", "%C3%A1", "%C3%AD", "%C3%B3", "%C3%BA", "%C3%B1", "%C3%91", "%C2%AA", "%C2%BA", // ¿ O^ ¬ ½ ¼ ¡ «   » alfa ß" "%C2%BF", "%C3%94", "%C2%AC", "%C2%BD", "%C2%BC", "%C2%A1", "%C2%AB", "%C2%BB", "%CE%B1", "%C3%9F", // gamma pi bigsigma smsigma mu "%CE%93", "%CF%80", "%CE%A3", "%CF%83", "%C2%B5",

// tau BIGfi theta omega delta "%CF%84", "%CE%A6", "%CE%98", "%CE%A9", "%CE%B4", // Uu uu EPS INTERS 3bars +- <= >= INTEGRUP INTEGRDOWN "%C5%AC", "%C5%AD", "%CE%B5", "%E2%88%A9", "%E2%89%A1", "%C2%B1", "%E2%89%A5", "%E2%89%A4", "%E2%8C%A0", "%E2%8C%A1", // A/ A\ a~ A~ E^ I/ O/ o~ O~ o^ "%C3%81", "%C3%80", "%C3%A3", "%C3%83", "%C3%8A", "%C3%8D", "%C3%93", "%C3%B5", "%C3%95", "%C3%B4" ];

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

///////////// GLOBAL VARIABLES /////////////// // This is the key of the font we are displaying. key gFontTexture = "font"; // 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[\\]^_`"; gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~\n\n\n\n\n";



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

// Return the offset in the texture. return < kcol.x + kcol.y * Col, krow.x + krow.y * Row, 0.0>; } ShowChars(integer linknum, vector grid_offset1, vector grid_offset2, vector grid_offset3, vector grid_offset4, vector grid_offset5, vector grid_offset6, vector grid_offset7, vector grid_offset8) { // Set the primitive textures directly.

llSetLinkPrimitiveParamsFast( linknum, [ PRIM_TEXTURE, 0, (string)gFontTexture, kmul, grid_offset1, 0.0, PRIM_TEXTURE, 1, (string)gFontTexture, kmul, grid_offset2, 0.0, PRIM_TEXTURE, 2, (string)gFontTexture, kmul, grid_offset3, 0.0, PRIM_TEXTURE, 3, (string)gFontTexture, kmul, grid_offset4, 0.0, PRIM_TEXTURE, 4, (string)gFontTexture, kmul, grid_offset5, 0.0, PRIM_TEXTURE, 5, (string)gFontTexture, kmul, grid_offset6, 0.0, PRIM_TEXTURE, 6, (string)gFontTexture, kmul, grid_offset7, 0.0, PRIM_TEXTURE, 7, (string)gFontTexture, kmul, grid_offset8, 0.0 ]); }

// SALAHZAR intelligent procedure to extract UTF-8 codes and convert to index in our "cp850"-like table integer GetIndex(string char) { integer ret=llSubStringIndex(gCharIndex, char); // llSay(0,"pos: "+(string)ret); if(ret>=0) return ret;

// special char do nice trick :) string escaped=llEscapeURL(char); // llSay(0,"char: "+escaped+" "+llList2CSV(decode)); integer found=llListFindList(decode, [escaped]);

// Return blank if not found if(found<0) return 0;

// return correct index // llSay(0,"returning "+(string)(100+found)); return 100+found;


RenderString(integer linknum, string str) { debug("telling link "+(string)linknum+" str {"+str+"}"); // Get the grid positions for each pair of characters. vector GridOffset1 = GetGridOffset( GetIndex(llGetSubString(str, 0, 0)) ); // SALAHZAR intermediate function vector GridOffset2 = GetGridOffset( GetIndex(llGetSubString(str, 1, 1)) ); // SALAHZAR vector GridOffset3 = GetGridOffset( GetIndex(llGetSubString(str, 2, 2)) ); // SALAHZAR vector GridOffset4 = GetGridOffset( GetIndex(llGetSubString(str, 3, 3)) ); // SALAHZAR vector GridOffset5 = GetGridOffset( GetIndex(llGetSubString(str, 4, 4)) ); // SALAHZAR vector GridOffset6 = GetGridOffset( GetIndex(llGetSubString(str, 5, 5)) ); // SALAHZAR vector GridOffset7 = GetGridOffset( GetIndex(llGetSubString(str, 6, 6)) ); // SALAHZAR vector GridOffset8 = GetGridOffset( GetIndex(llGetSubString(str, 7, 7)) ); // SALAHZAR

// Use these grid positions to display the correct textures/offsets. llSetLinkPrimitiveParamsFast( linknum, [ PRIM_TEXTURE, 0, (string)gFontTexture, kmul, GridOffset1, 0.0, PRIM_TEXTURE, 1, (string)gFontTexture, kmul, GridOffset2, 0.0, PRIM_TEXTURE, 2, (string)gFontTexture, kmul, GridOffset3, 0.0, PRIM_TEXTURE, 3, (string)gFontTexture, kmul, GridOffset4, 0.0, PRIM_TEXTURE, 4, (string)gFontTexture, kmul, GridOffset5, 0.0, PRIM_TEXTURE, 5, (string)gFontTexture, kmul, GridOffset6, 0.0, PRIM_TEXTURE, 6, (string)gFontTexture, kmul, GridOffset7, 0.0, PRIM_TEXTURE, 7, (string)gFontTexture, kmul, GridOffset8, 0.0 ]); // ShowChars(linknum, GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5, GridOffset6, GridOffset7, GridOffset8); }

parseMessage(string m, integer row) { debug("Parsing message {"+m+"} to row: "+(string)row);

integer j;

for(j=0;j<g_length;j++) { string element=llGetSubString(m,j*8,(j+1)*8-1); integer found=llListFindList(g_lookup,[ (string)(row+j) ]); if(found>=0){ integer linknumber=llList2Integer(g_lookup,found+1);

RenderString(linknumber,element); } } // for(i=start;i<llGetNumberOfPrims();i++) RenderString(i+1," "); }

string g_name; integer g_start; integer g_end; integer g_length;

list g_lookup; // pairs name + linknumber

default { state_entry() { debug("Initialize"); // Initialize the character index. list bits=llParseStringKeepNulls(llGetScriptName(),["-"],[]); g_name=llList2String(bits,0); g_start=(integer)llList2String(bits,1); g_end=(integer)llList2String(bits,2);

g_length=(integer)llList2String(bits,3); debug("Start: "+(string)g_start+", End: "+(string)g_end+", length: "+(string)g_length); ResetCharIndex(); integer i; g_lookup=[]; for(i=1;i<=llGetNumberOfPrims();i++) { string pname=llGetLinkName(i); integer iname=(integer)pname; if(iname>=g_start && iname<=g_end) { debug("adding {"+pname+"} link: "+(string)i); g_lookup+=[ pname, i ]; } } debug("parsing"); integer r; for(r=1000;r<200000;r+=1000) parseMessage("{abcdefghijklmABCDEFGHIJKLMNOPQRSTUVWXYZàèìòù}",r); } link_message(integer sender,integer channel,string str,key id) { if(channel>= g_start && channel <=g_end ) parseMessage(str,channel); }

} </lsl>

And here is the sample program:

<lsl> default { state_entry() { // will display Hello World on the cells starting from 1000 upward llMessageLinked(LINK_SET,1000,"Hello World",""); } } </lsl>