Showing posts with label geeky. Show all posts
Showing posts with label geeky. Show all posts

CEF, New Media, and Old Videos

Recently (June 23rd), the Linden viewer released with an important update to "CEF" - the Chromium Embedded Framework that's used for viewing web pages and -- crucially -- media. At the time of writing, only the Linden viewer has this support, but third party viewers will likely adopt this feature very soon.

Support for the new CEF version first surfaced in a project viewer that used streaming media for the Adult Swim "The Shivering Truth" event, in May.

But there are broader benefits of the enhanced media support. Notably, other video services and formats are now supported that before would give a blank screen (at best). One such service is Vimeo, fairly widely used in entertainment and, specific to SL, the home of an exhaustive archive of old Designing Worlds videos that were once hosted by Treet TV.

Unlocking these Vimeo-based archives needs a bit of tweaking on in-world video players. It's actually possible to play these videos on either "Shared Media" surfaces (aka "Media on a Prim") or Parcel Media. Both will use a custom URL that embeds Vimeo's proprietary streaming player to see the video directly instead of a web page surrounding the player. The basic player URL is offered as a way to "Share" the video from the regular Vimeo web page, but there are a lot of confusing options. What I've found to work best is to copy the number in the Vimeo webpage URL (call that VIMEO_NUMBER), and splice it into an address as follows:
https://player.vimeo.com/video/VIMEO_NUMBER?autoplay=1&loop=1
which sets it to autoplay and loop by default. (Note the "player" in that address.)

Both Shared and Parcel media also face a quirk of the Vimeo player: it displays the video in the center 56.25% of the vertical surface texture - a huge "letterbox" effect. To get around this, a script can scale the texture to stretch out that strip to span the whole height of the surface, or you can do this manually in the editor. (To get a black letterbox, include "&transparent=0" in the URL.)

When presented in Shared Media, the user can interact directly with the viewer by clicking on it (or using the keyboard with the surface selected), so you'll likely want to turn off the Shared Media control options that hover above the display. Unlike Shared Media, Parcel media has no means of interacting directly with the viewer, so using "&controls=0" will hide the Vimeo player scrollbars, etc. (Some Parcel media players have their own controls, *some* of which will operate the Vimeo player stream.)

Here's a little script that does what's described here for both Shared and Parcel media, using a couple different Designing Worlds episodes of personal interest.

==================
Vimeo Media Script
==================

string VIMEO_NUMBER = "427716338";
integer MEDIA_FACE = 1;
mediaError(integer num)
{
list MEDIA_ERRORS =
[ STATUS_MALFORMED_PARAMS, "Function was called with malformed parameters"
, STATUS_TYPE_MISMATCH, "Argument(s) passed to function had a type mismatch"
, STATUS_BOUNDS_ERROR, "Argument(s) passed to function had a bounds error"
, STATUS_NOT_FOUND, "Object or other item was not found"
, STATUS_NOT_SUPPORTED, "Feature not supported"
, STATUS_INTERNAL_ERROR, "An internal error occurred"
, STATUS_WHITELIST_FAILED, "Whitelist Failed"
];
llWhisper(DEBUG_CHANNEL, "Shared media error encountered: "
+ llList2String(MEDIA_ERRORS, llListFindList(MEDIA_ERRORS, [num])+1));
}
default
{
state_entry()
{
integer errNum = llClearLinkMedia(LINK_THIS, MEDIA_FACE);
if (errNum)
{
mediaError(errNum);
return;
}
string html = "https://player.vimeo.com/video/"
+ VIMEO_NUMBER
+ "?autoplay=1&loop=1";?
llScaleTexture(1.0, 0.5625, MEDIA_FACE);
errNum = llSetLinkMedia(LINK_THIS, MEDIA_FACE,
[ PRIM_MEDIA_AUTO_PLAY, TRUE
, PRIM_MEDIA_CURRENT_URL, html
, PRIM_MEDIA_HOME_URL, html
, PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE
]);
if (errNum)
mediaError(errNum);
// parcel
// return; // to prevent setting PARCEL media
html = "https://player.vimeo.com/video/"
+ "375405889"
+ "?autoplay=1&loop=1&controls=0&transparent=0";
llParcelMediaCommandList([ PARCEL_MEDIA_COMMAND_STOP]);
llParcelMediaCommandList([ PARCEL_MEDIA_COMMAND_UNLOAD]);
llParcelMediaCommandList(
[ PARCEL_MEDIA_COMMAND_URL, html
, PARCEL_MEDIA_COMMAND_TYPE, "text/html"
]);
llParcelMediaCommandList([ PARCEL_MEDIA_COMMAND_PLAY]);
}
}


