Friday, December 29, 2006

Tantum Agendum Parvulus Tempus

Oh, no! With the grid down for repair, I've been reading the SL newsletters and the like, and stumbled across the portfolio of Aimee Weber. Okay, so from what I gather she's really well known in SL, but I guess I don't get out of the sandbox often enough; it's the first time I'd heard of her.



Oh. My. God. From what she says on the page linked from the image above, she uses "baked textures" (i.e. the shadows are part of the textures themselves). That is absolutely phenomenal and I MUST LEARN HOW TO DO IT!


Once the grid comes back up, I have to visit the American Apparel store she built; apparently it swaps textures based on the time of day, so that it's lit up with floodlights at night (with shadows on the walls changing to reflect the changing light sources).

My guess is that she must reproduce her builds exactly in an external 3D application, texture and light them the way she wants them to look, then render at least a couple of passes for each wall/floor/cabinet/etc. (one for the base textures, with the furniture removed; and another one for the shadow maps with the furniture present), then combine the passes into a single image and apply them to the SL objects.

Although... in searching through the LSL Wiki for something else, I did run across this function, which includes a mouth-watering flag name: STATUS_CAST_SHADOWS"PREVIEW: If set, object casts shadows on other objects (if shadows are enabled)" Looks like it's time to try out the preview grid!

(As if I don't already have enough things on my SL "To Do" list, and RL Boyfriend isn't already feeling neglected as it is... If only I could get him sucked into SL too!)

Thursday, December 28, 2006

Yet Another Cheapside Hoard Necklace

Well, I finally hit the ceiling on complex jewelry. I made another reproduction of a Cheapside Hoard piece — this time, a chain made of alternating square-cut gems and little enameled flowers — but when I'd made a whole necklace out of them, it weighed in at over 500 prims. Way too many to link!

So I started cutting out links, working from the center back forwards, until I was far enough under 256 that I could use some simpler links for the back. I used the same links that I made for my first Cheapside Hoard necklace, but even that was too much; I had to take out the tiny prims which joined the larger ones, and stretch the large ones until they abutted.


I was hoping I could make a two-strand chain, but at a staggering 246 prims for just one half-strand, I guess that's as good as it's going to get. I'm going back and forth on whether or not to just use a simple prim with a texture mapped onto it for the complex bits. I mean, sure, there's a feeling of accomplishment at doing it without "cheating", but who really looks that closely at jewelry in Second Life? (Apart from other jewelers, I suppose). Would segments this tiny really suffer that much?

Wednesday, December 27, 2006

Quite enough holiday cheer

I feel like a slug for not creating anything in SL or posting here in what seems like forever, but alas, the holidays intruded.

Just getting back into the swing of things (and undertaking my first paying SL job, refurbishing some scripts for someone to cut down on lag and permit multiple instances of the object to coexist), so not really much to show from recent times.

However, I did make this necklace today:



Before the holidays, I also got around to trying to make some movies from within SL, and (once I'd found a codec that actually wrote something to disk!) made a clip showing one of my earlier accessories in motion and uploaded it to YouTube. And I'm just now getting around to putting it on the blog.

My first day in SL, I raided the freebie store on Help Island for everything they had, and among them were some prim wings. "I wonder if I can make wings which flap?" I asked myself. The answer was "yes, but not very well". I'll skip the boring explanations of why my first two versions didn't work, and skip straight to Johanna's Bat Wings, Mark III.



They're flexi prims, scripted to flap in a way which will resynchronize them within a few seconds if they get out of synch, and they appear and disappear based on whether or not the avatar is flying. These were kind of tricky; flexi prims get really weird when you rotate them via a script. When you turn on flexi, the "handles" move from the center of the prim to one edge. You can rotate it manually with the handles — and it rotates from the edge, like you'd expect — but if you try to rotate it from the script, it'll still rotate from the center. But if you cut the prim in half, like you do to make a swinging door open along one edge, it doesn't react the way you'd expect. So I ended up having to do both a rotation and a translation in order to keep their edges "anchored" on the shoulder blades.

And, hey, comments on my other entries! Time to go answer those...

Monday, December 18, 2006

Another "oops" moment

Day 36

If you're on a platform hundreds of meters up in the air, don't make a prim and then turn on the "Phantom" and "Physical" checkboxes just to see what will happen.

What will happen is: as soon as you don't have your phantom physical prim selected for editing anymore, the "Physical" setting will cause it to drop straight down, and the "Phantom" setting will let it go right through the floor. And then all the way down to the ground, passing through every platform and building in the way. And then, when it hits the ground, it'll bounce and roll away. And then it may try to go into a zone where somebody's put up a "no entry" barrier, and bounce away from that.

But of course since you were hundreds of meters up and it caught you by surprise, by the time you get down to the ground, it's had a huge head start and you have no idea where it's gone. And the next thing you know, you're dredging the river looking for your prim. Which is where I finally found mine.

Scripting Tutorials

Natalia inadvertently reminds me that I've been very remiss! Since the way Second Life does building is very different from the 3D modeling I'm used to, I've been posting neat little tricks that I find while playing around with prims. But the scripting language is so similar to other programming that I don't even think about it!

So I'm starting a series of scripting tutorials, in hopes that I can make Linden Scripting Language make sense to someone who's never written a computer program before.

Now, I don't know how well I'll be able to put things in layman's terms — it can be hard, when you've been doing something for a long time, to break the concepts down clearly and concisely — so I hope (if you read them) that you'll be kind enough to give me some feedback on what I could make clearer, whether I should cover less information per post, or more information, or try to phrase things in a different way, or if I'm even coherent...

I've put links to the tutorial posts up on the sidebar (I'm back-dating them to before when I started this blog, so that they'll all appear together and they won't clutter up my regular posts, and also putting them in reverse chronological order so that part 1 will appear before part 2 when you read through the archives). So far I've written two parts; part 1 is just a tour through the default script which gets created when you click on the "New Script..." button, and part 2 talks about functions and data types.

Sunday, December 17, 2006

Oops!

Day 35

I tortured a torus into a spindly, curvy shape and made a simple rotation script in it — really nothing more than llTargetOmega( <0,0,1>, 10, 0.1 ) — and just for the heck of it, turned on the "Physical" checkbox to see what it would do.

It hit the ground, fell over, and started rolling away — and it was so spindly, I couldn't select it to turn off the script! I chased after it, madly right-clicking all the way, and it hit the edge of the sim and disappeared. A few minutes later, I was notified that it had been returned to my Lost and Found folder because it had dropped off the world, two sims away. (I wonder if there are any Wild West sims that could use some tumbleweeds?)

Next time I decide to play around with physical objects, I'm building a corral for them first — and minimizing the script window after I save instead of closing it, so I can uncheck the "running" box if something goes horribly, horribly wrong.

On a side note, I see that I've used exclamation points in just about every post title so far. I'm going to have to watch that, or I'll run out of them.

Earrings! And neuroses!

Day 35

Last night (and into the wee hours of this morning), I made a set of earrings from the Cheapside Hoard (actually, I think it may have been a pendant — I stupidly forgot to get a picture of the card saying exactly what it was, or include anything in the picture to give a sense of scale — but I think it looks great as an earring). When I make (or break down and buy) a decent skin, I'll have to take a picture with them on.


The little tiny rings linking the parts together are toruses, of size <0.020,0.020,0.020>, hollow 95, hole size X=0.25,Y=0.49, profile cut B=0.74,0.76, and a twist of B=180,E=180. It's the twist that lets them get so tiny — the profile cut leaves just the smallest sliver around the outside edge of the torus, and the twist rotates it around the minor axis until it's the inner edge.

Once I'd finished the earrings, I had to buy hair that showed my ears! On the other hand, I didn't have to upload a new set of textures for these, so I suppose I came out ahead.

It's funny that I'm obsessing about how much L$ I spend in Second Life; in real life I'm a total spendthrift. "I can't be overdrawn, I still have checks left!" Maybe I could do better at balancing my RL checkbook if I had a real-time display of my available funds in the upper right corner of my field of vision... I'm sure in fifty years, or whenever they finally develop brain-interface computers, people will be able to do that! And I'm stuck here using Excel like a savage!

Speaking of obsessing, I'm really starting to worry about the cross necklace; I don't see much religious symbolism at all in Second Life. Does it make me come across as a religious fanatic? Or am I just neurotic for even worrying about it?

Saturday, December 16, 2006

Much better! (And, goals and obstacles)

Day 34