==================

All the scripted operations can be done manually, too, and may work in existing in-world media players. Note that a parcel media script must be owned by the landowner, so on group owned land it must be deeded to the group. And remember that you'll need a viewer supporting the CEF updates to actually watch the videos.
Reporter Qie Niangao
20200708

Sit a spell. Or don't

A recent scripting forum post asked for help with Second Life objects we sit on, and listed examples: a prize-awarding chair, a dance pole, and a teleporter. That list reminded me how often in SL we use a "sit" interaction to trigger some wholly unrelated event -- nothing about conventional "sitting" at all -- because scripts get these handy artefacts of the avatar being on a scripted seat assembly.

Teleporting, for example. Scripters have several variants of "sit teleporters" but anybody new to SL must wonder why ever they should "sit" on a thing in order to be transported somewhere else. After a while we get so used to it that we worry that, instead, walk-thru (Experience) portals might "break immersion" -- as if being transported by sitting is somehow more intuitive. We can disguise the "sittingness" of the interaction with llSetSitText() so the right-click menu shows something like "Teleport" instead of "Sit here" but still it involves an explicit right-click gesture. Or, we could make the teleport trigger with a left click with a llSetClickAction of _SIT, but that makes a mouse cursor of a chair -- hardly universal symbol for teleportation!

I have no idea why a prize chair should be a chair. Somebody scripted one that way once, I guess. Maybe it gives a hint of exclusion to others who'd want to claim the prize? Because the sitter is occupying the space while the prize is distributed, so nobody else confuses the process? From the prizewinner's point of view, though, the chair is just weird. Analogue of a throne for coronation? Seems a stretch.

Even though dancing isn't much like sitting, the dance pole is the LEAST artificial of the examples. The real reason to "sit" on a dancepole is to lock the dance animation to the location of a static prop (the pole). Again, using "Dance!" as the sit text makes it a little more intuitive, but a chair -ClickAction mouse cursor is no more evocative of poledancing than of teleporting.

Builders and scripts - particularly those developing with an Experience target - might consider adopting, whenever possible, some more natural interaction, even if it needs to secretly trigger a forced, scripted "sit" behind the scenes, and reserve explicit "sitting" for those special occasions when the user is literally choosing a place to play a real sit animation.
Reporter Qie Niangao
20200605

Geek To The EEP

In an outdoor photo, you can see the weather. In a film, you can FEEL the weather. That's because the dynamic medium represents how the weather CHANGES environmental cues: Clouds move in and the sky darkens, lighting changes, the sea gets heavy.

What if we could give our visitors that feeling in an intentional, _directed_ series of environmental settings? Immerse them in a little program of weather dynamics?

Below is a sample Experience script that demonstrates features of the new Environment Enhancement Project (EEP), to provide just that kind of weather-changing experience, individually for each visitor. As configured, it applies a sequence of four Library environmental "sky" assets to make the weather seem progressively more threatening over about a minute and a half after arrival. (In real use, you might choose longer transition intervals to stretch this out more realistically. And of course you can use other EEP environments in any sequence - perhaps a cheery brightening of the sky as clouds lift.)


/*
Released into Public Domain, 2020, Qie Niangao for Bay City Post
Give visitors an enhanced experience with a SERIES of Environments
So clouds can build, sky can darken, seas roil more over time - or vice versa
(sample Environments in ENV_STAGES below are super primitive, just to demo the concept)
This basic script uses simple passage of time to trigger next stage
Make sure parcel (at least) has the script's Experience enabled
*/

//== USER SPECIFIED CONSTANTS =======================

integer AGENT_SCOPE = AGENT_LIST_PARCEL; // or AGENT_LIST_PARCEL_OWNER or AGENT_LIST_REGION
// but if they're on land without the Experience, there will be errors
list ENV_STAGES = // sequence of (SAMPLE) Environment UUIDs and durations (in seconds)
[ "d15d3fb0-7e14-4af8-203e-33b85346f3f1", 20 // "Neutral" Library sky
, "cd880f86-53c7-1a91-532c-2797f517a35f", 20 // "Daytime shadows" Library sky
, "07899451-b3d8-9f5c-563b-14b89eda481d", 30 // "Dusty" Library sky
, "2a000d12-e692-da2a-9e08-0cbda70bc0bf", 30 // "PaperSnow" Library sky
]; // These use unnaturally short durations for demo & testing)
float AVATAR_SCAN_INTERVAL = 10.0; // in seconds.
// Not a real sensor, but still some list manipulation, so keep it as long as tolerable
// A new arrival may wait UP TO this interval, so just half this time on average
integer MAX_IGNORED_AGENTS = 20; // after which, forget the oldest (prevent memory leak)
integer MAX_STARTING_AGENTS = 3; // most we'll wait to reply to permissions request (prevent leak)

//== SCRIPT-MANAGED VARIABLES =======================

list tAvStages; // avatar UUIDs in scope, index of their Env.stage, preceded by time to move through stage
list ignoredAgents; // don't spam these with more exp perm requests
key parcelID;
list agentsStarting;

debugOut(string outStr) {
// llOwnerSay(outStr);
}

default
{
state_entry()
{
if ([] == llGetExperienceDetails(NULL_KEY))
{
llWhisper(DEBUG_CHANNEL, "Script \""+llGetScriptName()+"\" is not associated with an experience. Exiting.");
return;
}
// trivial, impossible to match sensor, just set a repeating no_sensor interval:
llSensorRepeat("NO SUCH NAME", llGetKey(), AGENT, 0.01, 0.01, AVATAR_SCAN_INTERVAL);
parcelID = llList2Key(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_ID]), 0);
}
on_rez(integer start_param)
{
llResetScript();
}
no_sensor()
{
list newAgents = llGetAgentList(AGENT_SCOPE, []);
// any already-staged agents need removing?
integer oldAgentIdx = llGetListLength(tAvStages) - 2;
while (0 <= oldAgentIdx)
{
key oldAgent = llList2Key(tAvStages, oldAgentIdx);
if (-1 == llListFindList(newAgents, [oldAgent]))
tAvStages = llDeleteSubList(tAvStages, oldAgentIdx-1, oldAgentIdx+1);
oldAgentIdx -= 3;
}
// any newly-arrived agents need request to add?
integer newAgentIdx = llGetListLength(newAgents);
while (0 <= --newAgentIdx)
{
key newAgent = llList2Key(newAgents, newAgentIdx);
integer scopeIdx = llListFindList(tAvStages, [newAgent]);
if (-1 == scopeIdx) // agent not yet in list of agents with managed environments
if ((-1 == llListFindList(ignoredAgents, [newAgent])) // agent didn't deny us in past
&& (-1 == llListFindList(agentsStarting, [newAgent]))) // and not already being invited
{
if (!llAgentInExperience(newAgent)) // agent new to this Experience: greet & invite
llRegionSayTo(newAgent, 0, "Greetings "+llGetDisplayName(newAgent)
+" and welcome! You're being invited to an Experience, \""
+ llList2String(llGetExperienceDetails(NULL_KEY), 0) // script's Experience name
+"\" that demonstrates dynamic personal Environment settings."
+" We hope you'll agree to participate."
+" (If you don't grant permissions, this script will try not to ask again for a while.)");
agentsStarting += newAgent;
if (MAX_STARTING_AGENTS < llGetListLength(agentsStarting))
agentsStarting = llList2List(agentsStarting, 1, -1);
llRequestExperiencePermissions(newAgent, "");
}
}
}
experience_permissions_denied(key agentId, integer reason)
{
integer agentStartingIdx = llListFindList(agentsStarting, [agentId]);
if (-1 != agentStartingIdx)
{
if (XP_ERROR_NOT_PERMITTED == reason)
{
if (-1 == llListFindList(ignoredAgents, [agentId]))
{
ignoredAgents += agentId; // don't spam after first request
if (llGetListLength(ignoredAgents) >= MAX_IGNORED_AGENTS)
ignoredAgents = llList2List(ignoredAgents, 1, MAX_IGNORED_AGENTS);
}
}
agentsStarting = llDeleteSubList(agentsStarting, agentStartingIdx, agentStartingIdx);
}
else
if (XP_ERROR_NOT_PERMITTED_LAND != reason)
// ignore _LAND perm error because it's raised every time agent moves to non-XP parcel
llWhisper(DEBUG_CHANNEL, "Experience permissions denied, reason #"+(string)reason
+": "+llGetExperienceErrorMessage(reason));
}
experience_permissions(key agentId)
{
integer agentStartingIdx = llListFindList(agentsStarting, [agentId]);
if (-1 != agentStartingIdx)
{
tAvStages = [llGetUnixTime(), agentId, 0] + tAvStages;
llSetTimerEvent(0.1);
agentsStarting = llDeleteSubList(agentsStarting, agentStartingIdx, agentStartingIdx);
return;
}
// else handle timer-triggered requests (process the tAvStages queue)
integer now = llGetUnixTime();
if (now >= llList2Integer(tAvStages, 0))
{
key agent = llList2Key(tAvStages, 1);
integer envIdx = llList2Integer(tAvStages, 2);
key env = llList2Key(ENV_STAGES, envIdx);
integer transition = llList2Integer(ENV_STAGES, envIdx + 1);
debugOut("process env "+(string)env+" for agent "+llKey2Name(agent)
+"\n\t with tAvStages = "+llDumpList2String(tAvStages, " | "));
integer envErr = llReplaceAgentEnvironment(agent, (float)transition, env);
if (0 > envErr)
llWhisper(DEBUG_CHANNEL, "Error in llReplaceAgentEnvironment : "+(string)envErr);
envIdx += 2; // advance agent to wait for next stage
integer nextTime = now + transition;
if (envIdx >= llGetListLength(ENV_STAGES))
nextTime = 2147483647; // Final env stage should last for rest of stay (MAXINT)
tAvStages = llListReplaceList(tAvStages, [nextTime, agent, envIdx], 0, 2);
}
tAvStages = llListSort(tAvStages, 3, TRUE); // lazy
float timeTo = (float)(llList2Integer(tAvStages, 0) - now);
if (0.0 >= timeTo) timeTo = 0.1;
llSetTimerEvent(timeTo);
}
timer()
{
llSetTimerEvent(0); // set again in exp_perms event
if (llGetListLength(tAvStages)) // any Avs left?
llRequestExperiencePermissions(llList2Key(tAvStages, 1), "");
}
}