This morning, I optimized the Necklace Generator — since the necklace should be symmetrical, it's stilly to start in the front and work all the way around. So now I start halfway through (in the center back), and each time I calculate the position for one link, I just have to flip it across the X axis by negating the Y value, and I have the position for its opposite link in the chain! That cut the calculation time in half (though it still seems like it takes forever).


That's a new, nicer chain. I kept the basic link shape (it's a torus, size <0.026,0.010,0.010>, profile cut B=0.15,E=0.35, hollow 95), but instead of those flattened disc things I had linking them, I used a tiny little "cord" in between them (actually it's the same thing as the main link, but just brought down to 0.010 in the X size, and the profile cut range was reduced to B=0.20,E=0.30). You can just see them if you click on the above image for the larger version.

What are my goals?

I feel like a constant newbie, because I don't have a lot of things. And I don't have a lot of things because I vowed not to spend any real-world money on Second Life. It would be so easy to just plunk down $20 for some Linden Dollars (L$5279 at the current exchange rate, quite a bit more than I've been able to amass in over a month of camping chairs, dance pads, Scramble, and the occasional contest prize or gift). But I'm not fishing for handouts here — if I manage to turn my building and scripting skills into a profitable SL business, there'll be a bit more satisfaction in knowing I started from nothing.

I didn't get into the game with the intent of starting a business — I just thought it sounded like a neat playground for someone who enjoyed programming and 3D modeling (and besides, my boyfriend is in grad school and I have to find some way to occupy my time while he's got his nose stuck in a book). But I saw some of the things people were selling, and I thought, "Hey, I could do that! Why not give it a try?"

Obstacles

Now, building is free (though I've been told that it used to cost L$ just to rez a prim). Texturing, on the other hand... if you want to really customize your prim, you have to make the texture yourself. Each texture costs L$10 to upload, and I create each of my gemstone textures in sets of 9 (amethyst, blue topaz, citrine, diamond, emerald, garnet, ruby, sapphire and topaz). The rectangular ones need 18 (one set for vertical orientation, one set for horizontal), so that's L$90 or L$180 per set.

The best place I've found for dancing overnight (and the fact that there are usually open dance pads is part of why it's "best") only pays L$2 per 17 minutes of dancing, so every hour and 25 minutes gives me enough to upload one texture. One nine-color gem texture set requires over fourteen hours of dancing (assuming the sim doesn't get restarted and lose me all the dancing money I've accrued)! Fortunately there are other things, like Scramble games, which pay more, but you have to actually participate; you can't just sit there.

Technical User Interfacing offers a Career Course in jewelry, which includes information on how to set up a business (something I really need). I've talked to a few students who have taken or are taking it, and they all seem to believe it's well worth the L$3000 it costs to take the course. The last time it was offered, I had only a fraction of the cash to take the course. I can afford it now, just barely, but my inner skinflint is apoplectic at the thought of spending that much money all at once! Sigh.

Success!

Day 33

Well, Johanna's Necklace Generator is almost finished! After two days of fruitless script-bashing, I swallowed my pride and asked for help on the Scripters of Second Life group. A very polite young man (from Canada, which explains the politeness) spent about an hour and a half with me, ripping out my Euler rotations and replacing them with quaternion math, and then bashing on those until they finally started doing what I needed.

The Necklace Generator is pretty simple, really — to use, at least! It's a script which you put into a cylinder, along with an object representing the chain link you want to use. Edit the script and tell it the name of the link object, how long the link is (which will usually be a little less than the actual length, assuming the links go through each other), whether or not you want it to rotate every other link 90 degrees, how far down you want the chain to come in back, and a couple of other things.

Then you edit your cylinder and stretch it to the dimensions you want. The dimension along the X axis of the cylinder controls how wide the necklace is from front to back (I know, that should be the Y axis, but that would add a whole new layer of math to everything... no thanks!), the Y dimension controls how wide the necklace is from side to side, and the height of the cylinder controls how far down it comes in front. (If you set its Z dimension to 0.010, it suppresses the drop entirely.) How far down it comes in back is controlled by a variable, as mentioned above; it's pretty much a percentage of how far down it comes in front.

Then you just touch the cylinder, and it thinks for a LONG time, and...


...several months later, you have a necklace chain wrapped around the cylinder! (Well, as long as you leave the cylinder with rotation <0,0,0> — it'll ignore the cylinder's rotation and act like it's unrotated regardless.) That slice cut out of the cylinder is to remind me which way is "front". I suppose I'll eventually make a special cylinder texture for the Necklace Generator.


After seeing it, I'm not really sure I like the style of that link — the rings are so thin they disappear when seen edge-on — but you get the idea. And from further away, it doesn't look too bad on the Cheapside Horde cross. As you can see below, the rotations aren't perfect, but it's close enough that only a couple minutes' tweaking are enough to bring everything in line, which is still much better than having to build the whole chain by hand!


The only thing I have left to do is add an option to make it automatically come to a point in front, for pendants and the like.

Also, I'm trying out some new textures for gems. I loaded a test pattern texture (I think it was from Robin Wood's in-world texture tutorial) onto a cylinder tortured into a gemstone shape to see how the faces were actually patterned. After doing a little unrotating, I came up with this texture:



Basically, it's a straight-on view of the gemstone's table and crown, taking up the top 80% of the image (I pasted shrunken copies underneath it as a "bleed" area), with the bottom 20% being the crown "unwrapped" with The GIMP's Polar Coords filter (Filters -> Distorts -> Polar Coords, with "To Polar" unchecked), cropped to the area beneath the points of the table and above the curves of the girdle, and then resized vertically to take up the bottom 20%.

When uploaded to Second Life, and applied to the gemstone, a little massaging the repeats and offsets just a touch, it resulted in this:


It looks a little blurry, because I made the texture at too small a size. But that doesn't matter for this particular gem, because unless people are looking really closely, they probably won't notice.


This is the gem on top of a cube sized to <0.010,0.010,0.010> — the smallest you can make a prim in Second Life without resorting to prim torture:

The gem's prim is actually <0.020,0.020,0.010>, so it's twice as big around as it could be! (I needed to make it bigger to get the proper slope for the crown.) It's a sphere, set to dimple B=0.95, E=1.00, converted into a cylinder, tapered to X=0.95, Y=0.95, and then resized to <0.020,0.020,0.010>.

If you care to try it, feel free to grab the gemstone texture and put it on the prim! The table (the flat part on top) should have its horizontal and vertical repeats per face set to 0.400, the rotation to -90.00 degrees, and the vertical offset at 0.1. The crown (the sloped edges) should be set with a horizontal repeat per face of 1.000, flipped; vertical repeat per face of 4.000, rotation of 0 degrees, a horizontal offset of 0.000, and a vertical offset of 0.300.

(Edit: For some reason, when I followed my original instructions just to make sure I had them right, they didn't produce the results I expected! Above are the corrected instructions.)

Thursday, December 14, 2006

Jewelry

Day 32

So here are some pictures of the jewelry I've made so far. I'm still not finished with the necklace chain generator, but I'm getting closer (I now have placement on the perimeter of the cylinder and one rotation; I just need two more all three rotations).



Above is the first really intricate piece I made, it's an attempt to reproduce my grandmother's locket in-world. The necklace chain was made by hand, and took almost as long as the locket itself. As with all my jewelry, it's scripted: you can change the color of the metal and the gemstone textures, turn bling on and off, and a couple of other minor things. It's "actual size", the locket itself is about 5cm tall and 4cm wide.


This is a matching circlet, bracelet and armband; I was playing around with torturing a torus and the design suggested itself to me. Like with the locket, you can change the colors — either individually or all pieces at once.



This is a pretty simple brooch. I mainly made it to showcase a set of gemstone textures I made. The problem with the usual gemstone textures is that they don't look good when wrapped around a sphere, spheres need to be textured with a Mercator-like projection for minimal distortion. I rendered a spherical faceted gem in POV-Ray with an orthographic camera, then used The GIMP to perform a reverse polar mapping on it. That gave me the top hemisphere, so I just cloned the layer, rotated it 180 degrees and moved it down so that its top edge was touching the bottom edge of the original layer. Flatten, save and upload, et voilĂ ! A spherical gem texture that looks good on a sphere! I also made it in multiple colors:


I made nine gem colors in all: amethyst, blue topaz, citrine, diamond, emerald, garnet, ruby, sapphire and topaz.

This cross, which hopefully will have a necklace chain sometime before the end of the century, is a reproduction of a piece from the Cheapside Hoard, a treasure trove of jewelry from around 1600, dug up in the early 1900s. There are plenty of things I want to make from the Hoard. Above is a rectangular-cut amethyst texture I made; below are garnet and blue topaz (shown with the metal set to gold and silver, respectively). Actually, I created two textures for each color: one for the gem oriented horizontally, and one for vertical orientation. Alas, the distortion caused by tapering the box robbed a lot of the detail from the sides of the gems, but it's still not too bad. There's no bling on this piece, because I found it was just so tiny the bling particles overwhelmed it. But sometimes subtle is much nicer than flashy.

The little finial gemstones are almost too small, as I mentioned in my previous post; I lost quite a few attempts while making them. Create a sphere and set its dimple to B=0.98 and E=1.00, then change it to a cylinder. Taper it to x=0.95 and y=0.95, and then set the size to x=0.010, y=0.010, z=0.010, and you have an incredibly tiny conical frustum. The rectangular and diamond-shaped pieces were made small (or excessively tiny!) in a similar fashion; dimple a sphere (I think I used B=0.75,E=1), change it into a box, then taper and resize to taste.



Just for a little sense of scale, here's a table showing the different pieces of jewelry I've made so far. The sword isn't really jewelry, but I'm proud of it too. Most nights, the Technical User Interfacing (or TUi) school holds challenge build competitions: you show up, and when the competition starts, they tell you what the topic is. You have 45 minutes to create it entirely from scratch (you can use textures and scripts from your inventory, but that's it — all the prims in your build have to be created, textured, and positioned right there). The topic for that night, obviously, was "swords". I decided I wanted something ladylike — no point in making something I couldn't use! — so I Googled for images of swords and found a beautiful swept-hilt rapier.


I was working right up to the time limit. I didn't win (well, I thought mine looked the best!) but it was fun, and challenging, and I made something I probably wouldn't have otherwise. Unfortunately, those contests are usually held at 10PM Pacific time, so if I participate on a work night I only get about four hours' sleep.

Wednesday, December 13, 2006

Math is hard, let's go shopping!

Day 31

I find building jewelry in Second Life to be relaxing, rewarding and aggravating. "Relaxing" because there's a regularity to it; it's precision work, and I can just let the world fade away as I concentrate on moving little tiny prims around. It's what I imagine Zen is like, complete focus on a single thing, where time and the physical world are meaningless.

It's rewarding because there's always the challenge to slice up the prims just a teeny bit smaller than they're supposed to go, without getting them so tiny that the client won't render them anymore and they vanish, invisible and unselectable, not to be seen again until the sandbox returns your them because they've been there too long.

But it's aggravating because if you want to do things perfectly, sometimes you have to do math. And I hate math. I'm not good at it, I look at pages like this and want to run away. (And that was the least technical (and condescending!) answer in the thread!)

See, I was trying to build a better mousetrap. Necklace chains are an enormous pain to do by hand (Zen relaxation is great, but there's only so much of it I can stand). And there are a few scripts out there which will automate building necklaces, but I haven't found any which do quite what I wanted:

  1. Let the user create a cylinder and stretch it to the desired dimensions, so that the x dimension represents the width of the neck, the y dimension represents the front-to-back measurement (including the amount the chain comes forward to lie properly on the chest), and the z dimension represents the vertical curve of the chain (coming around the neck, over the shoulders and down the chest).
  2. Keep the spacing of the links constant, with no clustering or gapping if the path is elliptical.
  3. Calculate the proper number of links necessary, based on the length of the links and the distance traveled around the path.
  4. Rotate every other link 90 degrees around the axis of travel, if desired.
Doesn't sound too hard, right?

Ha! For starters, there's not even a definitive formula for finding the perimeter of an ellipse — just approximations with varying degrees of accuracy! I bashed my head against a wall for about eight hours last night and this morning, trying to get the formula in the above-linked page to work, before I finally gave up and resorted to a brute-force approach. It's not pretty, it's slow as molasses, but at least it works.

Well, what I've got written so far works — they took the grid down for updates before I was able to write the code which rotates the individual links into the proper orientation.

Once I get some decent chains made, I can put up some pictures of my jewelry!


Oh, also, a public service announcement: don't try to play Primtionary (like Pictionary, only with prims) if you've had a drink or two. The results aren't pretty.

Saturday, December 9, 2006

Here Goes Nothing

I never thought I would write a blog, but then again I never thought I'd get addicted to an MMORPG, so it just goes to show.

I used to make fun of my boyfriend for spending hours playing World of Warcrack (instead of paying attention to me, like he should have been). Well, now I guess I've shown him how it feels, because now he's the one complaining about being a computer widow. Turnabout is fair play, bucko!

Anyway, I was motivated to start this blog when I came across Natalia Zelmanov's Second Life blog. It was interesting seeing all the things she's made, and how she made them. She has the Mad Costuming Skillz™, and I've found her writings really helpful. Me, I'm probably strongest as a scripter, but I'm diving into all aspects of SL — building, dressmaking, animating, and so on.

So here's a journal of my progress through Second Life, starting 28 days after I first logged on.

Thursday, November 30, 2006

Scripting: the basics, part 1

LSL, the Linden Scripting Language, is very similar to many modern scripting languages, such as JavaScript or PHP (which themselves have a lot in common with the C programming language and its descendants).

But not a whole lot of people write scripts in Second Life, maybe because it's a bit scary with all those curly brackets and parentheses and semicolons. I thought I'd try my hand at being a teacher in SL, since I learned so much in classes my first couple of weeks there, and wanted to "give back" to the community, so I'll use this blog to start organizing my thoughts for what and how I'd like to teach!



For this first post, let's look at the default "New Script" which gets created when you click the New Script… button in the Content tab of the edit window. (If you've never tried this before, rez a prim and select the Edit button on the Focus/Move/Edit/Create/Land panel that pops up. If it's a short window, click on the More » button in the lower right-hand corner. Then click on the rightmost tab labeled Content, and press the New Script… button.)

When you create a new script, your prim will say something like "Object: Hello, Avatar!" (assuming you haven't renamed it from its default name of "Object"). Double-click on the script (named, aptly enough, "New Script"), and an edit window will pop up. The script will look something like this:

default
{
state_entry()
{
llSay(0, "Hello, Avatar!");
}

touch_start(integer total_number)
{
llSay(0, "Touched.");
}
}

The first line in the script is "default". This means that the following code pertains to the object's default state. (States are an easy way of making the object react different ways to the same stimuli. If you don't have need of different states, as this script doesn't, then everything happens in the default state.)

The second line is an opening curly bracket (or curly brace). Curly braces are used in LSL, as in several other languages, for grouping multiple statements together. All states and functions must enclose their contents in curly brackets, even if those contents are empty. (They'll be more useful later on, when we get into conditionals and flow control.)

The third line is "state_entry()" - this is a function called every time the script enters a state. If it's located in the default state, it gets called every time the script is saved or reset. The parentheses appear after every function definition, and enclose the parameters that are passed to the function. The state_entry() function has no parameters, so there's nothing between the parentheses.

The fourth line is another curly bracket, this time enclosing the contents of the state_entry() function. (You'll notice that things are indented, and each time there's a set of curly brackets, everything inside them gets indented another level; this isn't required, but it does make it a lot easier for a human to read if the contents of every nested container are indented by a tab's worth of spaces.)

The fifth line — the actual contents of the state_entry() function — is a call to a predefined LSL function, llSay(). (Many predefined LSL functions start with "ll", for Linden Lab.) This particular function call causes the object to say "Hello, Avatar!" on chat channel 0 (zero), the one players talk on and the only one they can "hear" without some sort of scripted listening device. The line ends with a semicolon (;), which is put at the end of every statement.

The sixth line is a closing curly bracket, and as we can see by the way it's indented, it lines up with the opening brace for the state_entry() function. If you find a script where things are not indented like this, you may have to count the opening and closing braces to find out where things begin and end!

After a blank line (again, not strictly necessary, but good for readability), we have a new function, touch_start(). This one is called whenever someone begins touching the object or prim containing the script, and takes one parameter, an integer (whole number, with no decimal point) which indicates the total number of agents (avatars, or players) who have started touching it since the last time it was called. This is usually just one, unless people touched it simultaneously or the sim is very lagged. This particular script doesn't do anything with that parameter, but we still need to include it.

Handy Tip: When you type a word that the script editor recognizes as a valid LSL keyword, it changes color. You can hover your mouse pointer over a colored word to see more information about it — for example, if you type the name of a function and hover the mouse pointer over it, it will pop up a little tooltip telling you what parameters it takes, and what kind of data they are.


Within the curly brackets for touch_start(), we have another llSay() function. In this particular script, when someone touches the prim or object containing the script, it will say "Touched."

After the closing curly bracket for the touch_start() function is the closing curly bracket for the default state... and that's the end of the script!

Scripting: the basics, part 2

In the first part, we wrote a couple of functions, and used another one a couple of times, but we didn't really go over what a function is, how it works, or why you would want to use one. Here we will look at functions, and also at the types of data which Second Life scripts can use.

In LSL, the Linden Scripting Language (and in programming in general), a function is a modular unit of code which can optionally be passed a number of parameters, and which can optionally return a value.

float quintuple( float theNumber )
{
return theNumber * 5.0; // Multiply by 5
}

A silly little example, but it should serve to illustrate the main points of a function.

First, we have the function declaration. This consists of three parts: the optional return type (in this case, a "float", or floating-point number — a number which can have a decimal point, as opposed to an integer which must be a whole number), the function name, and the optional parameter list, which shows what kind of data the function expects, and gives it a (hopefully) descriptive name.

If your function doesn't return a value, leave off the return type. If it does return a value, the return type must indicate the type of data the function will return (we'll cover data types later on in this post). The function name is required (otherwise there's no way to call the function). The parentheses which enclose the parameter list must be present in every function declaration, even if you have no actual parameters.

Next we have the curly brackets which enclose the statements of the function itself. Curly brackets are used to group multiple statements into a larger unit. They are required to enclose the body of a function, even if the body consists of only one statement (as this one does).

And inside the curly brackets we have a statement which multiplies the parameter by 5, and returns that value. The statement ends with a semicolon. After the semicolon are two forward slashes (//) — this starts a comment. You can put anything you want in a comment, but usually comments are used to explain what's going on (either so that somebody else reading your script can understand what your intent is, or when you come back to modify your script months later, you can understand what your intent was).

And finally we have the closing curly bracket, which terminates the function declaration.

If we were to call this function from elsewhere in the script, it might look something like this:
float result = quintuple( 3.0 );
This line creates a new floating-point variable named "result", and assigns the return value of our "quintuple" function to it. Since we pass it the number 3.0 as the parameter, and the function multiplies the parameter by 5.0, we should end up with 15.0 in the variable named result.

Why would you want to use this as a function, rather than just multiplying values directly in your code? Well, you wouldn't; as I said, this was just a silly example. But if you have a case where you perform multiple actions on some data, and you have to perform these actions many times from different places in your code, you might turn the actions into a function. You can also improve the readability of complex code by moving logical groupings of instructions into their own functions.

And now a word about variables, and the types of data they can represent.

A variable is a way of storing values for later use. You can store seven types of values in LSL:

  • integer - a whole number, with no decimal point. (examples: 0, 1, -27, 42, 2147483647)

  • float - a floating-point number, or one with a decimal point. (examples: 0.0, 3.141592654, 17.6, -57.0)

  • string - text data. String values are enclosed in quotes when you define them. (examples: "Hello, Avatar!", "yes", "no", "I'm sorry, Dave, but I'm afraid I can't do that.", "1942"1)

  • key - A special kind of string, which represents something within Second Life. Key values are also enclosed in quotes when you define them. (example: "c541c47f-e0c0-058b-ad1a-d6ae3a4584d9")

  • vector - a three-part number which represents a 3D position in space. You can also use vectors to store Euler rotations, but we won't get into those quite yet. The components of a vector are each float numbers. (examples: <0,0,0>2, <17.8,32.5,16.0>)

  • rotation - similar to a vector, only with four components. These are used for quaternion rotations, and will make your brain hurt. (examples: <0,0,0,1>, <3.141592654,0.0,0.0,1.0>)

  • list - a special kind of data type which can contain zero or more elements of any other data type. Lists are signified by square brackets surrounding their elements, which are separated by commas. We'll get into lists much later. (examples: [0,1,2,3,4,5], ["Bob", "Mary", "Joe", "Sue"], [], [1, 3.141592654, "Isn't this fun?", <0,0,0>])



1. Even though 1942 is an integer, you can have a numeric value in a string. It's the quotes that make all the difference.

2. If a float number has nothing but zeroes after the decimal point, you can usually leave off the decimal point; LSL will usually promote it to a float. Usually. This is not, however, always the case; certain functions will produce errors if you pass an integer in a list where a float is expected.

Scripting: the basics, part 3

This script is one I wrote for a barrier which prohibits entry unless the barrier's owner touches or collides with it; when this happens, it will set itself to PHANTOM and turn partially transparent for five seconds, and then return to its previous opaque solidity.

It's also a good demonstration of how to modify prim parameters, set timers, and use multiple states.

default {
collision_start(integer total_number) {
if (llDetectedKey( 0 ) == llGetOwner()) state open;
}

touch_start(integer total_number) {
if (llDetectedKey( 0 ) == llGetOwner()) state open;
}
}

state open {
state_entry() {
llSetPrimitiveParams( [PRIM_PHANTOM,TRUE,
PRIM_COLOR,4,<1,1,1>,0.75,
PRIM_COLOR,2,<1,1,1>,0.75] );
llSetTimerEvent( 5 );
}

timer() {
llSetPrimitiveParams( [PRIM_PHANTOM,FALSE,
PRIM_COLOR,4,<1,1,1>,1.0,
PRIM_COLOR,2,<1,1,1>,1.0] );
llSetTimerEvent( 0 );
state default;
}
}


Okay, now the first line in this script is the default state. States are a way for the script to behave differently based on certain criteria; without states, we would have to use variables to keep track of things. In this script, I have two states: the default state (in which a touch or collision by the owner will "open" the barrier), and the "open" state, in which it opens itself, waits five seconds, and then closes again; in this state, further touches and collisions will do nothing.

We have two functions in the default state: collision_start and touch_start. These two functions are called, if they exist, when an avatar or object starts to collide with the prim containing the script, or when an avatar starts touching the prim containing the script.

These two functions are passed a parameter, an integer value indicating how many objects or avatars have started colliding with (or touching) the prim. This number is usually one (it's theoretically possible for two avatars to start colliding with or touching the prim at the exact same instant, but for the sake of simplicity we're going to assume that it's only one).

As it happens, I want both of these conditions to do exactly the same thing, so the code within each function is identical:

       if (llDetectedKey( 0 ) == llGetOwner()) state open;


llDetectedKey() gets the UUID of the object detected by the function. Index number 0, which we're using here, is the first object or avatar detected. (We could check the "total_number" variable, and if it's greater than one, we could loop through and check all of them, using llDetectedKey( 1 ), llDetectedKey( 2 ), and so on.)

The statement then compares the value returned by llDetectedKey() against the key of the person who owns the barrier, which is returned by the llGetOwner() function. If they're equal, then it switches into the state I've named "open".

Tip: The default state must occur first in the script. If you try to insert another state above default, the LSL compiler will produce a syntax error when you try to save the script.


The "open" state contains a function named state_entry. This gets called, if it exists, whenever the script enters the state the function is contained within. All states may have a state_entry function; like with all predefined functions, whichever one exists in the currently active state is the one that gets called.

The state_entry function calls llSetPrimitiveParams(). This is a function which can change nearly every feature of a prim, including what shape it is! It takes a list of things you want to change, and what you want to change them to. (The wiki has a great page describing all the things it can do.)

Here, we're changing the PRIM_PHANTOM parameter, and setting it to TRUE. I also decided to give a little visual feedback, so I'm setting the front and back faces of the barrier to partially transparent. With llSetPrimitiveParams(), you have to change both the color and the alpha setting simultaneously. Fortunately, I know that the faces should be completely white, so for each face I pass the PRIM_COLOR keyword (which tells llSetPrimitiveParams() what I want to change), the number of the first face to change, the RGB value <1,1,1> (white), and the alpha value of 0.75, (75% opaque). I then pass the same sequence again for the other face.

Tip: To find out what face you want to change, edit the prim and click on the "Select Texture" radio button in the edit dialog. You should see crosshairs appear on all faces of the prim. Click on the face you want to find the number for, and hit Ctrl-Alt-Shift-T. Your chat window will give you certain information about the texture on that face, including the face number.


After setting the prim parameters, I then set a timer event to trigger five seconds later (and subsequently in five-second intervals until the timer is killed), and that's the end of the state_entry function.

The next function is the timer function, which gets called when the timer event is triggered. In this function, I call llSetPrimitiveParams() again to reverse the changes I made earlier (I set the PRIM_PHANTOM parameter back to FALSE, and set the alpha on the front and back faces to 1.0 (fully opaque).

The next line kills the timer events (by passing a parameter of 0), and then the next line after that sets the script back into the default state again so that it will "listen" for collisions and touches again.