Note that Experience permissions are required by llReplaceAgentEnvironment, the EEP function that makes this possible, so the script will need to be compiled to an Experience that's enabled on the land where it runs. And visitors have to grant the permissions requested when the script invites them to participate.

At the moment, EEP is only visible in the Linden viewer, but soon it'll be supported in popular third party viewers too. Although the script sequences the effect individually for each viewer, it manipulates what's called the "Shared Environment" -- so it can be overridden by users who specify their own environment or time-of-day.

Passage of time needn't be the only thing to trigger a change of EEP environment. Should one particular room or area have different lighting? Such a script would track where visitors roam and update their environments accordingly. Or maybe trigger environments by level in an in-world game, or in response to some scripted role-play interaction.

Another possible extension: _Sound_ is powerfully evocative of weather: wind, thunder, bird song, waves, etc: llPlaySound() in a HUD is heard only by the wearer; llAttachToAvatarTemp() could transparently attach a sound-emitting HUD to experience participants.
Reporter Qie Niangao
200504

Fair Winds

Second Life sailors will have noticed that boats move according to wind patterns that vary over time and across the continents. That's possible because the Second Life simulation includes wind. In a recent Forums thread, https://community.secondlife.com/forums/topic/451524-sl-wind-variance-between-sims , we explored a bit more about how that wind simulation behaves.

Although all regions have wind that varies over time, it seems that some have wind that also varies over the region at any instant -- and some don't. The thread shows a few scripters figuring why some regions have wind that varies across the region, and it's a good example of loose but effective teamwork investigating the phenomenon. (It turns out that isolated island regions, surrounded by void, do not get the spatially-varied wind, and the ones that do are seeing patterns that propagate across region borders, covering whole continents of connected regions.)

There are several little scripts in the thread showing approaches to quick-and-dirty data collection, interspersed with digging into dusty scrolls in the Forums archive, the jira, the wiki, and old release notes.

It's not exactly Indiana Jones or a Dan Brown novel, but it kinda makes me want to dress my avatar as Holmes -- or maybe Watson.

Besides sailboats and the beautiful particle effect that introduced the thread, SL wind also affects ambient sound in the viewer, flexiprim movement, and scripted things like windsocks and weather vanes. It used to be an option to see Linden trees moving according to wind conditions but we found that was removed in early 2012 to reduce viewer lag. I've also used wind as a sort of whimsical "switch" for a space heater that turns on when it's "windy" -- the SL "weather" simulation doesn't include temperature... or anything else except wind.

There could be an opportunity here if anybody can think of a product that could benefit from a semi-randomly fluctuating property that varies over time and space.
Reporter Qie Niangao
200406

Slow Inventory Saves a Step

Perhaps you, like me, find yourself copying the contents of one object into another, for example to move a whole bunch of animations from one piece of furniture into a new one. Out of habit, I've always done this by first copying everything into my own Inventory, then back out to the other object. This is really slow and prone to failure. Some third party viewers have a way to make it a little more reliable, but it's still pretty painful.

Thanks to particularly sluggish Inventory services I recently realized that this was a silly extra step: All I really need to do is copy the contents directly, object to object. So here's a script that does that (read the comments for some caveats and options about how it works):

/*
Simple little script to copy the contents of this prim to another, identified by key in textbox
To get the key to put in that textbox, can use Firestorm's "Copy Keys" button in the Build Tool editor or drop this trivial script into the target object:

default { state_entry() { llOwnerSay((string)llGetKey()); } }

Unfortunately (and weirdly) I don't know a way for a script to get the permissions on a rezzed object, so this will try to copy to anything the toucher owns, which won't work for no-mod targets. This expects both the sending and recipient objects to be owned by the toucher.

Some special cases might make sense with other owners, but lots of ways for that to cause trouble, too. Can theoretically use something like this to give contents to an avatar
but as written it would wait 2 seconds between items. Some TPVs have built-in means of doing this reliably.

Could use llGiveInventoryList() to make it faster, but that can't work at all for no-copy,
which would mean a hybrid approach, much like product unpacking scripts.
*/

integer COPY_SCRIPTS = FALSE;

// Scripts would arrive confusingly NOT running (use TRUE to copy them anyway, including THIS script,

// but then will need to either recompile them or take target object into inventory and re-rez it.)

integer MOVE_NO_COPY_CONTENTS = FALSE;

// It could damage the sender; use TRUE to do it anyway, but that won't work if copying to an attachment

integer listenHandle;



default

{

touch_start(integer num_detected)

{

key toucher = llDetectedKey(0);

if (llGetOwner() != toucher)

{

llRegionSayTo(toucher, 0, "You need to own this object to copy its contents");

return;

}

llListenRemove(listenHandle);

integer chan = -1000000000 - (integer)llFrand(1000000000);

llListen(chan, "", toucher, "");

llTextBox(toucher, "Key (UUID) of recipient object?", chan);

}

listen(integer channel, string name, key id, string text)

{

key target = (key)text;

if (NULL_KEY == target)

llRegionSayTo(id, 0, "Sorry, that's not a valid key");

else

if (id != llGetOwnerKey(target))

llRegionSayTo(id, 0, "Sorry, you don't own that object");

else

{

integer inv = llGetInventoryNumber(INVENTORY_ALL);

while (--inv >= 0)

{

string invName = llGetInventoryName(INVENTORY_ALL, inv);

if (COPY_SCRIPTS || (INVENTORY_SCRIPT != llGetInventoryType(invName)))

{

if (! (PERM_COPY & llGetInventoryPermMask(invName, MASK_OWNER)))

if (MOVE_NO_COPY_CONTENTS)

llGiveInventory(target, invName);

else

llRegionSayTo(id, 0, "Skipping no-copy contents: "+invName);

else

llGiveInventory(target, invName);

}

}

llRegionSayTo(id, 0, llGetScriptName()+": Done");

}

}

}
Reporter Qie Niangao
200302

Hopeful: Embedded MP4s, Hopeless: Measuring visitor script times

There's excellent news from the Lab about plans to eventually support embedded MP4s. This has been a problem because many videos, notably including those hosted on Vimeo, use this format that does not work with out-of-the-box Chromium Embedded Framework, the basis of parcel and shared ("Media on a Prim") media as well as viewer browsers. Although there's no schedule yet, it's an encouraging development compared to a previous statement in the Jira that the Lab did NOT plan to support the format. (This may affect plans for hosting SL-related video content and for displaying that content.)

On an unrelated topic, there have long been devices that measure visitor scripts with the goal of controlling the lag they might cause. In a recent forums thread ( https://community.secondlife.com/forums/topic/448580-avatar-script-count-limit-on-homestead/) some of us took a deeper dive into what such devices might do to be useful, and what they very often do that is useless -- or worse. To summarize:

Scripts worn by visitors can lag other scripts but generally do not contribute to the most common forms of lag experienced by avatars in a busy region. That's because scripts are the lowest priority of sim processes, so a lot of avatars in a region will tax *other* sim resources, squeezing out scripts (and making script-on-script lag more severe). It can be annoying that scripts don't get much time to run, but that's rarely best addressed by reducing the script load itself.

That said, reducing the load of the most over-scripted visitors can improve responsiveness of other scripts (such as vendors), and griefers can take over-scripting to such an extreme that it hurts overall sim performance.

To efficiently find script offenders, what to measure? Turns out that the simplest metric -- count of running scripts -- is probably the best option. That's because, as detailed in the cited thread, both Script Memory and Script Time are actually measuring something very different from what the names imply. Script Time in particular is almost impossible to use responsibly because the more a region is having performance problems, the less accurately Script Time can be measured in the region and the longer it takes (20 minutes or more) to settle into a potentially useful metric.

Worse, the *direction* of inaccuracy is also a function of time and sim load: when an avatar first arrives in a busy region, their Script Time measure will be (hugely) *over*-estimated compared to a less busy region, but later when the measure stabilizes, the same scripts will show *lower* Script Times than when the sim is less busy. Indeed an avatar's Script Time depends less on their scripts than on the performance of the region and the time since the avatar arrived.
Reporter Qie Niangao
200206

Is A Little Progress A Dangerous Thing?

The Lab is shifting some cost burden from Land to other things of value in Second Life, increasing some fees, encouraging Premium subscriptions, etc.

One thing often mentioned is the increased capacity of land to hold builds: More land impact available per square meter means a landowner shouldn't need to own as much land, so should need to pay less tier for the same build complexity. This, in addition to the (typically) more efficient mesh compared to old prim builds means that land should be a somewhat less important cost in using Second Life, even before land fees were directly reduced (as they were in 2019, but I'm focusing on earlier land CAPACITY effects here).

Does enhanced land capacity really reduce the cost of land for typical SL use cases?

In the familiar case of residential housing, this seems to work. People who already own land won't immediately benefit from it, but new land buyers need to buy less land to hold the same amount of content. There's SOME intrinsic value to a parcel's SIZE independent of its capacity to hold content, but for housing, content capacity seems to be the main factor driving expansion, and so increased object capacity should reduce housing land cost.

In contrast, another familiar use case, the social club venue, doesn't benefit so directly. That's because land size for a club doesn't so much limit content capacity as it reflects practical avatar capacity. Many clubs need a full region just to comfortably hold their peak avatar count without needing to turn too many away.

The progress in CONTENT capacity just hasn't carried over to improved AVATAR capacity.

We've always heard that it's difficult to scale up avatar capacity. I'd understood this to be a problem for hosting thousands of avatars for a concert or other big event. What I hadn't appreciated before is that it also gets in the way of shifting away from Land the costs of using SL for such a common and important use as club ownership.

There's certainly pressure now to make avatars less demanding, and with Project ARCTAN to improve estimates of how demanding they really are. But even if by magic avatars were to be only half as laggy, it wouldn't really save land costs for clubs because it still wouldn't be practical to pack clubs any tighter on sims; at the same time, clubs are absorbing some of the increased costs, such as event listings, to which the reduced land costs shift.

This isn't to claim that SL social clubs are endangered by this shift; I have no information about that, and any effect may be dwarfed by renewed overall SL activity. Rather, I'm merely marvelling at the complex economics of shifting costs away from land, how it interacts with avatar capacity, and how that may affect the specific uses that have become important to Second Life.

Now, considering the subtlety of these gradual changes in SL, imagine the effects across entirely different platforms. One wonders which seemingly insignificant factors might make SL the "Goldilocks" virtual world.
Reporter Qie Niangao
200106

Using Second Life to "Extend the Rafters"

Anyone finding themselves near Ottawa Canada between now and 5 April 2020 may want to arrange a special trip to the National Gallery of Canada to experience "Àbadakone | Continuous Fire", a very special exhibit of international Indigenous art with a wide array of amazing works from around the world. 
Of particular relevance to Second Life is "Teiakwanahstahsontéhrha’ / We Extend the Rafters" by Kanyen'kehà:ka (Mohawk) artist Skawennati.
Central to the work is a machinima, "The Peacemaker Returns", constructed and recorded in Second Life. The entire work also includes wampum belts (deer hide, sinew, plastic beads), LED lights, and an aluminum structure.

An earlier (2017) exhibition of the work explains the title of the piece: "The bilingual title of the exhibition—in Kanien’kéha (Mohawk) and in English—refers to the action of 'extending the rafters' of a longhouse. 
These traditional Indigenous structures would be lengthened to make room for new generations or even other families." 
As shown, the "rafters" of the aluminium structure extend into the futuristic machinima itself, which also includes virtual wampum belts echoing the real beaded belts in the museum exhibit space.

The artist outlines the significance of the work and details the Second Life effort involved in its development in a fascinating background video.
Reporter+Image Qie Niangao
191202

A Hoverlogo And Other Sprites

Particles are two-dimensional images situated in 3-D space. They (except still-rare "ribbon" particles) have the special property of always facing the camera: they're "sprites" is the parlance of computer graphics. This is fine for many purposes, but in other situations can challenge immersion: there's no real world analogue of a surface that's always facing you regardless of how your view moves around it.

Well, there's the optical illusion of eyes in paintings such as the Mona Lisa that appear to follow as the viewer moves around the room, but particle sprites really do face the cam.

That's similar to hovertext which, too, is always drawn facing the cam; one big difference is that hovertext doesn't scale with distance from the emitter whereas particles do.

In SL, things that aren't realistic may break immersion -- or they may appear magical. Here we have what I prefer to see as magical: a "hoverlogo" that always shows the circular logo image head-on, regardless of whence it's viewed:

default
{
state_entry()
{
llParticleSystem(
[ PSYS_PART_FLAGS, 0
| PSYS_PART_EMISSIVE_MASK
| PSYS_PART_FOLLOW_SRC_MASK
, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP
, PSYS_SRC_TEXTURE, "422ed2fa-482d-b9fd-1bd7-eef1611a1dbf"
, PSYS_PART_START_SCALE, <0.5, 0.5 , 0.0>
, PSYS_PART_MAX_AGE, 30.0
, PSYS_SRC_BURST_RATE, 3.0
]);
llSetText( "Visit the\nBay City Post", <1.0, 1.0, 1.0>, 1.0);
}

touch_start(integer total_number)
{
llLoadURL(llDetectedKey(0), "Visit the Bay City Post online",
"https://baycitypostsl.blogspot.com/");
}
}

This example is intended to go inside a fully transparent 0.5 m sphere so it can be activated by touch. Of course for another logo you'll want to change the texture UUID and hovertext as well as the prompting text and URL that's loaded when touched. If no touch-response is desired, the particle system will continue to show the logo after the script is removed, saving a tiny bit of simulator resources.

I've also used this approach to create an analogue "hoverclock" that shows the time from all sides, perched on top of teleport domes on major Virtual Railway Consortium sites around the SLRR. (The reader is invited to ponder the mystery of how the hands move around the face of those hoverclocks.)
Reporter Qie Niangao
191104

Off to the Races (and back again)

Programmers are familiar with a kind of system bug called a race condition. This is when events happen in an unexpected order for which the system isn't equipped. Some Lindens recently spent a very late Friday night dealing with such a problem.

After the weekly server update, rolling a Release Candidate version to the main server channel, scripters started seeing reports of missed messages between scripted objects and items those scripts rez. (A hypothetical example might be a custom-scripted chair that rezzes a table and then tells the table to color itself to match the current chair color.)

After some sleuthing they figured out that the rezzing scripts were trying to send messages before the rezzed objects' scripts were ready to receive them. But this problem hadn't happened before.

This was NOT because the recipients were slower getting ready, but rather because the senders were unusually quick. That was because they get notified when their rezzed item is indeed rezzed -- which used to coincide with it being ready to receive messages. Not anymore.

In fact, that sequence was never guaranteed. It just worked that way. For years. In a lot of scripts.

Working to speed up script processing, the Lab had streamlined notification of successful rezzing - a good thing, except some user scripts were unintentionally relying on that being no faster than it always had been.

To restore functionality to those scripts, Lindens rolled back the entire grid to the old server software while they work on a change to delay that specific notification until the recipient scripts are ready to get messages. That will make predictable the sequence of these events, removing risk of this particular race condition in user scripts.

Streamlining script processing could have other unexpected effects, as might other speed improvements. Sim crossings are particularly sensitive to timing, for example, so they could be affected -- for better or worse -- by subtle changes in the speed of script processing or by future timing changes as Second Life sims move to the cloud.

Such race conditions are notoriously hard to diagnose and fix. Even more so when the "race" is in user scripts supported by a changing platform.
Reporter Qie Niangao
191007

Experiencing a Bug, and BoM Obscurities

Some folks with their own Experience scripts may have noticed a particularly nasty bug that's a real head-scratcher when first encountered. It seems that with some recent server update, all rezzed-in-world scripts compiled to any Experience lost record that they were compiled to that Experience. The scripts keep running using the Experience for which they were compiled, but they can't be recompiled (nor shift-drag copied, etc.) unless re-introduced to that Experience.

Turns out this doesn't really affect that many end-user scripts because most folks just use whatever Experiences are already compiled, and a lot of Experience scripts (such as the AVsitter auto-attach scripts I often praise) are usually embedded in object inventories, not rezzed-out on their own, so they weren't affected.

New objects don't seem to be affected now, but currently it's not clear whether there's any way for the Lab to fix the already damaged objects. If you're interested you can watch developments at https://jira.secondlife.com/browse/BUG-227526 for updates.
Much wider attention is on the new "Bakes on Mesh" (BoM) feature for attached mesh bodies and heads. This enables direct wearing of skin-tight textures and alpha masks, just as the old system avatars used, rather than the much more complex and limited "applier" scripts and "alpha cuts" HUDs that mesh attachments needed until now.

Much has been said about BoM already, but some things I think have been under-reported. First, in addition to the oft-cited improvement in rendering efficiency, the feature overcomes a constant source of user frustration and support problems: the unpredictable interaction of multiple layers of blended alpha textures. This comes up all the time when people try to wear makeup and tattoos, say, on overlapping layers of their attached mesh. All those problems go away when those overlapping transparent layers are simply baked on top of each other into a single displayed surface texture.

This also completely eliminates a problem inherent to how Advanced Lighting is implemented, limiting how many projected light sources affect a blended alpha surface. If you've ever noticed make-up, for example, looking weirdly coloured depending on where it is in a scene with complicated lighting, you've seen the problem -- which you WON'T see anymore with baked-on textures.

Finally, a note about BoM and Materials: if creators of BoM skin, make-up, tattoos, clothing, etc. want to include "Materials" effects, they should simply supply the normalmaps and/or specularmaps directly with their products, so folks can manually compose their combined normal- and specularmaps for the baked surface layer. These assets have zero value as "stolen" content, so the creator loses nothing by doing so, however much they may have been trained to "protect IP" at all cost. Mesh avatar makers understand this and provide a way to apply those manually composed material maps (at least this applies to Slink, which was "first out the gate" with BoM so I could test the existing built-in Materials applier on the skin layer, and a single-layer Omega applier for Materials will also work).

The reason this process still involves manual composition is that there's no simple rule for combining the "bumpiness" of normalmaps layered on top of one another. An under-shirt might be tight enough to reveal skin-layer bumpiness, but its own ribbed collar may not be revealed under a tight jacket, for example. It's still up to the user to decide which bumps should show through, and that means combining the normalmaps individually. (Most end-users may not want to do this themselves but creators of quality assembled outfits will surely follow this process for their customers.)
Reporter Qie Niangao
190902

Calendar



Park Plaza Hotel

Park Plaza Hotel
Please visit our Sponsor!

Luxury Living in Bay City


Park Plaza residents enjoy beautiful views, fine dining, and a roof top patio with hot tub; all this near route 66, and within walking distance to Hairy Hippo Fun Land and aquarium. Rates start at 55L a week for studios, and 185L a week for full size apartments. Free furnishings available. Contact Roc Plutonium for more information!

Archive