<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.secondlife.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Wulfie+Reanimator</id>
	<title>Second Life Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.secondlife.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Wulfie+Reanimator"/>
	<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/wiki/Special:Contributions/Wulfie_Reanimator"/>
	<updated>2026-06-14T07:12:25Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Project_Bento_Resources_and_Information&amp;diff=1218837</id>
		<title>Project Bento Resources and Information</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Project_Bento_Resources_and_Information&amp;diff=1218837"/>
		<updated>2026-05-13T19:14:21Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added a link to updated rigs that can be used with base Blender. Removed outdated note about editing the wiki.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{TOCright}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Welcome to Project Bento! As we mentioned in [https://community.secondlife.com/t5/Featured-News/Introducing-Project-Bento-New-Bones-Added-to-Second-Life-Avatar/ba-p/2987206 our announcement], Project Bento is adding new bones and attachment points to the avatar skeleton.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Project Bento Testing is now Complete!  Project Bento is now Live in all regions on all viewers!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== What is Project Bento?? ==&lt;br /&gt;
&lt;br /&gt;
Project Bento is an update to the Second Life Avatar Skeleton which is what allows Second Life Avatars to move and come to life, as well as enabling the creation of more lifelike and intricate rigged mesh attachments.&lt;br /&gt;
&lt;br /&gt;
===Bone Updates===&lt;br /&gt;
&lt;br /&gt;
The Project Bento Update added bones to the skeleton to expand rigging and animation support in the following areas:&lt;br /&gt;
&lt;br /&gt;
* Facial Area (new)&lt;br /&gt;
* Spine (additional bones)&lt;br /&gt;
* Hands (new finger bones)&lt;br /&gt;
* Tail (new)&lt;br /&gt;
* Wings (new)&lt;br /&gt;
* Hind Legs for Quadruped Avatars (new)&lt;br /&gt;
&lt;br /&gt;
A full list of the alterations for the skeleton are [[Project Bento Skeleton Guide|here]].&lt;br /&gt;
&lt;br /&gt;
===Slider System / Shape Updates===&lt;br /&gt;
Additionally Project Bento updated the existing avatar shape system to make the adjustment sliders affect these new bones on top of it&#039;s previous (unaltered) shape control functionality.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scope of slider interactions&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
While the updates to the slider system made it so that these new bones move with sliders, it does not override, or affect in any way existing behavior on the default Linden Avatar head and hands.  The default Linden Avatar depends on &#039;&#039;morph targets&#039;&#039; to perform hand poses and facial expressions.  This is a completely different system from using &#039;&#039;bone based rigging&#039;&#039; on mesh attachments worn in the same regions of the body (which is what Project Bento adds support for).  While these two systems are affected &#039;&#039;similarly&#039;&#039; by slider manipulation by design, it is good to keep in mind that it is an approximation, and only that.  It is fundamentally impossible to exactly mimic the motion of a morph target based system with a bone based rigging system.  For example : sliders will affect a mesh attachment head differently than it affects the Linden Avatar head.  This is unavoidable due to how both systems differ mechanically.&lt;br /&gt;
&lt;br /&gt;
===Animation File Updates===&lt;br /&gt;
On top of additional bones and slider integration, Project Bento also increased the maximum allowable size of animation files, and also removed the restriction on BVH animation file uploads that prevented translation based bone movement.&lt;br /&gt;
&lt;br /&gt;
===Partial Skeletons===&lt;br /&gt;
Upload of rigged content no longer needs all the bones specified in the DAE file for convenience purposes. For rigged content, simply omitting unused/missing bones from the skinning data is now allowed.&lt;br /&gt;
&lt;br /&gt;
== Caveats &amp;amp; Best Practices ==&lt;br /&gt;
&lt;br /&gt;
*Due to the restrictions of the pre-existing Second Life Animation System. Playing animations that affect bone scale will (continue to) not work with Project Bento, however the slider system has been incorporated to adjust bone positions and bone scale.&lt;br /&gt;
*Any slider controls that affect the position ( translation ) of a specific bone will be overridden if the user plays an animation that moves that bone.  Since presently the animation system has no means of knowing the positional offset that the slider is providing, that animation will be played based off of the bones natural ( pre-slider adjustment ) position.  However the slider&#039;s influence on the affected bones scale will not be altered, as animations presently cannot affect bone scale.&lt;br /&gt;
&lt;br /&gt;
Because of the above two restrictions it is ill advised, to mix content where you want the translation based sliders to work with translation based animations. Because the animation will always override the slider.&lt;br /&gt;
For a list of all the bones that are influenced by sliders that adjust their position refer to this : https://wiki.secondlife.com/wiki/Project_Bento_Skeleton_Guide#Bones_Currently_Affected_By_Positional_Sliders&lt;br /&gt;
&lt;br /&gt;
This was the best practical alternative to simply not allowing translation based animations at all due to the possibility of them overriding sliders.   The community asked for the ability to have translation/position based animations available for greater creative control and it was agreed upon that suffering the confusion from this conflict of behavior was still an overall gain.&lt;br /&gt;
&lt;br /&gt;
Prior to Project Bento, rigging to attachment points was never formally supported, and was strongly discouraged, as once an animation stops playing it often leaves the attachment points in a deformed location that is very difficult for a resident to understand.  With the addition of Project Bento, meshes rigged to Attachment points &#039;&#039;may be rejected by the server&#039;&#039;  since one of the primary reasons for the addition of these new bones was to discourage this process.&lt;br /&gt;
&lt;br /&gt;
== Current Content &amp;amp; Resources ==&lt;br /&gt;
&lt;br /&gt;
=== Models &amp;amp; Skeleton Files / Test Content ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Modern glTF (glb) and Collada (dae) files&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
This model was created with compatibility for Blender 5 and 4.5 (the last version to include Collada export).&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/LittleBitwise/Second-Life-Skeleton human female] (Github repository)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The bones in the glb file are aligned differently than the ones in the dae file! Use the file that matches the filetype you&#039;re going to export.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Collada (dae) files&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
The following model files are compatible with the old project viewer, as of 2016-07-14, and should work with currently implemented sliders. Includes weighting to the new mFaceJawShaper bone.&lt;br /&gt;
&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/models/collada/female_2016_08_05.dae.zip human female] (August 5, 2016) (attention, this rig is broken)&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/models/collada/male_2016_08_05.dae.zip human male] (August 5, 2016)&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/models/collada/angel_2016_08_05.dae.zip angel] (August 5, 2016)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Maya (ma) files&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
These are also current with the skeleton of July 14, 2016:&lt;br /&gt;
&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/models/maya/MayaBentoFemaleAugust2016.ma.zip maya human female] (August 17, 2016)&lt;br /&gt;
(NOTE: Standard Skeleton with bone located in standard positions determined by the avatar_skeleton.xml file.)&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/models/maya/MayaBentoMaleAugust2016.ma.zip maya human male] (August 17, 2016)&lt;br /&gt;
(NOTE: Standard Skeleton with bone located in standard positions determined by the avatar_skeleton.xml file.)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FBX files&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/models/fbx/FBXBentoFemaleSeptember2016.fbx fbx human female] (September 10, 2016)&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/models/fbx/FBXBentoMaleSeptember2016.fbx fbx human male] (September 10, 2016)&lt;br /&gt;
&lt;br /&gt;
These are based on the Maya files above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3ds Max (max) files&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
All files are saved as 3dsmax2012 files, if your version predates this, they probably won&#039;t load.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Basic&amp;quot; skeleton files contain no additional nodes and will not work for advanced control rigs and animation export, but are simpler, contain less clutter and might be ideal for simple rigging&amp;amp;skinning work.  However bone rotation axis will not behave ideally for animations.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Normal/Animation&amp;quot; Rigs have additional nodes for ease of use with animations, but animation must be done on &amp;quot;mBoneName_DRV&amp;quot; point helper nodes instead of the bones directly. These skeleton files have nodes oriented in a manner that will make joints rotate on their local axis.  This is particularly useful for mimicing animation in parts of the bento skeleton such as the hands and face where the bone is not aligned with an axis ( perpendicular to other things in the scene ).  Hopefully these skeleton rigs will help understanding of the new joints.&lt;br /&gt;
&lt;br /&gt;
For both skeletons ( both &amp;quot;basic&amp;quot; and &amp;quot;animation&amp;quot; ) the mesh skinning and rigging should be done to the mBoneName or &amp;quot;collision volume&amp;quot; (fitmesh) bones / dummies only. All skeletons are default (female) bone positions ONLY.&lt;br /&gt;
&lt;br /&gt;
* [https://bitbucket.org/polysail/bento-max2012-skeletons/raw/bc20e9c13a37b6d834ce92b63339f911f08c2e5b/BentoBones_BonesOnly_2012.max basic skeleton bone nodes] (December 26, 2016)&lt;br /&gt;
* [https://bitbucket.org/polysail/bento-max2012-skeletons/raw/bc20e9c13a37b6d834ce92b63339f911f08c2e5b/BentoBones_DummyNodesOnly_2012.max basic skeleton dummy nodes] (December 26, 2016)&lt;br /&gt;
* [https://bitbucket.org/polysail/bento-max2012-skeletons/raw/bc20e9c13a37b6d834ce92b63339f911f08c2e5b/LocalAnimationOrientation_Bones_2012.max skeleton bone nodes] (December 26, 2016)&lt;br /&gt;
* [https://bitbucket.org/polysail/bento-max2012-skeletons/raw/bc20e9c13a37b6d834ce92b63339f911f08c2e5b/LocalAnimationOrientation_DummyNodes_2012.max skeleton dummy nodes] (December 26, 2016)&lt;br /&gt;
&lt;br /&gt;
Dec, 2016 update fixes minor rotation issues in the collision volume bones that existed in the initial August 2016 posting.&lt;br /&gt;
&lt;br /&gt;
=== Test Animations ===&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/animations/anim/bento_randomize_bones_and_attachments.anim scramble the positions] of all bones and attachments&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/animations/anim/bento_reset_bones_and_attachments.anim reset the positions] of all bones and attachments to the default.&lt;br /&gt;
&lt;br /&gt;
=== Animation Export Tools ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Maya&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The following tools are used for export of BVH animation files from Autodesk Maya Software.  The exporter was authored by Aura Linden and is available for free below.&lt;br /&gt;
&lt;br /&gt;
It works ideally with the skeleton included in [https://marketplace.secondlife.com/p/MayaStar-Plugin-System-for-Maya-Autodesk/5454253 Mayastar], maintained by Cathy Foil.  But should be able to be used as a standalone script as well.&lt;br /&gt;
&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/tools/maya/Maya_BVH_Export_Bento_Project.mel Maya BVH Animation Exporter] - mel script --&#039;&#039;&#039;Note: when animating in Maya for export, orient your skeleton in Y+ up , X+ forward orientation.&#039;&#039;&#039;&lt;br /&gt;
* [https://bitbucket.org/lindenlab/secondlife-creator-resources/raw/master/tools/maya/maya_bvh_export_instructions.txt Maya BVH exporter instructions] - text file&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Blender&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
While blender does not have any freely available animation exporters specifically for Second Life, it has an excellently maintained plugin package known as [https://blog.machinimatrix.org/avastar/ Avastar] maintained by Gaia Clary.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3DSMax&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Autodesk 3D Studio Max presently does not have any publicly available animation exporters.  However, polysail resident is presently working on a plugin that will do so.  Completion date is TBD.&lt;br /&gt;
&lt;br /&gt;
=== Reference ===&lt;br /&gt;
&lt;br /&gt;
* Some helpful models to show the locations of various types of bones can be found at [https://marketplace.secondlife.com/p/Avatar-Testing-and-Visualization-Kit/9842033 Avatar Testing and Visualization Kit]&lt;br /&gt;
&lt;br /&gt;
== Scale Locking Option ==&lt;br /&gt;
&lt;br /&gt;
With Project Bento, the option to have an uploaded mesh attachment completely ignore all slider input was also added.&lt;br /&gt;
&lt;br /&gt;
When you upload a model beneath the standard &amp;quot;Include skin weight&amp;quot; and &amp;quot;Include joint positions&amp;quot; check boxes, there is now an additional check box for &amp;quot;Lock scale if joint position defined&amp;quot;. If you check this box, then any joint that has a position defined will also have its scale locked. Scale locking will be enforced whenever a joint would normally be scaled by a slider thus &amp;quot;immunizing&amp;quot; it to all slider influence.&lt;br /&gt;
To make a bone truly slider-proof, you will have to pay attention to the joint hierarchy. Any change to the scale of a parent bone will also cause the child bone to move, so you will have to make sure the bone&#039;s ancestors also have joint positions defined. A joint position will be ignored unless it differs from the bone&#039;s default location by at least 0.1 mm (ie, 0.0001 m in the units used by the skeleton definition file). If you want to lock a joint without changing its position, use a very small offset that will not be large enough to have a visual effect (some fraction of a mm).&lt;br /&gt;
&lt;br /&gt;
This feature was primarily included to assist with making &amp;quot;animated attachments&amp;quot; that re-purpose bone sets, such as the hind legs, wings or tail into pets or weapon attachments.  Ideally such attachments would not want slider influences due to the fact that the bones comprising them are &amp;quot;borrowed&amp;quot; from another part of the body.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Reset Skeleton Menu Option [Viewer]==&lt;br /&gt;
With the addition of Project Bento a new menu option was added to the viewer.  If you Right-Click another avatar the option to &amp;quot;&#039;&#039;Reset Skeleton&#039;&#039;&amp;quot; will appear, along with it&#039;s cousin &amp;quot;&#039;&#039;Reset Skeleton and Animations&#039;&#039;&amp;quot;.  This added functionality provides any resident the ability to attempt to visually fix another avatar that looks deformed on their screen.&lt;br /&gt;
&lt;br /&gt;
  &#039;&#039;&#039;Note this option does not send any data to the server.  This means that if you use &amp;quot;Reset Skeleton&amp;quot; on a deformed avatar ( including yourself ) that currently does NOT perform this action for everyone else viewing the deformed avatar.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This was done because visibly deformed avatars aren&#039;t always deformed universally.  The logic for why this is is long and complex, involving the mechanics of how avatars are loaded.  This feature isn&#039;t a fix-all, but is a great alternative to completely logging out of the client and reconnecting, which, prior to this update, used to be the only reliable way to resolve this sort of issue.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==I found a problem? How can I give feedback?==&lt;br /&gt;
&lt;br /&gt;
As always if you discover an undocumented bug please report it at [https://feedback.secondlife.com Second Life Feedback Portal] with the tag [BENTO]  in the summary line for it to be properly addressed!&lt;br /&gt;
&lt;br /&gt;
If you have a display problem, please test to see if there is a difference with with hardware skinning Enabled and Disabled&lt;br /&gt;
:Me-&amp;gt;Preferences-&amp;gt;Graphics-&amp;gt;Advanced-&amp;gt;Avatar Rendering-&amp;gt;Hardware skinning checkbox&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Touch_end&amp;diff=1218823</id>
		<title>Touch end</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Touch_end&amp;diff=1218823"/>
		<updated>2026-04-30T07:13:26Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added new example (moved from Touch)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Event|event_id=4|event_delay|event=touch_end&lt;br /&gt;
|p1_type=integer|p1_name=num_detected|p1_desc&lt;br /&gt;
|event_desc=Triggered when agent stops clicking on task&lt;br /&gt;
|constants&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=* If a prim [[face]] has [[Navigating Shared Media|Shared Media]] enabled and the avatar&#039;s viewer supports this feature, LSL scripts will not detect touches on that face. Touches from older clients will be detected.&lt;br /&gt;
* Rigged mesh attachments do not support touch events, this is because you can steer your avatar/camera by click dragging your avatar body which is what the rigged mesh replaces. The only way to get a touch event on a rigged mesh attachment is via the right click context menu and clicking the &amp;quot;Touch&amp;quot; button.&lt;br /&gt;
|examples=You can use index 0 through (&amp;lt;code&amp;gt;num_detected&amp;lt;/code&amp;gt;-1) with the various llDetected... functions to handle simultaneous touches at once.&lt;br /&gt;
&lt;br /&gt;
For most purposes, it&#039;s usually enough to handle just with the first toucher e.g. [[llDetectedKey]](0). It is rare (but not impossible) for &amp;lt;code&amp;gt;num_detected&amp;lt;/code&amp;gt; to be greater than 1.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        llResetTime();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    touch_end(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        llInstantMessage(llDetectedKey(0), &amp;quot;You held the mouse button down for &amp;quot; + (string) llGetTime() + &amp;quot; seconds&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This next example demonstrates detecting when the owner of the object clicks-and-holds on the object for 1 second in order perhaps to access a management menu or similar, Normal brief clicks are distinguished.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        llResetTime(); // Set llGetTime to 0&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    touch_end(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        // Check how much time elapsed since touch_start&lt;br /&gt;
        if (llGetTime() &amp;lt;= 1.0)&lt;br /&gt;
        {&lt;br /&gt;
            // The user did a normal quick click on the object&lt;br /&gt;
            // Execute actions for normal clicks...&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // The owner has touched this object for longer than 1 second&lt;br /&gt;
            // Execute some special feature such as issuing a management dialog...&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|notes=*If using a touch to change states be careful about the touch_ event order. &#039;&#039;&#039;The best advice is NOT to do state changes from within touch_start.&#039;&#039;&#039; Use touch_end and do the state change there. Changing state from within touch_start can cause the next occurrence of THAT touch_start code to be missed.  &lt;br /&gt;
&lt;br /&gt;
*On clicking a prim with touch events we trigger touch_start (on first contact), touch (during) and touch_end (as released). &lt;br /&gt;
&lt;br /&gt;
|helpers&lt;br /&gt;
|also_header&lt;br /&gt;
|also_events=&lt;br /&gt;
{{LSL DefineRow||[[touch_start]]|}}&lt;br /&gt;
{{LSL DefineRow||[[touch]]|}}&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llPassTouches]]|}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|also_footer&lt;br /&gt;
|notes&lt;br /&gt;
|mode&lt;br /&gt;
|deprecated&lt;br /&gt;
|cat1=Touch&lt;br /&gt;
|cat2=Detected&lt;br /&gt;
|cat3=Grab&lt;br /&gt;
|cat4=Passable&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Touch_start&amp;diff=1218822</id>
		<title>Touch start</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Touch_start&amp;diff=1218822"/>
		<updated>2026-04-30T07:06:11Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added new example (moved from Touch)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Event|event_id=2|event_delay|event=touch_start&lt;br /&gt;
|p1_type=integer&lt;br /&gt;
|p1_name=num_detected&lt;br /&gt;
|p1_desc=Number of agents detected touching during the last clock cycle&lt;br /&gt;
|event_desc=Triggered by the start of agent clicking on task&lt;br /&gt;
|constants&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=* If a prim [[face]] has [[Navigating Shared Media|Shared Media]] enabled and the avatar&#039;s viewer supports this feature, LSL scripts will not detect touches on that face. Touches from older clients will be detected.&lt;br /&gt;
* The default behavior is: If you have a multi-prim object and the root has a [[touch_start]] handler AND one or more child prims has a [[touch_start]] handler, the root prim&#039;s handler will be called when it (the root) or a child without a handler is touched. If you touch a child prim that has a [[touch_start]] handler, it will receive the event and the root prim will not.&lt;br /&gt;
** This behavior can be configured with [[llPassTouches]]. After configuring a prim&#039;s touch-passing behavior you can delete the script that configured the prim.&lt;br /&gt;
* Rigged mesh attachments do not support touch events, this is because you can steer your avatar/camera by click dragging your avatar body which is what the rigged mesh replaces. The only way to get a touch event on a rigged mesh attachment is via the right click context menu and clicking the &amp;quot;Touch&amp;quot; button.&lt;br /&gt;
* [https://feedback.secondlife.com/scripting-bugs/p/touch-start-only-triggers-on-the-second-touch-if-touch-is-already-active Due to a known issue], if a resident is already touching a scripted object, [[touch_start]] runs only every other time that other residents touch it.&lt;br /&gt;
|examples=You can use index 0 through (&amp;lt;code&amp;gt;num_detected&amp;lt;/code&amp;gt;-1) with the various llDetected... functions to handle simultaneous touches at once.&lt;br /&gt;
&lt;br /&gt;
For most purposes, it&#039;s usually enough to handle just with the first toucher e.g. [[llDetectedKey]](0). It is rare (but not impossible) for &amp;lt;code&amp;gt;num_detected&amp;lt;/code&amp;gt; to be greater than 1.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{ &lt;br /&gt;
    touch_start(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        key    avatarKey  = llDetectedKey(0);&lt;br /&gt;
        string avatarName = llDetectedName(0);&lt;br /&gt;
&lt;br /&gt;
        llInstantMessage(avatarKey, &amp;quot;Hello &amp;quot; + avatarName );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This next example demonstrates detecting when the owner of the object clicks-and-holds on the object for 1 second in order perhaps to access a management menu or similar, Normal brief clicks are distinguished.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        llResetTime(); // Set llGetTime to 0&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    touch_end(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        // Check how much time elapsed since touch_start&lt;br /&gt;
        if (llGetTime() &amp;lt;= 1.0)&lt;br /&gt;
        {&lt;br /&gt;
            // The user did a normal quick click on the object&lt;br /&gt;
            // Execute actions for normal clicks...&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // The owner has touched this object for longer than 1 second&lt;br /&gt;
            // Execute some special feature such as issuing a management dialog...&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_header&lt;br /&gt;
|also_events=&lt;br /&gt;
{{LSL DefineRow||[[touch]]|}}&lt;br /&gt;
{{LSL DefineRow||[[touch_end]]|}}&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llSetTouchText]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llPassTouches]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llDetectedTouchFace]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llDetectedTouchST]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llDetectedTouchUV]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llDetectedTouchPos]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llDetectedTouchNormal]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llDetectedTouchBinormal]]|}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|also_footer&lt;br /&gt;
|notes=*If using a touch to change states be careful about the touch event order. &#039;&#039;&#039;The best advice is not to do state changes from within touch_start.&#039;&#039;&#039; Add a [[touch_end]] handler and do the state change there. Changing state from within [[touch_start]] can cause the next occurrence of THIS [[touch_start]] code to be missed.  &lt;br /&gt;
&lt;br /&gt;
*On clicking a prim with touch event handlers, the following handlers are triggered: [[touch_start]] (on first contact), [[touch]] (during) and [[touch_end]] (as released). &lt;br /&gt;
|mode&lt;br /&gt;
|deprecated&lt;br /&gt;
|cat1=Touch&lt;br /&gt;
|cat2=Detected&lt;br /&gt;
|cat3=Grab&lt;br /&gt;
|cat4=Passable&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Touch&amp;diff=1218821</id>
		<title>Touch</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Touch&amp;diff=1218821"/>
		<updated>2026-04-30T07:03:19Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added new example, moving another to touch_start instead...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Event|event_id=3|event_delay|event=touch&lt;br /&gt;
|p1_type=integer|p1_name=num_detected|p1_desc&lt;br /&gt;
|event_desc=Triggered whilst an agent is clicking the task. It will continue to be triggered until the the prim/object is stopped being clicked (it triggers multiple times).&lt;br /&gt;
Triggered on touch start, each minimum event delay while held, and touch end.&lt;br /&gt;
|constants&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=* If a prim [[face]] has [[Navigating Shared Media|Shared Media]] enabled and the avatar&#039;s viewer supports this feature, LSL scripts will not detect touches on that face. Touches from older clients will be detected.&lt;br /&gt;
* Rigged mesh attachments do not support touch events, this is because you can steer your avatar/camera by click dragging your avatar body which is what the rigged mesh replaces. The only way to get a touch event on a rigged mesh attachment is via the right click context menu and clicking the &amp;quot;Touch&amp;quot; button.&lt;br /&gt;
|notes=*On clicking a prim with touch events we trigger touch_start (on first contact), touch (during) and touch_end (as released). &lt;br /&gt;
&lt;br /&gt;
|examples=You can use index 0 through (&amp;lt;code&amp;gt;num_detected&amp;lt;/code&amp;gt;-1) with the various llDetected... functions to handle simultaneous touches at once.&lt;br /&gt;
&lt;br /&gt;
For example, the following script shows the names of everyone touching it (click and hold) as floating text.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
list touchers;&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    // Collect names of avatars touching the object.&lt;br /&gt;
    touch(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for (i = 0; i &amp;lt; num_detected; ++i)&lt;br /&gt;
        {&lt;br /&gt;
            string name = llDetectedName(i);&lt;br /&gt;
            if (llListFindList(touchers, [name]) == -1)&lt;br /&gt;
            {&lt;br /&gt;
                touchers += name;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        llSetText(&amp;quot;I am being touched by &amp;quot; + llList2CSV(touchers) + &amp;quot;.&amp;quot;, &amp;lt;1,1,1&amp;gt;, 1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Remove names of avatars as they let go.&lt;br /&gt;
    touch_end(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for (i = 0; i &amp;lt; num_detected; ++i)&lt;br /&gt;
        {&lt;br /&gt;
            string name = llDetectedName(i);&lt;br /&gt;
            integer index = llListFindList(touchers, [name]);&lt;br /&gt;
            touchers = llDeleteSubList(touchers, index, index);&lt;br /&gt;
        }&lt;br /&gt;
        if (llGetListLength(touchers) &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            llSetText(&amp;quot;I am being touched by &amp;quot; + llList2CSV(touchers) + &amp;quot;.&amp;quot;, &amp;lt;1,1,1&amp;gt;, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            llSetText(&amp;quot;&amp;quot;, &amp;lt;1,1,1&amp;gt;, 1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For most purposes, it&#039;s usually enough to handle just with the first toucher e.g. [[llDetectedKey]](0).&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;default&lt;br /&gt;
{&lt;br /&gt;
    touch(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        llOwnerSay(&amp;quot;I am being touched by &amp;quot; + llDetectedName(0) + &amp;quot;.&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_header&lt;br /&gt;
|also_events=&lt;br /&gt;
{{LSL DefineRow||[[touch_start]]|}}&lt;br /&gt;
{{LSL DefineRow||[[touch_end]]|}}&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llSetTouchText]]|Set the pie menu&#039;s text for touch-action}}&lt;br /&gt;
{{LSL DefineRow||[[llPassTouches]]|Allows clicks captured by a child prim to be passed to the root as well}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|also_footer&lt;br /&gt;
|notes&lt;br /&gt;
|mode&lt;br /&gt;
|deprecated&lt;br /&gt;
|cat1=Touch&lt;br /&gt;
|cat2=Detected&lt;br /&gt;
|cat3=Grab&lt;br /&gt;
|cat4=Passable&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Touch&amp;diff=1218820</id>
		<title>Touch</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Touch&amp;diff=1218820"/>
		<updated>2026-04-30T06:38:47Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Updated examples.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Event|event_id=3|event_delay|event=touch&lt;br /&gt;
|p1_type=integer|p1_name=num_detected|p1_desc&lt;br /&gt;
|event_desc=Triggered whilst an agent is clicking the task. It will continue to be triggered until the the prim/object is stopped being clicked (it triggers multiple times).&lt;br /&gt;
Triggered on touch start, each minimum event delay while held, and touch end.&lt;br /&gt;
|constants&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=* If a prim [[face]] has [[Navigating Shared Media|Shared Media]] enabled and the avatar&#039;s viewer supports this feature, LSL scripts will not detect touches on that face. Touches from older clients will be detected.&lt;br /&gt;
* Rigged mesh attachments do not support touch events, this is because you can steer your avatar/camera by click dragging your avatar body which is what the rigged mesh replaces. The only way to get a touch event on a rigged mesh attachment is via the right click context menu and clicking the &amp;quot;Touch&amp;quot; button.&lt;br /&gt;
|notes=*On clicking a prim with touch events we trigger touch_start (on first contact), touch (during) and touch_end (as released). &lt;br /&gt;
&lt;br /&gt;
|examples=You can use numbers 0 through (num_detected-1) with the various llDetected... functions to get detected agent keys etc. For most purposes, it is adequate to bother only with the first detected toucher e.g. llDetectedKey(0). It is rare (but not impossible) for num_detected to be other than 1.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;default&lt;br /&gt;
{&lt;br /&gt;
     touch(integer num_detected)&lt;br /&gt;
     {&lt;br /&gt;
          llOwnerSay(&amp;quot;I am being touched by &amp;quot; + llDetectedName(0) + &amp;quot;.&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This next example demonstrates detecting when the owner of the object clicks-and-holds on the object for 1 second in order perhaps to access a management menu or similar, Normal brief clicks are distinguished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        llResetTime(); // Set llGetTime to 0&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    touch_end(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        // Check how much time elapsed since touch_start&lt;br /&gt;
        if (llGetTime() &amp;lt;= 1.0)&lt;br /&gt;
        {&lt;br /&gt;
            // The user did a normal quick click on the object&lt;br /&gt;
            // Execute actions for normal clicks...&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // The owner has touched this object for longer than 1 second&lt;br /&gt;
            // Execute some special feature such as issuing a management dialog...&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_header&lt;br /&gt;
|also_events=&lt;br /&gt;
{{LSL DefineRow||[[touch_start]]|}}&lt;br /&gt;
{{LSL DefineRow||[[touch_end]]|}}&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llSetTouchText]]|Set the pie menu&#039;s text for touch-action}}&lt;br /&gt;
{{LSL DefineRow||[[llPassTouches]]|Allows clicks captured by a child prim to be passed to the root as well}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|also_footer&lt;br /&gt;
|notes&lt;br /&gt;
|mode&lt;br /&gt;
|deprecated&lt;br /&gt;
|cat1=Touch&lt;br /&gt;
|cat2=Detected&lt;br /&gt;
|cat3=Grab&lt;br /&gt;
|cat4=Passable&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlDetectedLinkNumber&amp;diff=1218819</id>
		<title>LlDetectedLinkNumber</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlDetectedLinkNumber&amp;diff=1218819"/>
		<updated>2026-04-30T06:28:31Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2={{Issues/SVC-2996}}{{LSL_Function/detected|number|linkNumber|error={{HoverText|0|zero}}}}&lt;br /&gt;
|func_id=40|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|func=llDetectedLinkNumber&lt;br /&gt;
|return_type=integer&lt;br /&gt;
|return_subtype=link&lt;br /&gt;
|return_subtype_disp=link_number&lt;br /&gt;
|Return_text=of the triggered event. If not supported by the event, returns zero.&lt;br /&gt;
|p1_type=integer|p1_name=number&lt;br /&gt;
|func_footnote=For {{LSLGC|Touch|touch}} and {{LSLGC|Collision|collision}} categories of events only.&lt;br /&gt;
|func_desc&lt;br /&gt;
|spec&lt;br /&gt;
|caveats&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    // Drop this script into the root prim of of a build&lt;br /&gt;
    // Touch any prim to learn its link number&lt;br /&gt;
    touch_start(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        llOwnerSay(&amp;quot;Link number clicked: &amp;quot; + (string)llDetectedLinkNumber(0));       &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llDetectedTouchFace]]|}}&lt;br /&gt;
|also_events=&lt;br /&gt;
{{LSL DefineRow||[[touch_start]]|}}&lt;br /&gt;
{{LSL DefineRow||[[touch]]|}}&lt;br /&gt;
{{LSL DefineRow||[[touch_end]]|}}&lt;br /&gt;
{{LSL DefineRow||[[collision_start]]|}}&lt;br /&gt;
{{LSL DefineRow||[[collision]]|}}&lt;br /&gt;
{{LSL DefineRow||[[collision_end]]|}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|issues&lt;br /&gt;
|notes&lt;br /&gt;
|cat1=Link&lt;br /&gt;
|cat2=Touch&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlRezObjectWithParams&amp;diff=1218755</id>
		<title>LlRezObjectWithParams</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlRezObjectWithParams&amp;diff=1218755"/>
		<updated>2026-03-31T21:04:02Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Copied rez distance section from llRezObject.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2=&lt;br /&gt;
{{LSL_Function/inventory|inventory|uuid=false|type=object}}&lt;br /&gt;
{{LSL_Function/position|pos|region=*}}&lt;br /&gt;
|func_id=|func_sleep=0.1|func_energy=200.0&lt;br /&gt;
|func=llRezObjectWithParams|sort=RezObjectWithParams&lt;br /&gt;
|return_type=key&lt;br /&gt;
|p1_type=string|p1_name=inventory&lt;br /&gt;
|p2_type=list|p2_name=params&lt;br /&gt;
|func_desc=Instantiate {{LSLP|inventory}} object at {{LSLP|pos}} with an initial set of parameters specified in {{LSLP|params}}.&lt;br /&gt;
&lt;br /&gt;
{{LSLP|pos}} will default to the position of the object containing the script, unless REZ_POS is specified. (see below)&lt;br /&gt;
|return_text=which will be the key of the object when it is successfully rezzed in the world.&lt;br /&gt;
|spec= &lt;br /&gt;
{{:LSL Constants/llRezObjectWithParams}}&lt;br /&gt;
|caveats=&lt;br /&gt;
*Silently fails to rez {{LSLP|inventory}} if REZ_POS is too far from the geometric center of the object trying to rez {{LSLP|inventory}} (generally 10 meters; see [[llRezObject]]).&lt;br /&gt;
* When scripting attachments meant to rez objects, remember that when used in the root of an ([[attach|attached]]) [[attachment]] &amp;lt;code&amp;gt;[[llGetPos]]&amp;lt;/code&amp;gt; doesn&#039;t return the position of the attachment but instead returns the position of the avatar&#039;s bounding box geometric center. Read [[llGetPos]] and [[llParticleSystem#Caveats | llParticleSystem Caveats]] for more information.&lt;br /&gt;
* If the object is unattached and the owner of the object does not have copy permission  on {{LSLP|inventory}}, the object will no longer be present in inventory after it is rezzed (so another attempt to rez (the same object) will fail); if the owner does have copy permission, then a copy is rezzed, and the original {{LSLP|inventory}} remains in inventory.&lt;br /&gt;
* If the object is attached and the owner of the object does not have copy permission  on {{LSLP|inventory}}, an error is shouted on [[DEBUG_CHANNEL]]: &amp;quot;Cannot rez no copy objects from an attached object.&amp;quot;&lt;br /&gt;
*Silently fails if you don&#039;t have offline building rights on the land. To have the right, your objects needs to &#039;&#039;either&#039;&#039;: &lt;br /&gt;
** Be on land you own yourself.&lt;br /&gt;
** Be on land where anyone is allowed to build, e.g. a sandbox. &lt;br /&gt;
** Be deeded to the group that owns the land.&lt;br /&gt;
** Be set to the same group that owns the land and the land have the parcel flag &#039;allow group to build&#039; set.&lt;br /&gt;
** The group role &amp;quot;Always allow &#039;Create Objects&#039;&amp;quot; will only work to override this when you are online, in the region, or have a child agent in the region. See [[#SVC-3145|SVC-3145]] in the Issues subsection of Deep Notes for more information.&lt;br /&gt;
* See [[object_rez]] for examples on how to establish communications between the rezzing object and the new prim.&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&lt;br /&gt;
The list of parameters is entirely optional. If you want to rez an object exactly where the rezzer is, you may call the function with an empty list.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer total_number)&lt;br /&gt;
    {&lt;br /&gt;
        llRezObjectWithParams(&amp;quot;Object&amp;quot;, []);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The below example rezzes an object slightly above the rezzer, slowly spinning and with automatic cleanup. See [[:Category:LSL Temporary|Temporary]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer total_number)&lt;br /&gt;
    {&lt;br /&gt;
        llRezObjectWithParams(&amp;quot;Object&amp;quot;, [&lt;br /&gt;
            REZ_FLAGS, REZ_FLAG_TEMP | REZ_FLAG_PHANTOM,&lt;br /&gt;
            REZ_POS, &amp;lt;0,0,1&amp;gt;, TRUE, TRUE,&lt;br /&gt;
            REZ_OMEGA, &amp;lt;0,0,1&amp;gt;, TRUE, 0.5, PI&lt;br /&gt;
        ]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The below is a basic example of firing a typical bullet from a worn attachment. The bullet is fired with a left-click during [[mouselook]].&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    control(key id, integer level, integer edge)&lt;br /&gt;
    {&lt;br /&gt;
        integer click = level &amp;amp; edge;&lt;br /&gt;
&lt;br /&gt;
        if (click &amp;amp; CONTROL_ML_LBUTTON) {&lt;br /&gt;
            llRezObjectWithParams(&amp;quot;Bullet&amp;quot;, [&lt;br /&gt;
                REZ_POS, &amp;lt;2,0,0&amp;gt;, TRUE, TRUE, // Relative offset 2 meters forward&lt;br /&gt;
                REZ_ROT, ZERO_ROTATION, TRUE, // Relative rotation&lt;br /&gt;
                REZ_VEL, &amp;lt;100,0,0&amp;gt;, TRUE, FALSE, // Relative velocity 100m/s forward&lt;br /&gt;
                REZ_DAMAGE, 40,&lt;br /&gt;
                REZ_LOCK_AXES, &amp;lt;1,1,1&amp;gt;, // Disable all rotation&lt;br /&gt;
                REZ_FLAGS, 0&lt;br /&gt;
                | REZ_FLAG_PHYSICAL&lt;br /&gt;
                | REZ_FLAG_TEMP&lt;br /&gt;
                | REZ_FLAG_DIE_ON_COLLIDE&lt;br /&gt;
                | REZ_FLAG_DIE_ON_NOENTRY&lt;br /&gt;
                | REZ_FLAG_NO_COLLIDE_OWNER&lt;br /&gt;
                | REZ_FLAG_NO_COLLIDE_FAMILY&lt;br /&gt;
                | REZ_FLAG_BLOCK_GRAB_OBJECT&lt;br /&gt;
            ]);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    run_time_permissions(integer perm)&lt;br /&gt;
    {&lt;br /&gt;
        if (perm) {&lt;br /&gt;
            llTakeControls(CONTROL_ML_LBUTTON, TRUE, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    attach(key id)&lt;br /&gt;
    {&lt;br /&gt;
        if (id) {&lt;br /&gt;
            llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        if (llGetAttached()) {&lt;br /&gt;
            llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_constants=&lt;br /&gt;
{{LSL DefineRow||[[PRIM_TEMP_ON_REZ]]|}}&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llRezAtRoot]]|Rezzes the object at the requested position}}&lt;br /&gt;
{{LSL DefineRow||[[llRezObject]]|Rezzes the object at the requested position}}&lt;br /&gt;
{{LSL DefineRow||[[llGetStartString]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetStartParameter]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGodLikeRezObject]]|}}&lt;br /&gt;
{{LSL DefineRow||[[LlGetParcelFlags#Examples]]|Test if the parcel allows this script to rez}}&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_events=&lt;br /&gt;
{{LSL DefineRow||[[object_rez]]|triggered when this object rezzes an object from inventory}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes=&#039;&#039;&#039;Maximum rez distance&#039;&#039;&#039;&lt;br /&gt;
*The {{LSLP|distance}} scales with the size of the object the script is in.&lt;br /&gt;
**The formula is &amp;lt;code&amp;gt;10 + llVecMag(llGetScale()) * 0.5&amp;lt;/code&amp;gt;&lt;br /&gt;
*The {{LSLP|distance}} measured is between the [[llGetPos|center]] of the rezzing {{LSLP|prim}} and the [[llGetPos|center]] of the {{LSLP|prim}} that is rezzed.&lt;br /&gt;
**The tests did not include rezzing from or the rezzing of link_set objects.&lt;br /&gt;
**A &amp;lt;code&amp;gt;0.01&amp;lt;/code&amp;gt; meter cube could rez just beyond &amp;lt;code&amp;gt;10.0&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
**A &amp;lt;code&amp;gt;10.0&amp;lt;/code&amp;gt; meter cube could rez just beyond &amp;lt;code&amp;gt;18.66&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
**A &amp;lt;code&amp;gt;32.0&amp;lt;/code&amp;gt; meter cube could rez just beyond &amp;lt;code&amp;gt;37.71&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
**A &amp;lt;code&amp;gt;64.0&amp;lt;/code&amp;gt; meter cube could rez just beyond &amp;lt;code&amp;gt;65.42&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
|permission&lt;br /&gt;
|inventory&lt;br /&gt;
|negative_index&lt;br /&gt;
|cat1=Rez&lt;br /&gt;
|cat2=Object&lt;br /&gt;
|cat3=Inventory&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlRezObject&amp;diff=1218754</id>
		<title>LlRezObject</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlRezObject&amp;diff=1218754"/>
		<updated>2026-03-31T21:01:16Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Updated rez distance section with formula and up to official scale.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2={{Issues/SVC-3145}}{{Issues/SVC-6578}}{{Issues/SVC-6686}}{{Issues/SCR-485}}&lt;br /&gt;
{{LSL_Function/inventory|inventory|uuid=false|type=object}}&lt;br /&gt;
{{LSL_Function/position|pos|region=*}}&lt;br /&gt;
|func_id=104|func_sleep=0.1|func_energy=200.0&lt;br /&gt;
|func=llRezObject|sort=RezObject&lt;br /&gt;
|p1_type=string|p1_name=inventory&lt;br /&gt;
|p2_type=vector|p2_name=pos&lt;br /&gt;
|p3_type=vector|p3_name=vel|p3_desc=velocity (max [[llVecMag|magnitude]] is approximately 200m/s)&lt;br /&gt;
|p4_type=rotation|p4_name=rot|p4_desc=rotation&lt;br /&gt;
|p5_type=integer|p5_name=param|p5_desc=[[on_rez]] event parameter and value returned by [[llGetStartParameter]] in the rezzed object (or by each of the items in a coalesced object).|p5_hover=on_rez event parameter and value returned by llGetStartParameter in the rezzed object.&lt;br /&gt;
|func_footnote=The root of {{LSLP|inventory}} is not at {{LSLP|pos}} but the [[llGetGeometricCenter|center]] of {{LSLP|inventory}} is.&amp;lt;br/&amp;gt;To have the root prim at {{LSLP|pos}} use [[llRezAtRoot]] instead.&lt;br /&gt;
|func_desc=Instantiate {{LSLP|inventory}} object at {{LSLP|pos}} with velocity {{LSLP|vel}} and rotation {{LSLP|rot}} with start parameter {{LSLP|param}}&lt;br /&gt;
|return_text&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=&lt;br /&gt;
*Silently fails to rez {{LSLP|inventory}} (to have it&#039;s geometric center at {{LSLP|pos}}) if {{LSLP|pos}} is too far from the geometric center of the object trying to rez {{LSLP|inventory}}.&lt;br /&gt;
**If your script is mysteriously failing to rez things, make sure you haven&#039;t (say) written &amp;quot;&amp;lt;code&amp;gt;&amp;lt;0.0,0.0,1.0&amp;gt;&amp;lt;/code&amp;gt;&amp;quot; for the {{LSLP|pos}} parameter rather than (say) &amp;lt;code&amp;gt;[[llGetPos]]() + &amp;lt;0.0,0.0,1.0&amp;gt;&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
* When scripting attachments meant to rez objects, remember that when used in the root of an ([[attach|attached]]) [[attachment]] &amp;lt;code&amp;gt;[[llGetPos]]&amp;lt;/code&amp;gt; doesn&#039;t return the position of the attachment but instead returns the position of the avatar&#039;s bounding box geometric center. Read [[llGetPos]] and [[llParticleSystem#Caveats | llParticleSystem Caveats]] for more information.&lt;br /&gt;
* If the object is unattached and the owner of the object does not have copy permission  on {{LSLP|inventory}}, the object will no longer be present in inventory after it is rezzed (so another attempt to rez (the same object) will fail); if the owner does have copy permission, then a copy is rezzed, and the original {{LSLP|inventory}} remains in inventory.&lt;br /&gt;
* If the object is attached and the owner of the object does not have copy permission  on {{LSLP|inventory}}, an error is shouted on [[DEBUG_CHANNEL]]: &amp;quot;Cannot rez no copy objects from an attached object.&amp;quot;&lt;br /&gt;
*Silently fails if you don&#039;t have offline building rights on the land. To have the right, your objects needs to &#039;&#039;either&#039;&#039;: &lt;br /&gt;
** Be on land you own yourself.&lt;br /&gt;
** Be on land where anyone is allowed to build, e.g. a sandbox. &lt;br /&gt;
** Be deeded to the group that owns the land.&lt;br /&gt;
** Be set to the same group that owns the land and the land have the parcel flag &#039;allow group to build&#039; set.&lt;br /&gt;
** The group role &amp;quot;Always allow &#039;Create Objects&#039;&amp;quot; will only work to override this when you are online, in the region, or have a child agent in the region. See [[#SVC-3145|SVC-3145]] in the Issues subsection of Deep Notes for more information.&lt;br /&gt;
* See [[object_rez]] for examples on how to establish communications between the rezzing object and the new prim.&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;default&lt;br /&gt;
{&lt;br /&gt;
     touch_start(integer param)&lt;br /&gt;
     {&lt;br /&gt;
          llRezObject(&amp;quot;Object&amp;quot;, llGetPos() + &amp;lt;0.0,0.0,1.0&amp;gt;, &amp;lt;0.0,0.0,0.0&amp;gt;, &amp;lt;0.0,0.0,0.0,1.0&amp;gt;, 0);&lt;br /&gt;
     }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;// Rez an object on touch, with relative position, rotation, and velocity all described in the rezzing prim&#039;s coordinate system.&lt;br /&gt;
string object = &amp;quot;Object&amp;quot;; // Name of object in inventory&lt;br /&gt;
vector relativePosOffset = &amp;lt;2.0, 0.0, 1.0&amp;gt;; // &amp;quot;Forward&amp;quot; and a little &amp;quot;above&amp;quot; this prim&lt;br /&gt;
vector relativeVel = &amp;lt;1.0, 0.0, 0.0&amp;gt;; // Traveling in this prim&#039;s &amp;quot;forward&amp;quot; direction at 1m/s&lt;br /&gt;
rotation relativeRot = &amp;lt;0.707107, 0.0, 0.0, 0.707107&amp;gt;; // Rotated 90 degrees on the x-axis compared to this prim&lt;br /&gt;
integer startParam = 10;&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer a)&lt;br /&gt;
    {&lt;br /&gt;
        vector myPos = llGetPos();&lt;br /&gt;
        rotation myRot = llGetRot();&lt;br /&gt;
&lt;br /&gt;
        vector rezPos = myPos+relativePosOffset*myRot;&lt;br /&gt;
        vector rezVel = relativeVel*myRot;&lt;br /&gt;
        rotation rezRot = relativeRot*myRot;&lt;br /&gt;
&lt;br /&gt;
        llRezObject(object, rezPos, rezVel, rezRot, startParam);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_constants=&lt;br /&gt;
{{LSL DefineRow||[[PRIM_TEMP_ON_REZ]]|}}&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llRezObjectWithParams]]|Modern alternative with more parameters.}}&lt;br /&gt;
{{LSL DefineRow||[[llRezAtRoot]]|Rezzes the object at the requested position}}&lt;br /&gt;
{{LSL DefineRow||[[llGetStartParameter]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGodLikeRezObject]]|}}&lt;br /&gt;
{{LSL DefineRow||[[LlGetParcelFlags#Examples]]|Test if the parcel allows this script to rez}}&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_events=&lt;br /&gt;
{{LSL DefineRow||[[object_rez]]|triggered when this object rezzes an object from inventory}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes=&#039;&#039;&#039;Maximum rez distance&#039;&#039;&#039;&lt;br /&gt;
*The {{LSLP|distance}} scales with the size of the object the script is in.&lt;br /&gt;
**The formula is &amp;lt;code&amp;gt;10 + llVecMag(llGetScale()) * 0.5&amp;lt;/code&amp;gt;&lt;br /&gt;
*The {{LSLP|distance}} measured is between the [[llGetPos|center]] of the rezzing {{LSLP|prim}} and the [[llGetPos|center]] of the {{LSLP|prim}} that is rezzed.&lt;br /&gt;
**The tests did not include rezzing from or the rezzing of link_set objects.&lt;br /&gt;
**A &amp;lt;code&amp;gt;0.01&amp;lt;/code&amp;gt; meter cube could rez just beyond &amp;lt;code&amp;gt;10.0&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
**A &amp;lt;code&amp;gt;10.0&amp;lt;/code&amp;gt; meter cube could rez just beyond &amp;lt;code&amp;gt;18.66&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
**A &amp;lt;code&amp;gt;32.0&amp;lt;/code&amp;gt; meter cube could rez just beyond &amp;lt;code&amp;gt;37.71&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
**A &amp;lt;code&amp;gt;64.0&amp;lt;/code&amp;gt; meter cube could rez just beyond &amp;lt;code&amp;gt;65.42&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
|permission&lt;br /&gt;
|inventory&lt;br /&gt;
|negative_index&lt;br /&gt;
|cat1=Rez&lt;br /&gt;
|cat2=Object&lt;br /&gt;
|cat3=Inventory&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlRezObject&amp;diff=1218747</id>
		<title>LlRezObject</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlRezObject&amp;diff=1218747"/>
		<updated>2026-03-30T19:36:50Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2={{Issues/SVC-3145}}{{Issues/SVC-6578}}{{Issues/SVC-6686}}{{Issues/SCR-485}}&lt;br /&gt;
{{LSL_Function/inventory|inventory|uuid=false|type=object}}&lt;br /&gt;
{{LSL_Function/position|pos|region=*}}&lt;br /&gt;
|func_id=104|func_sleep=0.1|func_energy=200.0&lt;br /&gt;
|func=llRezObject|sort=RezObject&lt;br /&gt;
|p1_type=string|p1_name=inventory&lt;br /&gt;
|p2_type=vector|p2_name=pos&lt;br /&gt;
|p3_type=vector|p3_name=vel|p3_desc=velocity (max [[llVecMag|magnitude]] is approximately 200m/s)&lt;br /&gt;
|p4_type=rotation|p4_name=rot|p4_desc=rotation&lt;br /&gt;
|p5_type=integer|p5_name=param|p5_desc=[[on_rez]] event parameter and value returned by [[llGetStartParameter]] in the rezzed object (or by each of the items in a coalesced object).|p5_hover=on_rez event parameter and value returned by llGetStartParameter in the rezzed object.&lt;br /&gt;
|func_footnote=The root of {{LSLP|inventory}} is not at {{LSLP|pos}} but the [[llGetGeometricCenter|center]] of {{LSLP|inventory}} is.&amp;lt;br/&amp;gt;To have the root prim at {{LSLP|pos}} use [[llRezAtRoot]] instead.&lt;br /&gt;
|func_desc=Instantiate {{LSLP|inventory}} object at {{LSLP|pos}} with velocity {{LSLP|vel}} and rotation {{LSLP|rot}} with start parameter {{LSLP|param}}&lt;br /&gt;
|return_text&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=&lt;br /&gt;
*Silently fails to rez {{LSLP|inventory}} (to have it&#039;s geometric center at {{LSLP|pos}}) if {{LSLP|pos}} is too far from the geometric center of the object trying to rez {{LSLP|inventory}}.&lt;br /&gt;
**If your script is mysteriously failing to rez things, make sure you haven&#039;t (say) written &amp;quot;&amp;lt;code&amp;gt;&amp;lt;0.0,0.0,1.0&amp;gt;&amp;lt;/code&amp;gt;&amp;quot; for the {{LSLP|pos}} parameter rather than (say) &amp;lt;code&amp;gt;[[llGetPos]]() + &amp;lt;0.0,0.0,1.0&amp;gt;&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
* When scripting attachments meant to rez objects, remember that when used in the root of an ([[attach|attached]]) [[attachment]] &amp;lt;code&amp;gt;[[llGetPos]]&amp;lt;/code&amp;gt; doesn&#039;t return the position of the attachment but instead returns the position of the avatar&#039;s bounding box geometric center. Read [[llGetPos]] and [[llParticleSystem#Caveats | llParticleSystem Caveats]] for more information.&lt;br /&gt;
* If the object is unattached and the owner of the object does not have copy permission  on {{LSLP|inventory}}, the object will no longer be present in inventory after it is rezzed (so another attempt to rez (the same object) will fail); if the owner does have copy permission, then a copy is rezzed, and the original {{LSLP|inventory}} remains in inventory.&lt;br /&gt;
* If the object is attached and the owner of the object does not have copy permission  on {{LSLP|inventory}}, an error is shouted on [[DEBUG_CHANNEL]]: &amp;quot;Cannot rez no copy objects from an attached object.&amp;quot;&lt;br /&gt;
*Silently fails if you don&#039;t have offline building rights on the land. To have the right, your objects needs to &#039;&#039;either&#039;&#039;: &lt;br /&gt;
** Be on land you own yourself.&lt;br /&gt;
** Be on land where anyone is allowed to build, e.g. a sandbox. &lt;br /&gt;
** Be deeded to the group that owns the land.&lt;br /&gt;
** Be set to the same group that owns the land and the land have the parcel flag &#039;allow group to build&#039; set.&lt;br /&gt;
** The group role &amp;quot;Always allow &#039;Create Objects&#039;&amp;quot; will only work to override this when you are online, in the region, or have a child agent in the region. See [[#SVC-3145|SVC-3145]] in the Issues subsection of Deep Notes for more information.&lt;br /&gt;
* See [[object_rez]] for examples on how to establish communications between the rezzing object and the new prim.&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;default&lt;br /&gt;
{&lt;br /&gt;
     touch_start(integer param)&lt;br /&gt;
     {&lt;br /&gt;
          llRezObject(&amp;quot;Object&amp;quot;, llGetPos() + &amp;lt;0.0,0.0,1.0&amp;gt;, &amp;lt;0.0,0.0,0.0&amp;gt;, &amp;lt;0.0,0.0,0.0,1.0&amp;gt;, 0);&lt;br /&gt;
     }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;// Rez an object on touch, with relative position, rotation, and velocity all described in the rezzing prim&#039;s coordinate system.&lt;br /&gt;
string object = &amp;quot;Object&amp;quot;; // Name of object in inventory&lt;br /&gt;
vector relativePosOffset = &amp;lt;2.0, 0.0, 1.0&amp;gt;; // &amp;quot;Forward&amp;quot; and a little &amp;quot;above&amp;quot; this prim&lt;br /&gt;
vector relativeVel = &amp;lt;1.0, 0.0, 0.0&amp;gt;; // Traveling in this prim&#039;s &amp;quot;forward&amp;quot; direction at 1m/s&lt;br /&gt;
rotation relativeRot = &amp;lt;0.707107, 0.0, 0.0, 0.707107&amp;gt;; // Rotated 90 degrees on the x-axis compared to this prim&lt;br /&gt;
integer startParam = 10;&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer a)&lt;br /&gt;
    {&lt;br /&gt;
        vector myPos = llGetPos();&lt;br /&gt;
        rotation myRot = llGetRot();&lt;br /&gt;
&lt;br /&gt;
        vector rezPos = myPos+relativePosOffset*myRot;&lt;br /&gt;
        vector rezVel = relativeVel*myRot;&lt;br /&gt;
        rotation rezRot = relativeRot*myRot;&lt;br /&gt;
&lt;br /&gt;
        llRezObject(object, rezPos, rezVel, rezRot, startParam);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_constants=&lt;br /&gt;
{{LSL DefineRow||[[PRIM_TEMP_ON_REZ]]|}}&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llRezObjectWithParams]]|Modern alternative with more parameters.}}&lt;br /&gt;
{{LSL DefineRow||[[llRezAtRoot]]|Rezzes the object at the requested position}}&lt;br /&gt;
{{LSL DefineRow||[[llGetStartParameter]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGodLikeRezObject]]|}}&lt;br /&gt;
{{LSL DefineRow||[[LlGetParcelFlags#Examples]]|Test if the parcel allows this script to rez}}&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_events=&lt;br /&gt;
{{LSL DefineRow||[[object_rez]]|triggered when this object rezzes an object from inventory}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes=&#039;&#039;&#039;Maximum rez distance&#039;&#039;&#039;&lt;br /&gt;
*After tests on server 1.38, these figures were measured - (on some earlier server versions the same &#039;&#039;further-than-10-meter&#039;&#039; rezzes have been possible).&lt;br /&gt;
*The {{LSLP|distance}} measured is between the [[llGetPos|center]] of the rezzing {{LSLP|prim}} and the [[llGetPos|center]] of the {{LSLP|prim}} that is rezzed.&lt;br /&gt;
**The tests did not include rezzing from or the rezzing of link_set objects.&lt;br /&gt;
**A &amp;lt;code&amp;gt;10.0&amp;lt;/code&amp;gt; meter cube could rez a &amp;lt;code&amp;gt;0.5&amp;lt;/code&amp;gt; meter cube just beyond &amp;lt;code&amp;gt;18.6&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
**A &amp;lt;code&amp;gt;0.01&amp;lt;/code&amp;gt; cube could rez a &amp;lt;code&amp;gt;0.5&amp;lt;/code&amp;gt; meter cube just beyond &amp;lt;code&amp;gt;10.0&amp;lt;/code&amp;gt; meters away.&lt;br /&gt;
|permission&lt;br /&gt;
|inventory&lt;br /&gt;
|negative_index&lt;br /&gt;
|cat1=Rez&lt;br /&gt;
|cat2=Object&lt;br /&gt;
|cat3=Inventory&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Preview_Grid&amp;diff=1218687</id>
		<title>Preview Grid</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Preview_Grid&amp;diff=1218687"/>
		<updated>2026-03-07T16:26:39Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Help|Viewer=*|Misc=*}}&lt;br /&gt;
&lt;br /&gt;
In addition to the Second Life main grid (known as &amp;quot;[[Land#Agni|Agni]]&amp;quot;) that many thousands of Residents log into each day, there is another [[Land#Grid|grid]] open to the public, known as the preview grid, beta grid, or &amp;quot;Aditi.&amp;quot; Aditi is where we test server software under development.&lt;br /&gt;
&lt;br /&gt;
For login instructions, read below in [[Preview_Grid#How do I log in to Aditi?|How do I log in to Aditi?]]&lt;br /&gt;
&lt;br /&gt;
If you change your password on AGNI, it may not transfer to the ADITI login servers. Keep your old password in case it doesn&#039;t. You will need to [https://support.secondlife.com/create-case/ contact Support] to get it resync&#039;d.&lt;br /&gt;
&lt;br /&gt;
==What We Do with the Preview Grid==&lt;br /&gt;
&lt;br /&gt;
The preview grid is used for development and testing of new features and bug fixes. You will find that as you move from one region to another, you will frequently be moving to regions that are running a different version of the server software. To get the most of the preview grid, you need to know where you are.&lt;br /&gt;
&lt;br /&gt;
===How do I know what version is running on the region I&#039;m in?===&lt;br /&gt;
[[Image:Helpversion.jpg|right]]&lt;br /&gt;
&lt;br /&gt;
Some regions have a &amp;quot;channel name&amp;quot; imprinted into the ground over and over again. If the ground texture doesn&#039;t make it obvious, look at &amp;quot;About Second Life&amp;quot; in the help menu where you will see a wealth of information about your own system, as well as about the server software running the region you are in. The image below shows you where in the help dialog to find the channel the region you&#039;re in is running. (Just to the right of that is the version.)&lt;br /&gt;
&lt;br /&gt;
The most important information in the image here is &amp;quot;Second Life Server&amp;quot;; that&#039;s the channel that you&#039;re on. The version number is also important, but the biggest changes you will see are between different channels.&lt;br /&gt;
&lt;br /&gt;
==Reporting Bugs or Problems==&lt;br /&gt;
&lt;br /&gt;
This will vary depending on the channel of the region, and on the state of the release. If you find problems in the &amp;quot;Second Life Beta Server&amp;quot; channel before that version has been deployed to the main Second Life service, please use our [http://jira.secondlife.com Bug Tracker] to check to see if that problem has already been reported, and to report it if not. When reporting a problem, please give as much information as possible: what region you were in, exactly what you did and what behavior you saw, when it happened, and the version of both your viewer software and of the server software running in the region where you saw the problem.&lt;br /&gt;
&lt;br /&gt;
In general, you will use the same procedure for problems found on regions in other channels, but sometimes those channels are being used for a focused test by other developers.&lt;br /&gt;
&lt;br /&gt;
==What is on the Preview Grid==&lt;br /&gt;
[[File:Aditi image.jpeg|200px|thumb|right|A map from Aditi]]&lt;br /&gt;
&lt;br /&gt;
Aditi has usually about 120 regions (sims). Some of them share the same name and coordinates with regions on the main grid (usually from [[Sansara]]). Some regions are special and used for testings and have different names. Access is sometimes denied in part of the sims. Usually, when you login first time to Aditi, you will start at Waterhad hidden lakes telehub (looks exactly like Waterhad on the main grid). Preview grid has a lower population density.&lt;br /&gt;
&lt;br /&gt;
We will be using Aditi for beta testing of the next server version, but we will also be using it for early previews of software that isn&#039;t going to be on Second Life right away, and for some specific public testing of bug-fixes being worked on by some teams of developers.&lt;br /&gt;
&lt;br /&gt;
The regions on Aditi are divided into different channels. There will always be two core channels:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Second Life Production Server&#039;&#039;&#039; : this has the same version of the software as is running on the main Second Life hosts. This exists for purposes of comparison. Sometimes, it will have the *previous* release, right after a new server has been deployed to Second Life.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Second Life Beta Server&#039;&#039;&#039; : this channel is designated for the version of the server we&#039;re planning on next deploying to Second Life in a rolling restart. Generally, after a new server version is deployed to Second Life, it will be at least a few days before the next beta version goes out to Aditi.&lt;br /&gt;
&lt;br /&gt;
In addition, there will sometimes be other channels for specific tests and/or early betas of upcoming features.&lt;br /&gt;
&lt;br /&gt;
==How do I log in to Aditi?==&lt;br /&gt;
&lt;br /&gt;
Check out the [https://lindenlab.freshdesk.com/support/solutions/articles/31000156725-accessing-aditi Accessing Aditi] page at Second Life&#039;s support solution center for detailed tips on how to access Aditi for the first time.&lt;br /&gt;
&lt;br /&gt;
In order to access the Beta Grid (Aditi) for the first time, you will have a slightly different login process than usual. Specifically, you&#039;ll have to attempt to login once, which triggers an automatic update of your account to copy it to the Beta Grid (Aditi). You should receive an error message when you attempt to login that states &#039;&#039;&#039;Login Failed. We&#039;re currently in the process of syncing this account. Please try again later.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This error message is normal, and if you wait 10-15 seconds, you can try your login one more time and it should succeed, granting you access to the Beta Grid.&lt;br /&gt;
&lt;br /&gt;
You can use the same Second Life viewer you already use to log into Second Life. At the login screen, hit {{KeyCombo|ctrl=*|shift=*|G}}. At the bottom of the screen to the right of the &amp;quot;Log In&amp;quot; button, you will see a dropdown menu that allows you to select the grid that you want to log in to:&lt;br /&gt;
*[[Image:Grid_Selection.jpeg]]&lt;br /&gt;
*Select &amp;quot;&#039;&#039;&#039;Second Life Beta Test&#039;&#039;&#039;&amp;quot; (aka Preview or Aditi) to log into the Test Grid. To return to the Main Grid select &amp;quot;&#039;&#039;&#039;Second Life Main Grid (Agni)&#039;&#039;&#039;&amp;quot; at your next login. (On some third-party viewers a number of other grids may be listed in this dropdown.) &lt;br /&gt;
&lt;br /&gt;
There are other SL internal development grids which are not available for public access. Some viewers list them. Unless your last name is Linden, you won&#039;t be able to log into them.&lt;br /&gt;
&lt;br /&gt;
Some viewers display names other than &amp;quot;Second Life Beta&amp;quot;. This is the case for the Firestorm viewer. Preview Grid, Second Life Beta Grid, Test grid, and Aditi are all indicating the same grid.&lt;br /&gt;
&lt;br /&gt;
{{note|Starting with Viewer version 2.6.0, you will need to go to the Me-&amp;gt;Preferences-&amp;gt;Advanced menu and enable &amp;quot;Show Advanced Menu&amp;quot; before the above will work.&lt;br /&gt;
&lt;br /&gt;
The key combination works in versions 5+ without having to change any settings.}}&lt;br /&gt;
&lt;br /&gt;
Once you do log into Aditi, you should have all of the inventory that you have on Main Grid (Agni). However, in subsequent logins to Aditi new items acquired in the Second Life Main Grid (Agni) will not be available to your Aditi account until the next inventory sync, which should be within 24 hours after your Aditi login. The process is scheduled for a 6 AM SLT start. How long it may take to get to your account is an unknown.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IN NO EVENT&#039;&#039;&#039; will you be able to transfer money or objects back from the Preview Grid for use in the Main Second Life Grid (Agni).&lt;br /&gt;
&lt;br /&gt;
If you change your password or username on the main Second Life grid, you will need to contact the Support team to request a manual copy to update your account. &lt;br /&gt;
To contact Support for a manual copy to update your account on the Beta Grid (Aditi):&lt;br /&gt;
* Visit [https://support.secondlife.com/create-case/ the create a ticket section of support.secondlife.com]&lt;br /&gt;
* Sign in to your Second Life account, if you aren&#039;t already logged in. &lt;br /&gt;
* On the &#039;&#039;&#039;Submit A Ticket&#039;&#039;&#039; page, select &#039;&#039;&#039;Account Issue&#039;&#039;&#039; as the Issue Type&lt;br /&gt;
* For the Account Issue dropdown, select &#039;&#039;&#039;Viewer Login Issues&#039;&#039;&#039;&lt;br /&gt;
* Enter the name of the avatar you&#039;d like to use on the Beta Grid and in the &#039;&#039;Please describe the issue&#039;&#039; box, explain that you&#039;d like your account recopied to Aditi or the Beta Grid.&lt;br /&gt;
* Click &#039;&#039;&#039;Submit&#039;&#039;&#039; to send your request to Support.&lt;br /&gt;
&lt;br /&gt;
== Uploading Mesh on Aditi ==&lt;br /&gt;
You must accept the IP Terms specifically for Aditi, even if you have already accepted the terms on the Main grid.&amp;lt;br /&amp;gt;&lt;br /&gt;
The Aditi IP Terms agreement can be found here:  https://secondlife.aditi.lindenlab.com/my/account/ip/accept.php&amp;lt;br /&amp;gt;&lt;br /&gt;
You do not need to have payment information on file to upload mesh on Aditi.&lt;br /&gt;
&lt;br /&gt;
== Using Web Profiles on Aditi ==&lt;br /&gt;
[[Web profiles]] are available on Aditi with a bit of tweaking. To test web based profiles in the Viewer (2.5+) on ADITI, the WebProfileURL debug setting needs to be changed before logging in to the Viewer. You have two ways of doing this:&lt;br /&gt;
* The debug setting can be accessed via Advanced-&amp;gt;Debug Settings. Change the value to https://my-demo.secondlife.com/. This process will be automatic once login.cgi has been updated.&lt;br /&gt;
* You can invoke your viewer with the following executable parameter:&lt;br /&gt;
** --set WebProfileURL &amp;quot;https://my-demo.secondlife.com/&amp;quot; &lt;br /&gt;
** Removing this variable or setting it to &amp;quot;https://my.secondlife.com/&amp;quot; will set it back to use the AGNI based profiles&lt;br /&gt;
* Before logging back into AGNI remove this variable.&lt;br /&gt;
* This is Aditi , so things might be borked. Please bear with us.&lt;br /&gt;
&lt;br /&gt;
== Using Marketplace on Aditi ==&lt;br /&gt;
The Marketplace supports Beta testing on Aditi! All Merchants will have access to this grid for Beta testing, but will only be able to test purchasing items using L$, since Aditi is not set up to test out other payment methods.&lt;br /&gt;
&lt;br /&gt;
In order to participate in Beta testing, you just need to login to the Aditi grid using your viewer of choice. Your account should automatically be set up and you should receive L$50,000 to start.&lt;br /&gt;
&lt;br /&gt;
Nothing that happens on the test grid (Aditi) has any effect on your avatar, inventory, L$, or marketplace listings on the main grid (Agni). Objects, inventory and L$ on the Beta grid may be damaged by bugs, be removed, or disappear at any point during Beta testing.&lt;br /&gt;
&lt;br /&gt;
To access functions on the Beta grid:&lt;br /&gt;
&lt;br /&gt;
* Grid: Aditi (see [http://wiki.secondlife.com/wiki/Aditi#How_do_I_log_in_to_Aditi.3F this section] for more details)&lt;br /&gt;
* Marketplace.com: [https://marketplace.aditi.lindenlab.com https://marketplace.aditi.lindenlab.com] (NOTE: you may see old listings, which were migrated from production when Aditi was set up. Changes made to these listings will not have an impact on production.)&lt;br /&gt;
* Magic Boxes for Aditi: [https://marketplace.aditi.lindenlab.com/stores/75 https://marketplace.aditi.lindenlab.com/stores/75]&lt;br /&gt;
* Secondlife.com: [http://secondlife.aditi.lindenlab.com/ http://secondlife.aditi.lindenlab.com/]&lt;br /&gt;
* If no region is specified for the Beta, you can try one of the following regions:&lt;br /&gt;
** Virtual Trade Campus South: {{Region|Virtuatrade Campus S|127|127|27|grid=aditi}}&lt;br /&gt;
** Virtual Trade Campus SE: {{Region|Virtuatrade Campus SE|86|124|34|grid=aditi}}&lt;br /&gt;
** Or one of the [[#Permanent Sandbox Regions|permanent sandbox regions]]&lt;br /&gt;
* Note that you may need to accept a security exception to complete a purchase using L$. Since this is a test web site, no actual sensitive information will be passed, so it is safe to do on the Aditi web site.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you find a problem or have a suggestion, first try it out on Production before filing a bug on the Feedback Forum. See the KB instructions on [https://community.secondlife.com/knowledgebase/english/how-to-report-a-bug-r224/ How to report a bug] for general help on filing a bug.&lt;br /&gt;
&lt;br /&gt;
== Updating Your Account on Aditi ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;Revised:&amp;lt;/span&amp;gt;&#039;&#039;&#039; May 2018 &lt;br /&gt;
&lt;br /&gt;
Inventory updates are now a process that sync&#039;s your AGNI (main grid) inventory into your ADITI inventory. No overwrite or loss of ADITI inventory as previously.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you login and off, your inventory will re-sync next at about 6 AM PT. The login/out is the trigger. No need to change the password to update inventory. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Change your password regularly to protect your account. But, &#039;&#039;&#039;save your old password&#039;&#039;&#039;. Sometimes the Aditi login process does not update. Try your old password. (2017 bug)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If your inventory or password does NOT update, you will need to contact SL Support for help.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Your AGNI trash does sync to ADITI...&lt;br /&gt;
&lt;br /&gt;
== Regions on Aditi ==&lt;br /&gt;
&lt;br /&gt;
Aditi has limited capacity, so regions are frequently taken offline in order to make room for regions with more interesting test cases. See [http://aditi.coreqa.net/gridtool.php  this page] for the status of several Aditi regions.&lt;br /&gt;
&lt;br /&gt;
=== Permanent Sandbox Regions ===&lt;br /&gt;
Residents who wish to test their content before bringing it to the main grid have been given dedicated regions to do so:&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Cloud%20Sandbox%201/128/128 Cloud Sandbox 1 (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Mesh%20Sandbox%202/128/128 Mesh Sandbox 2 (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Sandbox%20Astutula/128/128 Sandbox Astutula (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Sandbox%20Pristina/128/128 Sandbox Pristina (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Sandbox%20Wanderton/128/128 Sandbox Wanderton (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_A.png]] [secondlife://Aditi/secondlife/Materials%20Adult/128/128 Materials Adult (128,128)]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= See Also =&lt;br /&gt;
* [[Preview Grid Common Questions]]&lt;br /&gt;
* [[Beta Server Office Hours]] are held &#039;&#039;on the preview grid&#039;&#039; at {{Region|Morris|192|251|35|grid=aditi}}.  These are to discuss beta testing of the next upcoming release of Second Life; when there is not a release pending, we will discuss general SVC issues listed in the JIRA.&lt;br /&gt;
* The [https://community.secondlife.com/forums/forum/310-second-life-server/ Release Team blog] has discussions about rolling restarts, server release beta testing in general, and specific versions of the server that are about to be released. Also, see the archived [http://forums-archive.secondlife.com/348/1.html Server Deploy Forums] for updates before February 2010.&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
{{References}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Bug Tracker]]&lt;br /&gt;
[[Category:Quality Assurance]]&lt;br /&gt;
[[Category:QA_Portal]]&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Preview_Grid&amp;diff=1218686</id>
		<title>Preview Grid</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Preview_Grid&amp;diff=1218686"/>
		<updated>2026-03-07T16:14:56Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Removed outdated paragraph about needing to contact support, which has been fixed since 2024.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Help|Viewer=*|Misc=*}}&lt;br /&gt;
&lt;br /&gt;
In addition to the Second Life main grid (known as &amp;quot;[[Land#Agni|Agni]]&amp;quot;) that many thousands of Residents log into each day, there is another [[Land#Grid|grid]] open to the public, known as the preview grid, or &amp;quot;Aditi.&amp;quot; Aditi is where we test server software under development.&lt;br /&gt;
&lt;br /&gt;
For login instructions, read below in [[Preview_Grid#How do I log in to Aditi?|How do I log in to Aditi?]]&lt;br /&gt;
&lt;br /&gt;
If you change your password on AGNI, it may not transfer to the ADITI login servers. Keep your old password in case it doesn&#039;t. You will need to [https://support.secondlife.com/create-case/ contact Support] to get it resync&#039;d.&lt;br /&gt;
&lt;br /&gt;
==What We Do with the Preview Grid==&lt;br /&gt;
&lt;br /&gt;
The preview grid is used for development and testing of new features and bug fixes. You will find that as you move from one region to another, you will frequently be moving to regions that are running a different version of the server software. To get the most of the preview grid, you need to know where you are.&lt;br /&gt;
&lt;br /&gt;
===How do I know what version is running on the region I&#039;m in?===&lt;br /&gt;
[[Image:Helpversion.jpg|right]]&lt;br /&gt;
&lt;br /&gt;
Some regions have a &amp;quot;channel name&amp;quot; imprinted into the ground over and over again. If the ground texture doesn&#039;t make it obvious, look at &amp;quot;About Second Life&amp;quot; in the help menu where you will see a wealth of information about your own system, as well as about the server software running the region you are in. The image below shows you where in the help dialog to find the channel the region you&#039;re in is running. (Just to the right of that is the version.)&lt;br /&gt;
&lt;br /&gt;
The most important information in the image here is &amp;quot;Second Life Server&amp;quot;; that&#039;s the channel that you&#039;re on. The version number is also important, but the biggest changes you will see are between different channels.&lt;br /&gt;
&lt;br /&gt;
==Reporting Bugs or Problems==&lt;br /&gt;
&lt;br /&gt;
This will vary depending on the channel of the region, and on the state of the release. If you find problems in the &amp;quot;Second Life Beta Server&amp;quot; channel before that version has been deployed to the main Second Life service, please use our [http://jira.secondlife.com Bug Tracker] to check to see if that problem has already been reported, and to report it if not. When reporting a problem, please give as much information as possible: what region you were in, exactly what you did and what behavior you saw, when it happened, and the version of both your viewer software and of the server software running in the region where you saw the problem.&lt;br /&gt;
&lt;br /&gt;
In general, you will use the same procedure for problems found on regions in other channels, but sometimes those channels are being used for a focused test by other developers.&lt;br /&gt;
&lt;br /&gt;
==What is on the Preview Grid==&lt;br /&gt;
[[File:Aditi image.jpeg|200px|thumb|right|A map from Aditi]]&lt;br /&gt;
&lt;br /&gt;
Aditi has usually about 120 regions (sims). Some of them share the same name and coordinates with regions on the main grid (usually from [[Sansara]]). Some regions are special and used for testings and have different names. Access is sometimes denied in part of the sims. Usually, when you login first time to Aditi, you will start at Waterhad hidden lakes telehub (looks exactly like Waterhad on the main grid). Preview grid has a lower population density.&lt;br /&gt;
&lt;br /&gt;
We will be using Aditi for beta testing of the next server version, but we will also be using it for early previews of software that isn&#039;t going to be on Second Life right away, and for some specific public testing of bug-fixes being worked on by some teams of developers.&lt;br /&gt;
&lt;br /&gt;
The regions on Aditi are divided into different channels. There will always be two core channels:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Second Life Production Server&#039;&#039;&#039; : this has the same version of the software as is running on the main Second Life hosts. This exists for purposes of comparison. Sometimes, it will have the *previous* release, right after a new server has been deployed to Second Life.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Second Life Beta Server&#039;&#039;&#039; : this channel is designated for the version of the server we&#039;re planning on next deploying to Second Life in a rolling restart. Generally, after a new server version is deployed to Second Life, it will be at least a few days before the next beta version goes out to Aditi.&lt;br /&gt;
&lt;br /&gt;
In addition, there will sometimes be other channels for specific tests and/or early betas of upcoming features.&lt;br /&gt;
&lt;br /&gt;
==How do I log in to Aditi?==&lt;br /&gt;
&lt;br /&gt;
Check out the [https://lindenlab.freshdesk.com/support/solutions/articles/31000156725-accessing-aditi Accessing Aditi] page at Second Life&#039;s support solution center for detailed tips on how to access Aditi for the first time.&lt;br /&gt;
&lt;br /&gt;
In order to access the Beta Grid (Aditi) for the first time, you will have a slightly different login process than usual. Specifically, you&#039;ll have to attempt to login once, which triggers an automatic update of your account to copy it to the Beta Grid (Aditi). You should receive an error message when you attempt to login that states &#039;&#039;&#039;Login Failed. We&#039;re currently in the process of syncing this account. Please try again later.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This error message is normal, and if you wait 10-15 seconds, you can try your login one more time and it should succeed, granting you access to the Beta Grid.&lt;br /&gt;
&lt;br /&gt;
You can use the same Second Life viewer you already use to log into Second Life. At the login screen, hit {{KeyCombo|ctrl=*|shift=*|G}}. At the bottom of the screen to the right of the &amp;quot;Log In&amp;quot; button, you will see a dropdown menu that allows you to select the grid that you want to log in to:&lt;br /&gt;
*[[Image:Grid_Selection.jpeg]]&lt;br /&gt;
*Select &amp;quot;&#039;&#039;&#039;Second Life Beta Test&#039;&#039;&#039;&amp;quot; (aka Preview or Aditi) to log into the Test Grid. To return to the Main Grid select &amp;quot;&#039;&#039;&#039;Second Life Main Grid (Agni)&#039;&#039;&#039;&amp;quot; at your next login. (On some third-party viewers a number of other grids may be listed in this dropdown.) &lt;br /&gt;
&lt;br /&gt;
There are other SL internal development grids which are not available for public access. Some viewers list them. Unless your last name is Linden, you won&#039;t be able to log into them.&lt;br /&gt;
&lt;br /&gt;
Some viewers display names other than &amp;quot;Second Life Beta&amp;quot;. This is the case for the Firestorm viewer. Preview Grid, Second Life Beta Grid, Test grid, and Aditi are all indicating the same grid.&lt;br /&gt;
&lt;br /&gt;
{{note|Starting with Viewer version 2.6.0, you will need to go to the Me-&amp;gt;Preferences-&amp;gt;Advanced menu and enable &amp;quot;Show Advanced Menu&amp;quot; before the above will work.&lt;br /&gt;
&lt;br /&gt;
The key combination works in versions 5+ without having to change any settings.}}&lt;br /&gt;
&lt;br /&gt;
Once you do log into Aditi, you should have all of the inventory that you have on Main Grid (Agni). However, in subsequent logins to Aditi new items acquired in the Second Life Main Grid (Agni) will not be available to your Aditi account until the next inventory sync, which should be within 24 hours after your Aditi login. The process is scheduled for a 6 AM SLT start. How long it may take to get to your account is an unknown.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IN NO EVENT&#039;&#039;&#039; will you be able to transfer money or objects back from the Preview Grid for use in the Main Second Life Grid (Agni).&lt;br /&gt;
&lt;br /&gt;
If you change your password or username on the main Second Life grid, you will need to contact the Support team to request a manual copy to update your account. &lt;br /&gt;
To contact Support for a manual copy to update your account on the Beta Grid (Aditi):&lt;br /&gt;
* Visit [https://support.secondlife.com/create-case/ the create a ticket section of support.secondlife.com]&lt;br /&gt;
* Sign in to your Second Life account, if you aren&#039;t already logged in. &lt;br /&gt;
* On the &#039;&#039;&#039;Submit A Ticket&#039;&#039;&#039; page, select &#039;&#039;&#039;Account Issue&#039;&#039;&#039; as the Issue Type&lt;br /&gt;
* For the Account Issue dropdown, select &#039;&#039;&#039;Viewer Login Issues&#039;&#039;&#039;&lt;br /&gt;
* Enter the name of the avatar you&#039;d like to use on the Beta Grid and in the &#039;&#039;Please describe the issue&#039;&#039; box, explain that you&#039;d like your account recopied to Aditi or the Beta Grid.&lt;br /&gt;
* Click &#039;&#039;&#039;Submit&#039;&#039;&#039; to send your request to Support.&lt;br /&gt;
&lt;br /&gt;
== Uploading Mesh on Aditi ==&lt;br /&gt;
You must accept the IP Terms specifically for Aditi, even if you have already accepted the terms on the Main grid.&amp;lt;br /&amp;gt;&lt;br /&gt;
The Aditi IP Terms agreement can be found here:  https://secondlife.aditi.lindenlab.com/my/account/ip/accept.php&amp;lt;br /&amp;gt;&lt;br /&gt;
You do not need to have payment information on file to upload mesh on Aditi.&lt;br /&gt;
&lt;br /&gt;
== Using Web Profiles on Aditi ==&lt;br /&gt;
[[Web profiles]] are available on Aditi with a bit of tweaking. To test web based profiles in the Viewer (2.5+) on ADITI, the WebProfileURL debug setting needs to be changed before logging in to the Viewer. You have two ways of doing this:&lt;br /&gt;
* The debug setting can be accessed via Advanced-&amp;gt;Debug Settings. Change the value to https://my-demo.secondlife.com/. This process will be automatic once login.cgi has been updated.&lt;br /&gt;
* You can invoke your viewer with the following executable parameter:&lt;br /&gt;
** --set WebProfileURL &amp;quot;https://my-demo.secondlife.com/&amp;quot; &lt;br /&gt;
** Removing this variable or setting it to &amp;quot;https://my.secondlife.com/&amp;quot; will set it back to use the AGNI based profiles&lt;br /&gt;
* Before logging back into AGNI remove this variable.&lt;br /&gt;
* This is Aditi , so things might be borked. Please bear with us.&lt;br /&gt;
&lt;br /&gt;
== Using Marketplace on Aditi ==&lt;br /&gt;
The Marketplace supports Beta testing on Aditi! All Merchants will have access to this grid for Beta testing, but will only be able to test purchasing items using L$, since Aditi is not set up to test out other payment methods.&lt;br /&gt;
&lt;br /&gt;
In order to participate in Beta testing, you just need to login to the Aditi grid using your viewer of choice. Your account should automatically be set up and you should receive L$50,000 to start.&lt;br /&gt;
&lt;br /&gt;
Nothing that happens on the test grid (Aditi) has any effect on your avatar, inventory, L$, or marketplace listings on the main grid (Agni). Objects, inventory and L$ on the Beta grid may be damaged by bugs, be removed, or disappear at any point during Beta testing.&lt;br /&gt;
&lt;br /&gt;
To access functions on the Beta grid:&lt;br /&gt;
&lt;br /&gt;
* Grid: Aditi (see [http://wiki.secondlife.com/wiki/Aditi#How_do_I_log_in_to_Aditi.3F this section] for more details)&lt;br /&gt;
* Marketplace.com: [https://marketplace.aditi.lindenlab.com https://marketplace.aditi.lindenlab.com] (NOTE: you may see old listings, which were migrated from production when Aditi was set up. Changes made to these listings will not have an impact on production.)&lt;br /&gt;
* Magic Boxes for Aditi: [https://marketplace.aditi.lindenlab.com/stores/75 https://marketplace.aditi.lindenlab.com/stores/75]&lt;br /&gt;
* Secondlife.com: [http://secondlife.aditi.lindenlab.com/ http://secondlife.aditi.lindenlab.com/]&lt;br /&gt;
* If no region is specified for the Beta, you can try one of the following regions:&lt;br /&gt;
** Virtual Trade Campus South: {{Region|Virtuatrade Campus S|127|127|27|grid=aditi}}&lt;br /&gt;
** Virtual Trade Campus SE: {{Region|Virtuatrade Campus SE|86|124|34|grid=aditi}}&lt;br /&gt;
** Or one of the [[#Permanent Sandbox Regions|permanent sandbox regions]]&lt;br /&gt;
* Note that you may need to accept a security exception to complete a purchase using L$. Since this is a test web site, no actual sensitive information will be passed, so it is safe to do on the Aditi web site.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you find a problem or have a suggestion, first try it out on Production before filing a bug on the Feedback Forum. See the KB instructions on [https://community.secondlife.com/knowledgebase/english/how-to-report-a-bug-r224/ How to report a bug] for general help on filing a bug.&lt;br /&gt;
&lt;br /&gt;
== Updating Your Account on Aditi ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;Revised:&amp;lt;/span&amp;gt;&#039;&#039;&#039; May 2018 &lt;br /&gt;
&lt;br /&gt;
Inventory updates are now a process that sync&#039;s your AGNI (main grid) inventory into your ADITI inventory. No overwrite or loss of ADITI inventory as previously.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you login and off, your inventory will re-sync next at about 6 AM PT. The login/out is the trigger. No need to change the password to update inventory. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Change your password regularly to protect your account. But, &#039;&#039;&#039;save your old password&#039;&#039;&#039;. Sometimes the Aditi login process does not update. Try your old password. (2017 bug)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If your inventory or password does NOT update, you will need to contact SL Support for help.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Your AGNI trash does sync to ADITI...&lt;br /&gt;
&lt;br /&gt;
== Regions on Aditi ==&lt;br /&gt;
&lt;br /&gt;
Aditi has limited capacity, so regions are frequently taken offline in order to make room for regions with more interesting test cases. See [http://aditi.coreqa.net/gridtool.php  this page] for the status of several Aditi regions.&lt;br /&gt;
&lt;br /&gt;
=== Permanent Sandbox Regions ===&lt;br /&gt;
Residents who wish to test their content before bringing it to the main grid have been given dedicated regions to do so:&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Cloud%20Sandbox%201/128/128 Cloud Sandbox 1 (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Mesh%20Sandbox%202/128/128 Mesh Sandbox 2 (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Sandbox%20Astutula/128/128 Sandbox Astutula (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Sandbox%20Pristina/128/128 Sandbox Pristina (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_G.png]] [secondlife://Aditi/secondlife/Sandbox%20Wanderton/128/128 Sandbox Wanderton (128,128)]&lt;br /&gt;
* [[File:Parcel_lght_A.png]] [secondlife://Aditi/secondlife/Materials%20Adult/128/128 Materials Adult (128,128)]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= See Also =&lt;br /&gt;
* [[Preview Grid Common Questions]]&lt;br /&gt;
* [[Beta Server Office Hours]] are held &#039;&#039;on the preview grid&#039;&#039; at {{Region|Morris|192|251|35|grid=aditi}}.  These are to discuss beta testing of the next upcoming release of Second Life; when there is not a release pending, we will discuss general SVC issues listed in the JIRA.&lt;br /&gt;
* The [https://community.secondlife.com/forums/forum/310-second-life-server/ Release Team blog] has discussions about rolling restarts, server release beta testing in general, and specific versions of the server that are about to be released. Also, see the archived [http://forums-archive.secondlife.com/348/1.html Server Deploy Forums] for updates before February 2010.&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
{{References}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Bug Tracker]]&lt;br /&gt;
[[Category:Quality Assurance]]&lt;br /&gt;
[[Category:QA_Portal]]&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=PBR_Materials&amp;diff=1218685</id>
		<title>PBR Materials</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=PBR_Materials&amp;diff=1218685"/>
		<updated>2026-03-07T13:46:54Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Clarified wording. /* PBR Stand-In Textures and Outdated Viewers */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{multi-lang|1=PBR_Materials|2=/en}}&lt;br /&gt;
&#039;&#039;&#039;Physically Based Rendering&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{{note|1=This page is being actively updated with new content and links as GLTF PBR Materials and related new features are being released. Contributions to this page may be from Second Life residents and staff members alike.}}&lt;br /&gt;
&lt;br /&gt;
[[Category:glTF]]&lt;br /&gt;
&lt;br /&gt;
== What is it and Why is it used? ==&lt;br /&gt;
&lt;br /&gt;
The term “Physically Based Rendering” or “PBR” is a technical term for composing images in computer software.  It refers to a collection of complex mathematical algorithms that attempt to &#039;&#039;accurately represent the ways that light interacts with objects in the real world.&#039;&#039; In other words, focus is put on the real physical properties of materials.&lt;br /&gt;
&lt;br /&gt;
In the real world, light&#039;s unique behavior against metal is how the human eye can recognize &amp;quot;that object is made of metal&amp;quot; without us actually reaching out and touching it.   The way a metal reflects light differs from that of a polished plastic or some other material, and these differences have been quantified by science.&lt;br /&gt;
&lt;br /&gt;
Older techniques, such as the existing Blinn-Phong reflection model in Second Life, are more naive approximations of light that lack many nuances (eg. conductivity, microsurfaces, fresnel, energy conservation) that are now possible to simulate in real time.&lt;br /&gt;
&lt;br /&gt;
==== How it applies to Second Life ====&lt;br /&gt;
By mimicking real-world physics principles in the virtual world it allows for the creation of more immersive recognizable realistic spaces, but also it helps us relate to fantastical worlds a little better too.  While we may not be familiar with what a newly imagined creation is, a metal&#039;s inherent metal-ness and aged wood&#039;s inherent wood-ness remain constant, making it easier to intuitively understand what we are interacting with in a virtual environment.&lt;br /&gt;
&lt;br /&gt;
Because tying the mathematics to simulate materials in virtual spaces to how they behave in the real-world makes things more immediately recognizable, PBR has become the foundation for creating imagined worlds over the last decade.  The metallic shine of exoskeletal armor in superhero movies is driven by a PBR workflow, as is the plastic sheen of toys or the glint of frozen ice in animated classics.  Now we are moving to bring this standardized quality to your home in Second Life.&lt;br /&gt;
&lt;br /&gt;
Bringing PBR to Second Life means updating the basic calculations of how light is represented and interacts with the world of Second Life.  The goal is to integrate these changes while minimally changing how everything that presently exists in Second Life (designed prior to the introduction of PBR) looks.  While the preservation of creative intent and the aesthetic appeal of items users have enjoyed for over two decades of Second Life is always a priority, Second Life is an ever-evolving platform, and to continue to do so, some changes are inevitable.&lt;br /&gt;
&lt;br /&gt;
Lastly, while we are attempting to mimic real-world reflections and material properties, Second Life has to run on a wide variety of devices, so some shortcuts have to be made.  Reflections are not mirror-perfect, as has often been a long-standing hope and request from Residents.  While the addition of a reflection system does bring dreams of distortion free mirrors for our avatars in Second Life closer to reality, unfortunately, due to the calculation requirements of doing perfect reflections, mirrors are sadly still, for the moment, not practical. Please see the note in the [[#Understanding_and_Assisting_the_New_Reflections_System|section below]] for more info.&lt;br /&gt;
&lt;br /&gt;
== What does this mean for Residents? ==&lt;br /&gt;
[[File:PrePostPBR.png | thumb | right | The lamp in this screenshot uses [[PBR_Materials#Nomenclature_changes|Blinn-Phong]] (Classic SL Materials), and was created before PBR&#039;s integration into SL; and demonstrates some of the visual differences to existing content. The environment used for the screenshot is the viewer&#039;s default Midday preset (Note that the PBR viewer has a new Midday preset).&lt;br /&gt;
]]&lt;br /&gt;
=== Changes to Existing Content ===&lt;br /&gt;
The largest change by far is the addition of an environmental reflection system to Second Life.  For most existing items, this change shouldn’t have a drastic immediately observable impact.  A few things in your inventory that you already own may appear more reflective with the new graphics configuration and those reflections should feel more realistic and immersive with your current environment.  As a general rule: the “shinier” an object was before, the more environment reflections it will pick up and the more visual difference there will be.&lt;br /&gt;
&lt;br /&gt;
Another notable change is the addition of [https://en.wikipedia.org/wiki/Tone_mapping tonemapping] to the viewer. Tonemapping is a way of representing an image with a higher native dynamic range than the display can support. Tonemapping is a de-facto requirement of PBR pipelines. This means that colors in Second Life will generally appear more saturated with less detail being lost in shadows and highlights. [https://modelviewer.dev/examples/color.html Click here for some additional reading on the subject.]&lt;br /&gt;
&lt;br /&gt;
The tonemapping method used is called &#039;&#039;&#039;Academy Color Encoding System (&#039;&#039;&#039;aka &#039;&#039;&#039;&amp;quot;ACES&amp;quot;)&#039;&#039;&#039;, and can be read about [https://www.oscars.org/science-technology/sci-tech-projects/aces here] and [https://docs.nvidia.com/gameworks/index.html#devices/shield-hdr-dev-guide/hdr-dev-guide-nvidia-shield.htm here].&lt;br /&gt;
&lt;br /&gt;
[[PBR_Materials#Nomenclature_changes|Blinn-Phong]] content making use of the Specular parameters may look different if the items were never viewed under local lights, as the PBR viewer allows for the environment (sun &#039;&#039;and&#039;&#039; sky) to contribute to specular reflections. As such, if the object receives blue specular reflections from the sky, these reflections are tinted, and may look odd. This effect is the same as prior viewers, as if the object was lit by a blue local light.&lt;br /&gt;
&lt;br /&gt;
=== Removal of Advanced Lighting Model ( ALM ) Graphics Option ===&lt;br /&gt;
There have been changes to the &#039;&#039;&#039;Graphics &amp;gt; Advanced Settings...&#039;&#039;&#039; Preferences. The most notable of which is the removal of the “Atmospheric Shaders”, &amp;quot;Local lights&amp;quot; and “Advanced Lighting Model” options.&lt;br /&gt;
&lt;br /&gt;
For those users on lower-end hardware who depended heavily upon those options to navigate Second Life with an acceptable framerate, we recommend the following settings:&lt;br /&gt;
*{{code|Transparent Water: Disabled}}&lt;br /&gt;
*{{code|Screen Space Ambient Occlusion: Disabled}}&lt;br /&gt;
*{{code|Shadows: None}}&lt;br /&gt;
*{{code|Screen Space Reflections: Disabled}}&lt;br /&gt;
*{{code|Reflection Detail: Static Only}}&lt;br /&gt;
*{{code|Reflection Coverage: Manual only}}&lt;br /&gt;
&lt;br /&gt;
The {{code|Local Lights}} setting has been superceded by updates made to the &#039;&#039;&#039;World &amp;gt; Improve graphics speed...&#039;&#039;&#039; &amp;quot;Quality &amp;amp; Speed&amp;quot; slider options. Users on low-end hardware should ensure that their &amp;quot;Quality &amp;amp; Speed&amp;quot; slider is set at an appropriate level for their hardware.&lt;br /&gt;
&lt;br /&gt;
== What does this mean for Creators? ==&lt;br /&gt;
=== Nomenclature changes ===&lt;br /&gt;
The PBR project represents a large step towards integrating standard rendering techniques used in the games industry. As such, the nomenclature of some items has changed, notably, what was once called &#039;&#039;&#039;[[:Category:Materials|Materials]]&#039;&#039;&#039; is now referred to as &#039;&#039;&#039;Blinn-Phong&#039;&#039;&#039;. This does not represent any changes to the underlying rendering techniques (beyond those mentioned above); and as such Blinn-Phong &#039;&#039;is not&#039;&#039; the same as PBR Spec/Gloss workflows, seen in some game engines such as Unity.&lt;br /&gt;
&lt;br /&gt;
=== Importing PBR Materials from External Software: ===&lt;br /&gt;
For creators who work with external tools such as Blender, Adobe Substance Painter, Cinema4D, 3D-Coat, or have used the Unreal or Godot game engines, the use of PBR texture sets should already be familiar.  In fact, some creators have been using tools that use PBR workflows to create content for Second Life and have then been forced to sacrifice visual quality to convert that information into Second Life’s existing Blinn-Phong materials system.  &lt;br /&gt;
&lt;br /&gt;
Second Life is adopting the “Metallic/Roughness” PBR model, and in its ongoing commitment to using Open Source standards whenever it is practical to do so, the glTF 2.0 file format has been chosen as the upload format for PBR Material assets.  One of the primary goals of implementing PBR Materials is to have more continuity from content creation applications towards Second Life, and have more consistent content behavior once it’s inworld.&lt;br /&gt;
&lt;br /&gt;
=== Using Imported Materials in Second Life ===&lt;br /&gt;
[[File:Editing_Material.jpg|thumb|right|Material Editing]]&lt;br /&gt;
For people who build exclusively within Second Life, the addition of PBR also means that there will be a new [[LlGetInventoryType|Inventory Type]] called “Material”.  These new Materials can be purchased on the Marketplace and are shareable like any other permitted object in Second Life.&lt;br /&gt;
&lt;br /&gt;
PBR materials come as a bundle of textures.  These all travel as a single unit and are applied all at once.  If you wish to change tint, transparency, or other similar parameters of the Material you’ll need to modify the Material from the &amp;quot;Editing Material&amp;quot; floater.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Meshes uploaded after the PBR release will look slightly different (and more accurate to your editor of choice) due to a change in the way Second Life handles mesh assets (Previously, some data required for accurate tangent generation was discarded at upload time - this is no longer the case). This will mean that a PBR material applied to a mesh uploaded before PBR launched (28th November 2023) might look incorrect; a simple reupload of the mesh will solve this. &#039;&#039;&#039;This is not required, but recommended.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
Applying existing materials works similarly to existing textures. You have two means of doing so:&lt;br /&gt;
&lt;br /&gt;
==== Drag and Drop ====&lt;br /&gt;
Simply drag and drop onto the face of a prim or a mesh.  For example, if you have a tiled floor material and you place it on a selected prim cube face that face will now look like a tile floor and reflect light like a tiled floor would with all the material qualities contained in the material.&lt;br /&gt;
[[File:DragAndDrop_Mat_Wiki.png | thumb | none | Drag and Drop Functionality.]]&lt;br /&gt;
&lt;br /&gt;
==== Switch to PBR and select from Inventory ====&lt;br /&gt;
Alternatively, select a face on the prim or mesh you want to apply your material to and choose the “Blinn-Phong” drop-down and change it to “PBR Metallic Roughness”, then select “Choose an item from your inventory” and apply.&lt;br /&gt;
[[File:PBRSelectorV3.png |left| thumb | none | Menu drop-down demonstration.]][[File:Material_Dropdown.jpg|thumb|none|Dropdown as of v7.0.1.689]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Texture Transforms ====&lt;br /&gt;
With PBR materials, texture transforms work in a slightly different way to Blinn-Phong transforms. Blinn-Phong (and OpenGL) has the texture origin in the lower left corner of the texture, whereas glTF materials (and Vulkan) has the texture origin in the &amp;lt;b&amp;gt;upper left&amp;lt;/b&amp;gt; corner.&lt;br /&gt;
&lt;br /&gt;
This means that PBR materials will react differently (namely, they are inverted in the Y (glTF v) axis) to Blinn-Phong materials.&lt;br /&gt;
For a simple solution to convert a Blinn-Phong texture transform to a PBR texture transform, please see [https://community.secondlife.com/forums/topic/507448-lsl-math-for-gltf-transformation/?do=findComment&amp;amp;comment=2678959 this forum thread].&lt;br /&gt;
&lt;br /&gt;
For more information, [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#images see here] and [https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform here].&lt;br /&gt;
&lt;br /&gt;
=== Media-on-a-Prim ===&lt;br /&gt;
Media on a prim will continue to work largely as it has done prior to PBR&#039;s launch.&lt;br /&gt;
&lt;br /&gt;
When using MOAP on a face with a PBR texture, it will function as if the media texture has an override to the &#039;&#039;&#039;Base Color&#039;&#039;&#039; and &#039;&#039;&#039;Emissive&#039;&#039;&#039; maps. Note that the emissive map is overridden, however the emissive tint is not (so, to disable the emissive map you can set the emissive tint to black &amp;lt;0,0,0&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Tools &amp;amp; Tutorials ==&lt;br /&gt;
As the PBR system is new, it is expected that existing users may be confused at first on how everything works. As such, Linden Lab and some third parties have provided some tutorials and informational videos:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=JtjGf06B8ZA Second Life University - PBR Materials]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=BJ1eRTlWDYk Second Life University - How to create PBR Materials]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://substance3d.adobe.com/tutorials/courses/pbrguide Allegorithmic (Adobe) PBR Guide]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The above tutorials require knowledge and access to Adobe&#039;s Substance Painter (subscription based software on Adobe website, or sold as perpetual license with one year of updates on Steam). Below are some open source tools and links found Googling &#039;Blender&#039;, &#039;glTF&#039; (requiring Blender 3.3)&lt;br /&gt;
*&#039;&#039;&#039;[https://www.khronos.org/blog/art-pipeline-for-gltf Khronos Art Pipeline tutorial]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://materialmaker.org Material Maker software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://aiaicapta.in/gltf-packer/ glTF Packer software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://armorpaint.org Armorpaint software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://armorlab.org Armorlab software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://youtu.be/KTPdNUGwIGc glTF Blender Tutorial]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://youtu.be/p7OPRoT6FkY Exporting glTF Alpha Textures tutorial]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== PBR Stand-In Textures and Outdated Viewers ==&lt;br /&gt;
It is worth mentioning that during the adoption period of a new system, a substantial portion of Second Life Residents view the world through third party viewers and mobile viewer solutions that will not have updated to be able to see the new content.  Anyone viewing an object with a PBR Material on a viewer that cannot display it will only see the old Blinn-Phong materials (diffuse, normal, specular).  By default, this is just the classic wood texture.&lt;br /&gt;
&lt;br /&gt;
Those who wish their content to be viewable by as many people as possible, might consider creating a Diffuse texture (with baked lighting, the kind that is easily generated with tools like Substance Painter&#039;s [https://www.youtube.com/watch?v=OwNwygk4LQo Baked Lighting Filter]) and possibly a Normal/Specular map, and applying those to the object they’re placing the PBR material on, prior to applying the PBR material.&lt;br /&gt;
&lt;br /&gt;
While this is an extra step in content creation, and complicates things somewhat, if you wish for your content to be appreciated by all, it’s worth considering adding this extra step to your workflow.  People designing PBR materials for distribution and sale might also consider offering a “Diffuse Only fallback” texture to accompany the PBR material specifically for people who cannot see the PBR content.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Once a PBR Material is applied, the Blinn-Phong textures cannot be updated. To remedy this, remove the PBR Material, then make changes to the Blinn-Phong textures and then reapply the PBR Material.}}&lt;br /&gt;
&lt;br /&gt;
== Understanding and Assisting the New Reflections System ==&lt;br /&gt;
Those creators that work at house-scale, or produce items that can be walked through, can gain additional control over the lighting in their creations by taking the time to fully understand the new toolkit that influences environmental reflections. There is a new type of volume that can be created and appended to object linksets specifically for scenes with these kinds of spaces.  These volumes define a custom area where reflections are calculated, overriding the default solution.  So-called “Reflection Probes” should be placed in a manner such that &#039;&#039;the fewest number of probes fills the largest amount of space possible with minimal overlap&#039;&#039;.  One probe per room is a good reference point for a general living space like a house.  If multiple probes exist in a given area they can cause visual artifacts and negatively impact performance  (also known as viewer lag).  &#039;&#039;&#039;&#039;&#039;Do not affix reflection probes to small creations such as furniture or decor.&#039;&#039;&#039;&#039;&#039;  Small items such as tables, chairs, musical instruments, candle sticks etc should use the reflection sample volume in the space in which they are placed.  They should not have one appended.  It is &#039;&#039;&#039;strongly&#039;&#039;&#039; recommended that any object that contains a reflection sample probe be left as “modify”, so the positioning of the probe can be adjusted or even removed by the owner of the item should they wish.&lt;br /&gt;
&lt;br /&gt;
{{Enote|Reflection probes &#039;&#039;&#039;are not&#039;&#039;&#039; intended for use with planar mirrors, and will look incorrect when being used to do so. Planar mirrors have been flagged for future work, but are not directly in the scope of the PBR project.}}&lt;br /&gt;
&amp;lt;!-- This image has been removed from public view as it is factually incorrect; as it references the blue tint on the floor as originating from the ocean, and not the sky. Note that the public project viewer currently also has a bug wherein the sky contribution on objects is too powerful; this has since been fixed however a public build containing the fix is yet to be released. [[File:ProblePlacementDemo_v.png | right | A visual demonstration of how manual probes interact with the surrounding environment and enhance it.]] --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== When is it Recommended to Create a Reflection Probe? ==&lt;br /&gt;
Manually placed probes are good for cleaning up undesirable noise and lighting from automatically placed probes that may be visually disruptive or confusing.  If you look in the image of the room in a house, on the left hand side of it, you can see a blue tint on the floor, which is the reflected blue light from the sky.  The probe filling half the room blocks this, and when it is extended to completely fill the room and just slightly beyond the thickness of the walls, (lower copy of the image) the problem is solved.  The probe does not need to be a part of the linkset for the rest of the room in order to function; however, once you have them placed it’s possible to add them into linksets like any other prim.&lt;br /&gt;
&lt;br /&gt;
=== Constructing a Reflection Sample Volume ( aka Reflection Probe ) ===&lt;br /&gt;
Start off in the Build Menu &#039;&#039;(Ctrl+3)&#039;&#039; by rezzing a basic prim. Under the &amp;lt;code&amp;gt;Features&amp;lt;/code&amp;gt; tab, at the bottom check the box labelled &amp;lt;code&amp;gt;Reflection Probe&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[File:ProbeUI.jpg | thumb | none | Manual Probe Creation UI.]]&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Reflection Probe&amp;lt;/code&amp;gt;:  Enabling this creates a Reflection Sampling Volume within the bounding box of the prim.&lt;br /&gt;
* &amp;lt;code&amp;gt;Box / Sphere Drop-down&amp;lt;/code&amp;gt;: Choose whether you want this sample to project reflections within a box shaped volume or a spherical one.&lt;br /&gt;
* &amp;lt;code&amp;gt;Dynamic&amp;lt;/code&amp;gt;: Allow skinned objects (e.g. Avatars, animesh, etc.) to be captured in the reflection.&lt;br /&gt;
* &amp;lt;code&amp;gt;Ambiance&amp;lt;/code&amp;gt;: Affects how much objects within the volume are lit as if they have bounced light hitting them from faked indirect illumination. &#039;&#039;See [[PBR_Materials#Fine-Tuning_Reflection_Sample_Volumes|&amp;quot;Fine-Tuning Reflection Sample Volumes&amp;quot;]] for more info.&lt;br /&gt;
* &amp;lt;code&amp;gt;Near Clip&amp;lt;/code&amp;gt;: Sometimes, there will be a large obstruction in the center of the volume you wish to use as a reflection probe. For example, suppose that there is a large architectural supporting structure in the middle of the room, such as a column. If the probe gets placed internally inside this, it will reflect the internal surface of the column instead of the room in which it is placed.  Increasing Near Clip will make the sampling volume exclude objects n meters from the center so they don’t get included in the reflection. &#039;&#039;See [[PBR_Materials#Fine-Tuning_Reflection_Sample_Volumes|&amp;quot;Fine-Tuning Reflection Sample Volumes&amp;quot;]] for more info.&lt;br /&gt;
&lt;br /&gt;
Select an appropriate probe shape for your scene. For example, if you want to create a reflection probe that covers a room, select a &amp;lt;code&amp;gt;Box&amp;lt;/code&amp;gt; shape, or for other objects a &amp;lt;code&amp;gt;Sphere&amp;lt;/code&amp;gt; shape is recommended.&lt;br /&gt;
&lt;br /&gt;
Move the reflection probe and resize it as necessary to fit your desired purpose. For the room example, resize the box so the probe just touches the walls, ceiling and floor of the room.&lt;br /&gt;
&lt;br /&gt;
Naming your probes is also recommended, so other people ( or yourself later on ) will remember why there’s a large transparent prim in the middle of your build.&lt;br /&gt;
&lt;br /&gt;
If you wish to come back and edit the reflection probe later on, enable both &amp;lt;code&amp;gt;Show Reflection Probe Volumes&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Select Reflection Probes&amp;lt;/code&amp;gt; under &amp;lt;code&amp;gt;Build &amp;gt; Options&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Fine-Tuning Reflection Sample Volumes ===&lt;br /&gt;
{{KBwarning|Sample volumes do not react to prim parameter changes.  Options such as “Hollow” or “Taper” or “Shear” do not change how the sampling area behaves.}}&lt;br /&gt;
&lt;br /&gt;
That said, it may be helpful to visualize &amp;lt;code&amp;gt;Near Clip&amp;lt;/code&amp;gt; as the hollowing out of your reflection sample volume, but the hollow and near-clip are not in any way linked and doing so is purely a convenience.  Reflection probe sample volumes only respond to changes in scale and position; however, different probe volumes behave differently when resized.&lt;br /&gt;
* &amp;lt;code&amp;gt;Box&amp;lt;/code&amp;gt; Probes: These are affected by position and non-uniform scale.&lt;br /&gt;
Example: If I create a box reflection probe and scale it to 10m,30m,10m size, the full volume will be affected by the probe.&lt;br /&gt;
* &amp;lt;code&amp;gt;Sphere&amp;lt;/code&amp;gt; Probes: These are affected by position and &#039;&#039;&#039;uniform&#039;&#039;&#039; scale only, otherwise the smallest dimension is used.&lt;br /&gt;
Example: If I create a spherical probe and scale it to 10m,30m, and 10m size , the probe will sample a 10 meter diameter spherical volume at the center of the probe.  However, a 30m,30m,30m sphere will create a sampling volume of a 30 meter diameter sphere.&lt;br /&gt;
[[File:SphereProbeErrror.jpg | thumb | none | A diagram of how spherical probes function (or, don&#039;t function as you may expect) while non-uniformly-scaled.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; controls how ambient light (Found in your [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]]) affects, or does not affect, the contents of the reflection probe, and the intensity of indirect lighting (aka Irradiance).&lt;br /&gt;
This value is influenced by the &amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; value found in the user&#039;s [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]], wherein if the Ambiance value given in the [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]] is higher than the value defined by the probe itself, it inherits the ambiance value of the [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]], or alternately if the ambiance value defined by the probe is higher, the probe&#039;s value is used.&lt;br /&gt;
&lt;br /&gt;
There are a few operation modes that are set with the &amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; value, in tandem with above:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Probe Ambiance behavior&lt;br /&gt;
! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0.00 || Ambient color from the environment applies at full intensity. &#039;&#039;&#039;Default for pre-PBR skies.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0.01 .. 0.99 || Blends ambiance with indirect lighting (sun + emissive or illuminated surfaces) within the probe.&lt;br /&gt;
|-&lt;br /&gt;
| 1.00 || Indirect light applies at full intensity, without ambient color. &#039;&#039;&#039;Default for PBR skies.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 1.01 .. 4.00 || Multiplier for indirect light, makes everything brighter within the probe.&lt;br /&gt;
|-&lt;br /&gt;
| 4.01 .. 100.00 || Multiplier for indirect light, but only further affects light from the sun. (Local lights are capped to 4.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Complex Reflection Probe placement ===&lt;br /&gt;
&lt;br /&gt;
At times, you may wish to place a reflection probe in an area where the reflection probe types (Sphere or Box) do not conform to the shape of the area. For example, a Box probe in a loft room has a triangular shape, which results in &amp;quot;probe bleed&amp;quot; wherein the influence volume affects an area larger than what is needed, and thus bleeds out onto the exterior roof, which is undesirable.&lt;br /&gt;
&lt;br /&gt;
In these cases, you may need to place multiple probes in order to blend together the sample volumes to achieve the desired result. In the loft room example, it&#039;s best to start with 3 box probes, which cover the floor, and each side of the roof. Then, use an additional box probe as &amp;quot;fill&amp;quot; to cover the gap inbetween the 3 probes. The main fill probe may need to be rotated at an angle to get the most coverage possible. If it is not possible to fill the gap between the 3 probes with this fill probe, you may wish to add more box probes to act as &amp;quot;secondary fill&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[TODO: Put photo examples of the above here!]&lt;br /&gt;
&lt;br /&gt;
Alternately, Sphere probes may be used to achieve the same result. Sphere probes have considerably softer blending than box probes, so this may give you the best results in severe bleed conditions.&lt;br /&gt;
&lt;br /&gt;
=== Unsupported use-cases ===&lt;br /&gt;
{{KBwarning|Do not attempt to do anything listed below. These uses are liable to either intentionally be disabled, or stop working in future updates.}}&lt;br /&gt;
You may be tempted to use reflection probes in the following ways, however these use-cases are either intentionally disabled or result in undefined behavior, and may work now but may not in future. &#039;&#039;&#039;You have been warned.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Under any circumstances, you should NOT:&#039;&#039;&#039;&lt;br /&gt;
* Wear a reflection probe. This intentionally does not work. (Reflection probes are a property of the scene, not an individual object / avatar)&lt;br /&gt;
* Attach a reflection probe to a physics-enabled object, e.g. vehicles. This intentionally does not work. (As above, the reflection probes are part of the scene; not part of an individual object. Reflection probes by design do not update in real-time, thus the object would always have incorrect reflections anyway.)&lt;br /&gt;
&lt;br /&gt;
== PBR Material Composition ==&lt;br /&gt;
PBR-defined values are measured by optical sensors and other capture tools and recorded in databases.  These parameters are then put into commercial software to be used by content creators.  Using software designed to create PBR content is always recommended; however, understanding how PBR materials are assembled can assist with editing and compiling them.  Having a direct understanding of what each individual color channel contributes is essential for editing PBR textures without relying upon some of the more advanced toolkits available.&lt;br /&gt;
&lt;br /&gt;
All PBR Values are listed from 0 to 1.0, though in an actual image, the values range from 0 - 255.&lt;br /&gt;
PBR Materials are composed of a set of four specifically designed textures; they are as follows:&lt;br /&gt;
&lt;br /&gt;
===Base Color [ RGB ] + Transparency [ A ]===&lt;br /&gt;
[RGB]: This is the unlit color of the surface.  This differs from the “Diffuse” texture that Second Life uses.  Diffuse textures often include faked reflection and specular information as well as added Ambient Occlusion shadows.  Base Color textures do not get any of this added information.  For metals ( as defined by the metalness value ), Base Color also determines specular reflection color, whereas, in non-PBR systems, this is defined by the Specular texture and tint.  In certain PBR texturing applications, Base Color is sometimes also referred to as “Albedo”.&lt;br /&gt;
&amp;lt;br /&amp;gt;[ A ]: Alpha Channel, dictates the transparency of the entire material overall.&lt;br /&gt;
&lt;br /&gt;
The Base color texture should be devoid of lighting information.&lt;br /&gt;
&lt;br /&gt;
===Occlusion [R] / Roughness [G] / Metalness [B]===&lt;br /&gt;
This texture is composed of 3 unrelated grayscale images stored in 3 different color channels of an RGB texture.&lt;br /&gt;
&amp;lt;br /&amp;gt;[R]: (Ambient) Occlusion is lighting data, &#039;&#039;&#039;removing&#039;&#039;&#039; the need to bake down shadows on to the Base Color map. Note that &#039;&#039;white&#039;&#039; (&amp;lt;1,1,1&amp;gt;) means no occlusion is applied, and &#039;&#039;black&#039;&#039; (&amp;lt;0,0,0&amp;gt;) applies full occlusion.&lt;br /&gt;
&amp;lt;br /&amp;gt;[G]: Roughness data ranges from 0 to 1.0, but the actual range of physical surfaces ranges from approximately 0.05 to 0.985.  No surface is perfectly smooth or completely rough.  The rougher a surface is the less mirror-like it behaves.  &lt;br /&gt;
&amp;lt;br /&amp;gt;[B]: Metalness values are mostly black or white.  Either the material is a conductive metal like copper, or it’s a non-metal like fabric.  0.0 is Non-Metallic, 1.0 is Metallic.  There are almost no materials with gray metalness values.&lt;br /&gt;
&amp;lt;br /&amp;gt;The alpha channel is ignored, as per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification].&lt;br /&gt;
&lt;br /&gt;
{{Enote|The Occlusion map &#039;&#039;&#039;is not&#039;&#039;&#039; controlled by the &amp;quot;Screen Space Ambient Occlusion&amp;quot; toggle in the Advanced Graphics Settings. &#039;&#039;&#039;The Occlusion map is &#039;&#039;always&#039;&#039; enabled.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
===Emissive [ RGB ]===&lt;br /&gt;
This determines the amount and the color of unlit (ignores ambient light conditions) areas of your material. When giving an object a white emissive map, the object will act as if the Blinn-Phong &amp;quot;Fullbright&amp;quot; option was checked. This map is useful for items which are expected to emit light, e.g. a table lamp, where the lamp shade would appear to glow when the lamp is turned on. If you wish to toggle the lamp on and off, it&#039;s recommended to change the Emissive Tint value to black (functionally disabling the emissive map, thus turning the lamp off), and then changing the tint back to your desired color to turn the lamp back on. Leaving the Emissive slot empty is recommended when it’s unused.&lt;br /&gt;
&lt;br /&gt;
Note that glow (The postprocessing effect), is controlled by a separate parameter in the build floater and is intentionally not part of the PBR material window. Glow is modulated by the emissive map, so black areas of an object&#039;s emissive map will not glow, similar to how Blinn-Phong emissive maps and glow work.&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the alpha channel is ignored.&lt;br /&gt;
&lt;br /&gt;
===Normal [ RGB ]===&lt;br /&gt;
The normal maps generated by your baking application / normal map generation toolkit should be compatible with Second Life in most cases.  The most common pitfall is using normal maps generated with “inverted” Green channels, such as those that are used for Direct3D and Unreal Engine.  Normal texture data redirects light in a different direction based on the vectors indicated by the color, so if the green channel is backward, it’ll seem to be “pointing the wrong way”.  To phrase the problem a different way, all the things that should be bumps look like dents, and vice versa.  If this occurs, double check that your settings aren’t for Direct3D and try re-generating it.  Also, taking it into image editing software and inverting the green channel only sometimes is a sufficient fix.&lt;br /&gt;
&lt;br /&gt;
Normal map tangent spaces are an extremely technical subject that most users need not be concerned with as most modern applications default to the correct setting.  However, if your normal maps look drastically different inside Second Life, compared to your source application, and you’ve already confirmed it’s not an inverted green channel, then checking which tangent space settings are being used is the next step.  PBR Normals use Mikkelsen Tangent Space. (Often abbreviated MikkT)  If you are unsure what this means, use similar workflows and settings for Second Life PBR as those that are generally recommended for the Godot 4 game engine.&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the blue channel of a normal map is only allowed to contain values above 0.5 to a max of 1 (255).&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the alpha channel is ignored.&lt;br /&gt;
&lt;br /&gt;
== Material Reference Libraries ==&lt;br /&gt;
As PBR is &#039;&#039;Physically Based&#039;&#039;, you may wish to know how to recreate a real-life material in PBR form. What color should you use? What metalness value should it have?&lt;br /&gt;
&lt;br /&gt;
Fortunately, reference libraries exist which can tell you how to recreate a given material in a PBR workflow.&lt;br /&gt;
&lt;br /&gt;
=== List of Reference Color Palettes &amp;amp; libraries ===&lt;br /&gt;
*&#039;&#039;&#039;[https://physicallybased.info/ Physically Based]&#039;&#039;&#039;&lt;br /&gt;
** Select the &#039;&#039;Godot&#039;&#039; Engine, with the Color Space as &#039;&#039;sRGB Linear&#039;&#039; and Color Representation as &#039;&#039;0-1&#039;&#039; (Depending on what you are doing, you may need to convert the value into gamma-corrected sRGB space, which can be done [https://davengrace.com/dave/cspace/ here.] Enter the value on the 3rd row.)&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=TcTh-1X2FsQ Grzegorz Baran Library]&#039;&#039;&#039;&lt;br /&gt;
**Available on [https://www.youtube.com/watch?v=TcTh-1X2FsQ YouTube (video format, older)], or [https://gbar.gumroad.com/l/cbwkvo PDF (most up-to-date)].&lt;br /&gt;
*&#039;&#039;&#039;[https://seblagarde.wordpress.com/2014/04/14/dontnod-physically-based-rendering-chart-for-unreal-engine-4/ Dontnod Entertainment Library]&#039;&#039;&#039;&lt;br /&gt;
** A very small library, but useful nonetheless.&lt;br /&gt;
*&#039;&#039;&#039;[https://docs.studio-397.com/developers-guide/general-reference/pbr-an-introduction-authoring-guide Studio 397]&#039;&#039;&#039;&lt;br /&gt;
** A small collection of various materials.&lt;br /&gt;
*&#039;&#039;&#039;[http://wiki.polycount.com/wiki/PBR Polycount Wiki]&#039;&#039;&#039;&lt;br /&gt;
** Not so much a library in itself, but links out to other libraries. &#039;&#039;&#039;See the Color Charts section&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== List of PBR asset libraries ===&lt;br /&gt;
*&#039;&#039;&#039;[https://ambientcg.com/ ambientCG]&#039;&#039;&#039;&lt;br /&gt;
** Provides CC0 licensed HDRi, Metal-Rough materials and .sbar (Substance Painter material) files.&lt;br /&gt;
*&#039;&#039;&#039;[https://polyhaven.com/ Poly Haven]&#039;&#039;&#039;&lt;br /&gt;
** Provides CC0 licensed HDRi, Metal-Rough materials and models.&lt;br /&gt;
&lt;br /&gt;
== Uploading Materials ==&lt;br /&gt;
There are two different methods of creating PBR material assets:&lt;br /&gt;
&lt;br /&gt;
=== Method 1 : Direct Upload a glTF File ===&lt;br /&gt;
The most convenient method to create an entirely new material is to directly upload a glTF file from your computer.&lt;br /&gt;
&lt;br /&gt;
This is accessible via &amp;lt;code&amp;gt;Build &amp;gt; Upload &amp;gt; Material...&amp;lt;/code&amp;gt; &lt;br /&gt;
[[File:PBRBuildMenu.jpg | thumb | none | PBR Upload in main build menu. ]]&lt;br /&gt;
&lt;br /&gt;
Select a .GLB or a .GLTF file from your computer and Open it:&lt;br /&gt;
This will give the following window:&lt;br /&gt;
[[File:PBRMatMenu.jpg | thumb | none| The layout of the PBR material creation and editing interface.]]&lt;br /&gt;
&lt;br /&gt;
All of the editable material properties that most creators are familiar with are integrated into the Material&#039;s vertically sizable window on upload.  &lt;br /&gt;
&lt;br /&gt;
Clicking “Save” pays the upload fees and creates the Material assets using the local filename. Using &amp;quot;Save As...&amp;quot; provides a window to name all the assets to unique inworld names. There is a 63 char limit for names of inventory assets but the naming floater on this Materials upload &amp;lt;b&amp;gt;has no length restrictions&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Textures will be uploaded to the inventory&#039;s default Textures folder (NoMod, NoCopy, Transfer) with an appended &amp;quot;type&amp;quot; added to the given name. (ie. asset name (Base Color), asset name (Normal), asset name (Metallic Roughness). In naming the asset keep in mind that the bracketed appendix is included in the name&#039;s 63 character limit. The PBR object is uploaded (Mod, NoCopy, Transfer) to the inventory&#039;s Material folder with the appended text (Material). Presently, naming the glTF asset from Save As... will NOT append (Material) to the inventory&#039;s name. Saving from the locally named file will.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Each individual texture used to compose the Material object is charged a separate upload fee. (ie. If you have 4 textures (Base Color, Metallic Roughness, Emissive, Normal) you are charged 4 times. If you only have a Normal and Metallic-Roughness texture you will be charged for 2, etc.)}}&lt;br /&gt;
&lt;br /&gt;
=== Method 2 : Edit a Blank. Create PBR Materials without a glTF File ===&lt;br /&gt;
If you have the necessary textures to compose a PBR material but do not have a glTF file you will need to build a Material from inventory.  Make sure your textures are in the correct format, with the correct data in the proper channels.  This requires some understanding of how [[PBR_Materials#PBR_Material_Composition:|PBR materials work]].  Once you have that, upload your textures as you normally would.&lt;br /&gt;
&lt;br /&gt;
[[File:PBRMenuUI.jpg | thumb | none | The PBR Materials UI in the Build Floater.]]&lt;br /&gt;
&lt;br /&gt;
* Find the &amp;quot;Materials&amp;quot; folder in your inventory, right click, and select &amp;quot;New Material&amp;quot; from the context menu.&lt;br /&gt;
* Name your new material something appropriate (E.g. &amp;quot;Red Bricks&amp;quot; for a red brick wall, etc.)&lt;br /&gt;
* Right click on your new material, and select &amp;quot;Open&amp;quot; from the context menu.&lt;br /&gt;
* Upload your PBR textures using the standard texture workflow (Usually, under &amp;lt;code&amp;gt;Build &amp;gt; Upload &amp;gt; Image&amp;lt;/code&amp;gt;).&lt;br /&gt;
* In the material window, select the appropriate maps that you just uploaded in their respective slots.&lt;br /&gt;
{{KBwarning|While there may be a temptation to try and place a Blinn-Phong “Diffuse” texture into the “Base Color” slot and a Blinn-Phong “Specular&amp;quot; into the “Metallic/Roughness” slot, doing so will not produce desirable results.  However, trying this won’t generate any warnings as there is no way to check that you’ve put the right texture type into the inputs, and the material will save and be applicable to objects.  &#039;&#039;&#039;Unfortunately, what will be created is not a functional PBR material.  Please do not do this.&#039;&#039;&#039;}}&lt;br /&gt;
* Once done, verify the material parameters are correct (E.g. Base color tint, M/R factor, Emissive tint, etc.)&lt;br /&gt;
* Save your new material.&lt;br /&gt;
* EITHER: [[PBR Materials#Drag_and_Drop|Drag and drop the material onto a rezzed prim]], OR edit the prim, click the &amp;quot;Blinn-Phong&amp;quot; drop-down, select &amp;quot;PBR Metallic-Roughness&amp;quot;, and click &amp;quot;Choose from Inventory&amp;quot;, and select your new material.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- * Create a test cube and select a face, switch it to be a PBR material.&lt;br /&gt;
* Select “Choose from Inventory”&lt;br /&gt;
* Instead of choosing something from your inventory, choose “Blank” and click “Ok”&lt;br /&gt;
* Choose “Edit Selected”,  this will open up the material creation UI.&lt;br /&gt;
* Choose your appropriate maps from your texture inventory and slot them into their matching locations.&lt;br /&gt;
{{KBwarning|While there may be a temptation to try and place a pre-PBR “Diffuse” texture into the “Base Color” slot and a pre-PBR “Specular&amp;quot; into the “Metallic/Roughness” slot, doing so will not produce desirable results.  However, trying this won’t generate any warnings as there is no way to check that you’ve put the right texture type into the inputs, and the material will save and be applicable to objects.  &#039;&#039;&#039;Unfortunately, what will be created is not a functional PBR material.  Please do not do this.&#039;&#039;&#039;}}&lt;br /&gt;
* Click “Save to Inventory”, this will bring up a prompt to name your new PBR material. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Creating Color Variants ===&lt;br /&gt;
After you’ve uploaded your first Material for your project, if you’re a store owner who would like to release more than one color palette for your creation, as many clothing and furniture designers find it useful to do, &#039;&#039;&#039;it’s important to not upload a new glTF file for every single variant of your Material&#039;&#039;&#039;.  Most variant materials, only the base color will change.  The Occlusion, Roughness, Metalness, Emissive and Normal map texture slots will remain the same.  Since these maps have already been uploaded once, if we upload a second glTF File, they will be duplicated.  Having different copies of the same texture, with differing UUID’s means that they will clog up download bandwidth and video card memory (not to mention your inventory as well).  This is very bad for Second Life.  So, for this reason, it’s recommended that when you create texture variants, you upload the additional copies of the base-color texture separately.  Then open your newly uploaded material in edit mode, choose the Base Color texture, and change it out for one of your newly uploaded Base Color textures, and click “Save As”.  This will create a second copy of your material that uses all the correct texture maps without needlessly duplicating them and causing additional lag.&lt;br /&gt;
&lt;br /&gt;
{{KBtip| You can also avoid uploading additional textures altogether by uploading a white color variant, then making use of the Base Color Tint parameter!}}&lt;br /&gt;
&lt;br /&gt;
== Double Sided Parameter : Uses and Dangers ==&lt;br /&gt;
“Double Sided” is a new property unique to Materials.  When “Double Sided” is checked, the surface upon which this material is placed will be drawn twice; once for the outward-facing portion of the surface, and a second time for the inward-facing side of the surface that is normally invisible without a double-sided material.  This option should only be used for very specific meshes that were designed to be used with double-sided materials, since placing materials with this parameter checked on normal objects will simply cause the viewer to draw it twice ( and thereby create additional viewer lag ) for no observable change.  It is &#039;&#039;&#039;very strongly recommended&#039;&#039;&#039; that this option be unchecked for any material that is to be distributed for general use.  Even more so if the “Modify” permission is revoked.  If you wish to distribute a version of a material that has “Double Sided” checked, please include a second copy of the material that has “Double Sided” unchecked as well, with an accompanying explanation to the next user as to why this was done.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
*LeafTextures_DoubleSided&lt;br /&gt;
*LeafTextures_SingleSided&lt;br /&gt;
&lt;br /&gt;
When designing mesh content for use with the double-sided material parameter, it is also &#039;&#039;very strongly recommended that you separate the triangles you intend to use the double-sided material upon into a separate mesh “face”&#039;&#039;, so as to not unintentionally render the portions of the mesh that already have triangles designed to represent the internal portion of the object a second time.&lt;br /&gt;
&lt;br /&gt;
== LSL Scripting ==&lt;br /&gt;
&lt;br /&gt;
Documentation for the LSL interface with glTF materials can be found on the following pages:&lt;br /&gt;
*[[GLTF Overrides]]&lt;br /&gt;
*[https://wiki.secondlife.com/wiki/Category:LSL_Material Category:LSL Material]&lt;br /&gt;
&lt;br /&gt;
== Recommended Application Settings ==&lt;br /&gt;
The glTF file format is widely adopted and many applications have export functionality for this type of file.&lt;br /&gt;
Below is a non-comprehensive list of some of the more popular applications that export glTF files.&lt;br /&gt;
&lt;br /&gt;
{{KBtip|&#039;&#039;&#039;The recommended HDRi for use in Second Life content creation is available at [https://github.com/Jenna-Huntsman/Second-Life-Resources/tree/main/PBR/HDRi this Github repository].&#039;&#039;&#039; This HDRi will closely match the lighting of objects under the PBR &amp;quot;Midday&amp;quot; environment preset.}}&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|While every effort has been made to ensure the recommended HDRi matches SL as close as possible, you may need to adjust the &amp;quot;Environment Exposure&amp;quot; parameter in your editing program to get 1:1 results with SL.}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===[https://www.blender.org/ Blender]===&lt;br /&gt;
&lt;br /&gt;
{{KBwarning|Older Blender versions have bugs in their glTF export tools which make their output incompatible with Second Life. &#039;&#039;&#039;Use of Blender versions of 3.3 and above are highly recommended to avoid issues.&#039;&#039;&#039;}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{{KBcaution|Please ensure that you correctly set up an HDRi in your viewport, as otherwise the rendering in Blender and in SL will not match. Please see [[PBR Materials#Recommended_Application_Settings|here for a reference HDRi]]. &#039;&#039;&#039;Please see [https://www.polygonartists.com/hdri-environment-maps-in-blender/ this tutorial].&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
Upon starting your project:&lt;br /&gt;
&lt;br /&gt;
Under: &amp;lt;code&amp;gt;Scene &amp;gt; Render Properties &amp;gt; Color Management&amp;lt;/code&amp;gt;:&lt;br /&gt;
*{{code|View Transform: Standard}}&lt;br /&gt;
*{{code|Sequencer: Linear ACEScg}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Create your materials within Blender using Principled BSDF Shader Nodes and the glTF Settings node.  Export them according to the official &#039;&#039;&#039;[https://docs.blender.org/manual/en/3.6/addons/import_export/scene_gltf2.html Blender Documentation.]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Second Life does not support BSDF Clearcoat, Subsurface, Anisotropy or Transmission parameters at this time.}}&lt;br /&gt;
&lt;br /&gt;
For materials creation, 2 example .blend files are provided, one using separate Occlusion, Roughness and Metallic textures (&amp;quot;Long Form&amp;quot;), and one that handles a pre-packed ORM map (&amp;quot;Short Form&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
The below files are intended for use with Blender versions 3.3 and above.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[https://github.com/Jenna-Huntsman/Second-Life-Resources/blob/main/glTF%20Node%20Tree%20-%20Long%20Form.blend glTF Node Tree - Long Form.blend]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[https://github.com/Jenna-Huntsman/Second-Life-Resources/blob/main/glTF%20Node%20Tree%20-%20Short%20Form.blend glTF Node Tree - Short Form.blend]&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.adobe.com/products/substance3d-painter.html Adobe Substance 3D Painter] === &lt;br /&gt;
{{KBcaution|While Substance Painter is generally considered to be stable, in some (rare) situations Substance may output a malformed glTF which will be rejected by the viewer. If this happens, import your materials into Blender, then export a glTF from Blender - this should be fixed in a future version of Substance.}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{{KBcaution|Please ensure that you correctly set up an HDRi in your viewport, as otherwise the rendering in Substance and in SL will not match. Please see [[PBR Materials#Recommended_Application_Settings|here for a reference HDRi]]. Please see [http://y2u.be/6goXJ2CJzaM this tutorial].}}&lt;br /&gt;
When starting your project:&lt;br /&gt;
&lt;br /&gt;
*{{code|Template: PBR - Metallic Roughness Alpha-test (starter_assets)}}&lt;br /&gt;
&lt;br /&gt;
OR&lt;br /&gt;
&lt;br /&gt;
*{{code|Template: PBR - Metallic Roughness Alpha-blend (starter_assets)}}&lt;br /&gt;
&lt;br /&gt;
Verify the following settings:&lt;br /&gt;
*{{code|Document Resolution: [Set as desired]}}&lt;br /&gt;
*{{code|Normal Map Format: OpenGL (Y+)}}&lt;br /&gt;
*{{code|Compute Tangents Per Fragment: YES}}&lt;br /&gt;
&lt;br /&gt;
Under &amp;quot;Color Management&amp;quot;:&lt;br /&gt;
{{KBtip| For a more in-depth explanation of the below settings, and issues that you may encounter, visit [https://mrlixm.github.io/blog/substance-painter-color-management/#aces-workflow this page].}}&lt;br /&gt;
*{{code|Color Management: OpenColorIO}}&lt;br /&gt;
*{{code|OpenColorIO Configuration: ACES 1.0.3}}&lt;br /&gt;
*{{code|Standard sRGB color space: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|8 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|16 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|Material color space default: Utility - sRGB - Texture}}&lt;br /&gt;
&lt;br /&gt;
When exporting:&lt;br /&gt;
&lt;br /&gt;
Settings Tab - Global Settings&lt;br /&gt;
*{{code|Output template: glTF PBR Metal Roughness}}&lt;br /&gt;
*{{code|Size: Based on each Texture Set&#039;s size}}&lt;br /&gt;
*{{code|Padding: Dilation + default background color - 8 bit}} &#039;&#039;&#039;OR&#039;&#039;&#039; {{code|Padding: Dilation infinite}}&lt;br /&gt;
{{KBtip|If you encounter color banding (AKA posterization) in your base color texture after export, you may wish to enable dithering.}}&lt;br /&gt;
*To enable dithering, open the {{code|Export Textures}} window, under the {{code|Settings}} tab, enable an override for the Base Color texture and swap the output format from {{code|8 bits}} to {{code|8 bits + dithering}}&lt;br /&gt;
&lt;br /&gt;
Settings Tab - Per-Material settings&lt;br /&gt;
*{{code|Output maps &amp;gt; Normal Map: 8 bits}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt;[[File:Substance_Painter_-_Export_Global_settings.png|thumb|Settings Tab - Global Settings]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt;[[File:Substance_Painter_-_Export_Per-Material_settings.png|thumb|Settings Tab - Per-Material settings]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.adobe.com/products/substance3d-designer.html Adobe Substance 3D Designer] === &lt;br /&gt;
{{KBcaution|Substance Designer &#039;&#039;does not support glTF output&#039;&#039;, unlike Substance Painter.}}&lt;br /&gt;
&lt;br /&gt;
Under preferences, verify the following &#039;&#039;&#039;Project&#039;&#039;&#039; settings:&lt;br /&gt;
*{{code|Color Management: OpenColorIO}}&lt;br /&gt;
*{{code|OpenColorIO Configuration: ACES 1.0.3}}&lt;br /&gt;
*{{code|8 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|16 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|Floating point images: ACES - ACEScg}}&lt;br /&gt;
*{{code|2D and 3D View Display Default: sRGB}}&lt;br /&gt;
&lt;br /&gt;
When exporting (Export Outputs):&lt;br /&gt;
*{{code|basecolor: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|normal: Utility - Raw}}&lt;br /&gt;
*{{code|roughness: Utility - Raw}}&lt;br /&gt;
*{{code|metallic: Utility - Raw}}&lt;br /&gt;
*{{code|ambientocclusion: Utility - Raw}}&lt;br /&gt;
&lt;br /&gt;
If you use a custom node to output a [[PBR_Materials#Occlusion_.5BR.5D_.2F_Roughness_.5BG.5D_.2F_Metalness_.5BB.5D|pre-compiled ORM map]], this should also be set to Raw.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.maxon.net/en/cinema-4d Cinema 4D] === &lt;br /&gt;
Please follow the documentation provided in this [https://www.bakedpixels.nl/blog/export-to-gltf-with-cinema-4d blog post].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===[https://3dcoat.com/ 3DCoat]===&lt;br /&gt;
{{KBwarning|As of version &amp;quot;2022-58&amp;quot;, 3DCoat is unable to produce a spec-compliant glTF file, and thus should not be used for Second Life. &#039;&#039;&#039;Use at your own risk - you &#039;&#039;will&#039;&#039; have issues!}}&lt;br /&gt;
&lt;br /&gt;
glTF export has been implemented since 3DCoat version 2020 and is found in the &amp;lt;code&amp;gt;File &amp;gt;Export &amp;gt; Export to glTF &amp;gt; glTF Separate (gltf + .bin + textures)&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://quixel.com/mixer Quixel Mixer] === &lt;br /&gt;
Quixel Mixer has no glTF output, however, it does generate the functional textures, though they do need to be edited and combined in photo editing software.&lt;br /&gt;
Export them by going to &amp;lt;code&amp;gt;Export Target &amp;gt; Custom&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Texture Preset: Metalness Maps&lt;br /&gt;
&lt;br /&gt;
Select: &lt;br /&gt;
&lt;br /&gt;
Albedo , Roughness, Normal, AO , Metalness  and ( if need be ) Emissive.&lt;br /&gt;
&lt;br /&gt;
Click “Export to Disk” and open the folder that the files were placed into.&lt;br /&gt;
&lt;br /&gt;
Albedo is the Base Color texture in this case.&lt;br /&gt;
&lt;br /&gt;
AO , Roughness , Metalness get combined into the ORM map as per [[PBR_Materials#PBR_Material_Composition|this explanation.]]&lt;br /&gt;
&lt;br /&gt;
Normal Map : Quixel generates Direct3D Normal Maps. The green channel needs to be inverted as [[PBR_Materials#Normal_.5B_RGB_.5D_|per here.]]&lt;br /&gt;
&lt;br /&gt;
Upload to Second Life using [[PBR_Materials#Method_2_:_Edit_a_Blank._Create_PBR_Materials_without_a_GLTF_File|Method 2.]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.autodesk.eu/products/3ds-max/overview Autodesk 3DS Max 2023] === &lt;br /&gt;
Autodesk just added the ability to create glTF files using their new glTF Material and glTF Export functionalities as outlined in &#039;&#039;&#039;[https://help.autodesk.com/view/3DSMAX/2023/ENU/?guid=GUID-EFBB037D-C4EB-42D2-9CE1-30FCAD483C31 Autodesk&#039;s official documentation]&#039;&#039;&#039;.  All prior versions of Autodesk software do not have this functionality.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.materialmaker.org/ Material Maker] ===&lt;br /&gt;
While [https://www.materialmaker.org/ Material Maker] does not currently export directly to glTF ([https://github.com/RodZill4/material-maker/issues/479 This may be added in future]), materials created with this program are compatible with Second Life.&lt;br /&gt;
&lt;br /&gt;
Export your materials using the &#039;&#039;&#039;Godot 4 ORM&#039;&#039;&#039; export preset.&lt;br /&gt;
&lt;br /&gt;
Upload to Second Life using [[PBR_Materials#Method_2_:_Edit_a_Blank._Create_PBR_Materials_without_a_GLTF_File|Method 2.]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== Adobe Photoshop ===&lt;br /&gt;
&#039;&#039;&#039;While Photoshop is not officially a PBR authoring tool&#039;&#039;&#039;, the changes to Alpha Blending (changing into Linear space from sRGB) may require you to change your PS settings to get consistent results. Please see [http://www.kiransprojects.com/blog/2014/photoshop-blending-is-broken/ this article] for more information. (See the section titled &amp;quot;A Partial Solution&amp;quot;). Alternatively, [https://www.reddit.com/r/photoshop/comments/61clf8/how_to_fix_photoshops_incorrect_and_ugly_color/ this Reddit post] also gives a few options on how to achieve blending in the correct manner.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== GIMP ===&lt;br /&gt;
&#039;&#039;&#039;While GIMP is not officially a PBR authoring tool&#039;&#039;&#039;, the changes to Alpha Blending (changing into Linear space from sRGB) in Second Life will make textures created in GIMP display their alphas correctly (GIMP defaults to linear alpha calculation).&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting content ==&lt;br /&gt;
&lt;br /&gt;
If you upload a piece of PBR content which does not match your editor, the advice from Linden Lab is to &#039;&#039;&#039;STOP: File a Feedback ticket. Do not attempt to &amp;quot;fix&amp;quot; the content.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
That said, there are some troubleshooting steps which you can do yourself:&lt;br /&gt;
* Check that you are using the PBR Linden Midday preset (called &amp;quot;Midday&amp;quot;) &#039;&#039;not &amp;quot;Midday (Legacy)&amp;quot;&#039;&#039;. Other environments may not match the reference HDRi, causing some visual differences. (This is intended behaviour, and content should be able to be viewed under any light correctly, but for troubleshooting purposes this may be required).&lt;br /&gt;
** At present, there is a bug with the PBR Linden Midday preset which results in an excessive blue sheen, due to the use of an over-saturated (unrealistic) sky color, among some other issues. This has been fixed in the upcoming glTF Maintenance viewer.&lt;br /&gt;
* Are you using a reflection probe? If not, does the problem reproduce if a manual reflection probe is placed over the object?&lt;br /&gt;
** This is because a common source of the &amp;quot;blue sheen&amp;quot; in interior scenes is the use of an auto-probe. Auto-probes sample their surroundings, and combined with the approximate (and often incorrect) placement this will mean the sky is sampled on all sides, thus meaning the reflected light from the surroundings (which counters the blue light from the sky) is absent, leading to a larger-than-expected level of sky contribution on the object. Auto-probe placement can be worse in skyboxes or sky platforms&lt;br /&gt;
* Triple-check the settings used for your editor match the ones given [[PBR_Materials#Recommended_Application_Settings|here]], including the reference HDRi. &#039;&#039;&#039;Any&#039;&#039;&#039; deviation from these settings may cause visual differences between your editor and in-world.&lt;br /&gt;
* Check your content against a glTF reference viewer; such as:&lt;br /&gt;
** [https://modelviewer.dev/editor/ Modelviewer.dev]&lt;br /&gt;
** [https://github.khronos.org/glTF-Sample-Viewer-Release/ Khronos Sample Viewer]&lt;br /&gt;
&lt;br /&gt;
If the above steps fail, then please &#039;&#039;&#039;[https://feedback.secondlife.com/ file some Feedback.]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A good Feedback ticket will include:&lt;br /&gt;
* An LM to a location where the problem can be examined in-world.&lt;br /&gt;
* A copy of the glTF content, attached to the ticket.&lt;br /&gt;
* Screenshots of the representation in-world, in a reference viewer, and in-editor.&lt;br /&gt;
** You should include screenshots of the object inside a manually placed reflection probe, and outside.&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Lua_Alpha&amp;diff=1218676</id>
		<title>Lua Alpha</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Lua_Alpha&amp;diff=1218676"/>
		<updated>2026-03-02T19:46:08Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: /* Second Life Lua (SLua) Alpha */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Warning|This functionality is in alpha. Instability is to be expected, and there may be very sharp edges. At this point it is expected that Luau can crash regions and perform other types of undesirable behavior.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🚨 PLEASE NOTE Memory and performance characteristics, and API specifics may change! Scripts are currently being run in unoptimized form for development purposes.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
= Second Life Lua (SLua) Alpha =&lt;br /&gt;
&lt;br /&gt;
[[File:Luau.png|720px|thumb|right|Luau logo]]&lt;br /&gt;
&lt;br /&gt;
We&#039;re thrilled to announce the launch of the SLua Alpha* for Second Life! This significant update introduces the [https://lua.org Lua scripting language], offering creators enhanced performance, improved memory efficiency, higher memory limit (128 KB), and a more versatile scripting environment.&lt;br /&gt;
&lt;br /&gt;
To get started, [[Try SLua|read the instructions here.]]&lt;br /&gt;
&lt;br /&gt;
Slua has entered its Beta phase in December 2025. It&#039;s available in parts of the main grid! See below for a list of SLua regions.&lt;br /&gt;
&lt;br /&gt;
== What is SLua? ==&lt;br /&gt;
&lt;br /&gt;
SLua is scripting for Second Life based on [https://luau.org Luau], a fast, small, safe, and gradually typed embeddable scripting language derived from Lua. It is designed to be backwards compatible with Lua 5.1, incorporating features from future Lua releases and expanding the feature set with type annotations and a state-of-the-art type inference system. Luau is largely implemented from scratch, with the language runtime being a heavily modified version of the Lua 5.1 runtime, featuring a completely rewritten interpreter and other performance innovations.&lt;br /&gt;
&lt;br /&gt;
== Why Lua? ==&lt;br /&gt;
&lt;br /&gt;
The decision to integrate Lua into Second Life was driven by its ability to meet all the requirements for a scripting engine within the platform. Lua offers a high-quality scripting experience to creators, addressing many of the limitations present in the current LSL (Linden Scripting Language) environment. Its lightweight nature and performance optimizations make it an ideal choice for enhancing the scripting capabilities in Second Life. For more information on why Lua was chosen, please see the [[Lua FAQ]].&lt;br /&gt;
&lt;br /&gt;
== How to Get Started with SLua ==&lt;br /&gt;
&lt;br /&gt;
In order to play with SLua, you&#039;ll need to download our Lua project viewer, and either log onto our [https://lindenlab.freshdesk.com/support/solutions/articles/31000156725-accessing-aditi Aditi beta grid] or go to the SLua-enabled beta regions on the main grid.&lt;br /&gt;
&lt;br /&gt;
* Access the latest build of the SLua-enabled Second Life Viewer from [https://releasenotes.secondlife.com/viewer.html here].&lt;br /&gt;
&lt;br /&gt;
SLua-enabled regions on the main grid:&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Landing/128/128/2 SLua Beta Landing]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Porridge/128/128/2 SLua Beta Porridge]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Eraserhead/128/128/2 SLua Beta Eraserhead]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Glass/128/128/2 SLua Beta Glass]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Void/128/128/2 SLua Beta Void]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Anderson/128/128/2 SLua Beta Anderson]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Snausage/128/128/2 SLua Beta Snausage (Adult)]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Nicolse/128/128/2 SLua Beta Nicolse]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20IsNeat/128/128/2 SLua Beta IsNeat]&lt;br /&gt;
&lt;br /&gt;
SLua-enabled regions on the beta grid:&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Yardang/241/235/27 SLua Yardang]&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Tombolo/241/235/27 SLua Tombolo]&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Mesa/241/235/27 SLua Mesa]&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Tideland/241/235/27 SLua Tideland]&lt;br /&gt;
&lt;br /&gt;
When editing a script in the new Lua project viewer, you&#039;ll notice a new &#039;&#039;&#039;Compiler&#039;&#039;&#039; drop-down near the save button. This drop-down will allow you to select which compiler will be used, as well as which script runtime will be used (LSO2, Mono, Luau).&lt;br /&gt;
&lt;br /&gt;
[[File:Compiler_dropdown.png|Compiler selection dropdown]]&lt;br /&gt;
&lt;br /&gt;
Compiler drop-down options:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LSL: Legacy (LSO2)&#039;&#039;&#039; - Scripts written in LSL, to be run on the old LSO2 VM&lt;br /&gt;
* &#039;&#039;&#039;LSL: Mono&#039;&#039;&#039;- Scripts written in LSL, to be run on the Mono VM&lt;br /&gt;
* &#039;&#039;&#039;Lua&#039;&#039;&#039; - Scripts written in Lua, to be run on the SLua VM&lt;br /&gt;
* &#039;&#039;&#039;LSL: 2025 VM&#039;&#039;&#039;- Scripts written in LSL, to be run on the SLua VM&lt;br /&gt;
&lt;br /&gt;
=== Transitioning from LSL to SLua ===&lt;br /&gt;
* &#039;&#039;&#039;Function Namespacing:&#039;&#039;&#039;&lt;br /&gt;
** In SLua, Linden Lab functions have been moved under the &#039;&#039;&#039;ll&#039;&#039;&#039; namespace.&lt;br /&gt;
** For example:&lt;br /&gt;
*** &#039;&#039;llSay&#039;&#039; becomes &#039;&#039;ll.Say&#039;&#039;&lt;br /&gt;
*** &#039;&#039;llGetPos&#039;&#039; becomes &#039;&#039;ll.GetPos&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Lists&#039;&#039;&#039;&lt;br /&gt;
** Lua indexes begin from 1, unlike LSL where indexes begin from 0.&lt;br /&gt;
** Lua uses &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;{}&amp;lt;/syntaxhighlight&amp;gt; for &#039;&#039;tables&#039;&#039;, unlike LSL where &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;[]&amp;lt;/syntaxhighlight&amp;gt; is used for &#039;&#039;lists&#039;&#039;.&lt;br /&gt;
* Types&lt;br /&gt;
** SLua doesn&#039;t support the usual vector/rotation syntax &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;&amp;lt;x, y, z&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** Instead, these values are created with the functions &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;vector(x, y, z)&amp;lt;/syntaxhighlight&amp;gt;, &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;rotation(x, y, z, s)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** Similar functions exist for &#039;&#039;&#039;uuid&#039;&#039;&#039; (key) and &#039;&#039;&#039;integer&#039;&#039;&#039; (distinct from the built-in &#039;&#039;&#039;number&#039;&#039;&#039; type)&lt;br /&gt;
&lt;br /&gt;
=== SLua Libraries ===&lt;br /&gt;
* &#039;&#039;&#039;Coroutines:&#039;&#039;&#039;&lt;br /&gt;
** SLua supports coroutines, allowing for cooperative multitasking within scripts.&lt;br /&gt;
** Key functions include:&lt;br /&gt;
*** &#039;&#039;coroutine.create&#039;&#039;&lt;br /&gt;
*** &#039;&#039;coroutine.status&#039;&#039;&lt;br /&gt;
*** &#039;&#039;coroutine.resume&#039;&#039;&lt;br /&gt;
** Refer to the [https://luau.org/library#coroutine-library coroutine library documentation] for more details.&lt;br /&gt;
* &#039;&#039;&#039;Bitwise Operations:&#039;&#039;&#039;&lt;br /&gt;
** SLua includes a &#039;&#039;bit32&#039;&#039; library for bitwise operations, enabling more efficient data manipulation.&lt;br /&gt;
** Refer to the [https://luau.org/library#bit32-library bit32 library documentation] for more details.&lt;br /&gt;
* &#039;&#039;&#039;JSON to Table Translation:&#039;&#039;&#039;&lt;br /&gt;
** SLua includes a modified [https://github.com/openresty/lua-cjson lua-cjson library] that translates tables into JSON objects and arrays and back.&lt;br /&gt;
** Vectors and quaternions are converted to strings, which may be changed back using the &#039;&#039;tovector&#039;&#039; and &#039;&#039;toquaternion&#039;&#039; functions, respectfully.&lt;br /&gt;
** Buffers are encoded as Base64 strings. They may be decoded using the &#039;&#039;llbase64.decode&#039;&#039; function.&lt;br /&gt;
** Functions:&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;json_text = lljson.encode(some_table)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;some_table = lljson.decode(json_text)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Base64 Encoding:&#039;&#039;&#039;&lt;br /&gt;
** A Base64 library capable of handling SLua strings and binary buffers, unlike classic &#039;&#039;ll&#039;&#039; namespace functions.&lt;br /&gt;
** Leveraged by the JSON functionality, but can also be called separately.&lt;br /&gt;
** Functions:&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;base64string = llbase64.encode(string_or_buffer)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;str = llbase64.decode(base64string)&amp;lt;/syntaxhighlight&amp;gt; for strings&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;buf = llbase64.decode(base64string, true)&amp;lt;/syntaxhighlight&amp;gt; for buffers&lt;br /&gt;
* &#039;&#039;&#039;Standard Library:&#039;&#039;&#039;&lt;br /&gt;
** SLua comes equipped with a standard library of functions designed to manipulate built-in data types.&lt;br /&gt;
** Explore the [https://luau.org/library Luau Standard Library] for a comprehensive list of available functions.&lt;br /&gt;
&lt;br /&gt;
== Feedback and Support ==&lt;br /&gt;
&lt;br /&gt;
We encourage all creators to explore the new scripting capabilities and provide feedback. Your insights are invaluable in refining and enhancing this feature. For more information and to share your experiences, please refer to our [[Lua FAQ]].&lt;br /&gt;
&lt;br /&gt;
== Example Scripts ==&lt;br /&gt;
&lt;br /&gt;
To help you get started, we&#039;ve assembled some example scripts that demonstrate the capabilities of SLua. These scripts cover various functionalities and can serve as a foundation for your own creations. Please feel free to propose changes to these scripts, or modify them to your heart&#039;s desire!&lt;br /&gt;
&lt;br /&gt;
=== default_script.lua ===&lt;br /&gt;
This script is roughly equivalent to the default &amp;quot;new script&amp;quot; that gets created for LSL.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function state_entry()&lt;br /&gt;
   ll.Say(0, &amp;quot;Hello, Avatar!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
LLEvents:on(&amp;quot;touch_start&amp;quot;, function(detected: {DetectedEvent})&lt;br /&gt;
   ll.Say(0, &amp;quot;Touched.&amp;quot;)&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
-- Simulate the state_entry event&lt;br /&gt;
state_entry()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dialog.lua ===&lt;br /&gt;
This script demonstrates how one can interact with dialog menus.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Define the menu buttons and dialog message.&lt;br /&gt;
local buttons = {&amp;quot;-&amp;quot;, &amp;quot;Red&amp;quot;, &amp;quot;Green&amp;quot;, &amp;quot;Yellow&amp;quot;}&lt;br /&gt;
local dialogInfo = &amp;quot;\nPlease make a choice.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local ToucherID = nil&lt;br /&gt;
local dialogChannel = nil&lt;br /&gt;
local listenHandle = nil&lt;br /&gt;
local timerHandle = nil&lt;br /&gt;
&lt;br /&gt;
-- This function is called when the script first starts.&lt;br /&gt;
function state_entry()&lt;br /&gt;
    -- Get the object&#039;s key and compute a dialog channel number.&lt;br /&gt;
    local key = ll.GetKey()&lt;br /&gt;
    -- Extract the last 7 characters of the key and convert it from hex.&lt;br /&gt;
    dialogChannel = -1 - tonumber(string.sub(tostring(key), -7, -1), 16)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Called when the object is touched.&lt;br /&gt;
LLEvents:on(&amp;quot;touch_start&amp;quot;, function(detected: {DetectedEvent})&lt;br /&gt;
    ToucherID = detected[1]:getKey()&lt;br /&gt;
    -- If there is already a listen handle, then remove it&lt;br /&gt;
    if listenHandle then&lt;br /&gt;
        ll.ListenRemove(listenHandle)&lt;br /&gt;
    end&lt;br /&gt;
    listenHandle = ll.Listen(dialogChannel, &amp;quot;&amp;quot;, ToucherID, &amp;quot;&amp;quot;)&lt;br /&gt;
    ll.Dialog(ToucherID, dialogInfo, buttons, dialogChannel)&lt;br /&gt;
    -- Set a 60-second timer for response.&lt;br /&gt;
    if timerHandle then&lt;br /&gt;
        LLTimers:off(timerHandle)&lt;br /&gt;
    end&lt;br /&gt;
    timerHandle = LLTimers:once(60,timeoutListen)&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
-- Called when a dialog response is received.&lt;br /&gt;
LLEvents:on(&amp;quot;listen&amp;quot;, function(channel, name, sender_id, message)&lt;br /&gt;
    if message == &amp;quot;-&amp;quot; then&lt;br /&gt;
        -- Redisplay the dialog if the &amp;quot;-&amp;quot; option is selected.&lt;br /&gt;
        ll.Dialog(ToucherID, dialogInfo, buttons, dialogChannel)&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
    -- Stop the timer, and stop the listening handler.&lt;br /&gt;
    ll.ListenRemove(listenHandle)&lt;br /&gt;
    listenHandle = nil&lt;br /&gt;
    if timerHandle then&lt;br /&gt;
        LLTimers:off(timerHandle)&lt;br /&gt;
    end&lt;br /&gt;
    -- Let the user know what they selected&lt;br /&gt;
    ll.Say(0, `You selected {message}`)&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
-- Called when the timer expires.&lt;br /&gt;
function timeoutListen()&lt;br /&gt;
    -- Stop the timer and clean up the listener.&lt;br /&gt;
    if listenHandle then&lt;br /&gt;
        ll.ListenRemove(listenHandle)&lt;br /&gt;
        ll.Whisper(0, &amp;quot;Sorry. You snooze; you lose.&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Invoke state_entry on startup, since simulator doesn&#039;t invoke &lt;br /&gt;
-- it like it does in LSL&lt;br /&gt;
state_entry()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== user_input_coroutine.lua ===&lt;br /&gt;
This script demonstrates [https://www.lua.org/pil/9.html coroutines] and how they can simplify the overarching logic of a script, enabling us to write the bulk of our multi-event code within a centralized function instead of fragmenting across separate event handlers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Wait for user input mid-function before doing something useful with it.&lt;br /&gt;
main = function(toucher)&lt;br /&gt;
    local handle = ll.Listen(0, &amp;quot;&amp;quot;, toucher, &amp;quot;&amp;quot;)&lt;br /&gt;
    local event = touch_start   -- save function for later&lt;br /&gt;
    touch_start = nil           -- disable touch_start&lt;br /&gt;
&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Do you want pants or gloves?&amp;quot;)&lt;br /&gt;
    local clothing = coroutine.yield() -- pause the routine&#039;s execution here&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;For men or women?&amp;quot;)&lt;br /&gt;
    local gender = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Favorite color?&amp;quot;)&lt;br /&gt;
    local color = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Here&#039;s &amp;quot;..color..&amp;quot; &amp;quot;..clothing..&amp;quot; for &amp;quot;..gender)&lt;br /&gt;
&lt;br /&gt;
    ll.ListenRemove(handle)&lt;br /&gt;
    touch_start = event -- restore touch_start&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
LLEvents:on(&amp;quot;touch_start&amp;quot;,function(detected: {DetectedEvent})&lt;br /&gt;
    local toucher = detected[1]:getKey()&lt;br /&gt;
    routine = coroutine.create(main)    -- new coroutine&lt;br /&gt;
    coroutine.resume(routine, toucher)  -- run coroutine (with one argument)&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
-- When the coroutine is suspended, incoming events can be handled&lt;br /&gt;
-- and we can resume() execution of the routine&lt;br /&gt;
-- and pass any number of arguments to be returned by yield()&lt;br /&gt;
LLEvents:on(&amp;quot;listen&amp;quot;, function(channel, name, id, message)&lt;br /&gt;
    coroutine.resume(routine, message)&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== multi_user_input_coroutine.lua ===&lt;br /&gt;
Following from the above example, how can we handle multiple users? This is where coroutines shine.&lt;br /&gt;
&lt;br /&gt;
Instead of disabling touches to prevent others from interacting with the object, we can create new copies of the coroutine each time an avatar touches the object. We can then resume whichever coroutine is needed, based on the avatar, while all of them track their own progress separately and automagically.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Key: avatar uuid; Value: coroutine thread&lt;br /&gt;
routines = {}&lt;br /&gt;
&lt;br /&gt;
main = function(toucher)&lt;br /&gt;
    local handle = ll.Listen(0, &amp;quot;&amp;quot;, toucher, &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Do you want pants or gloves?&amp;quot;)&lt;br /&gt;
    local clothing = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;For men or women?&amp;quot;)&lt;br /&gt;
    local gender = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Favorite color?&amp;quot;)&lt;br /&gt;
    local color = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Here&#039;s &amp;quot;..color..&amp;quot; &amp;quot;..clothing..&amp;quot; for &amp;quot;..gender)&lt;br /&gt;
&lt;br /&gt;
    ll.ListenRemove(handle)&lt;br /&gt;
    routines[toucher] = nil -- Remove from collection&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
LLEvents:on(&amp;quot;touch_start&amp;quot;,function(detected: {DetectedEvent})&lt;br /&gt;
    local toucher = detected[1]:getKey()&lt;br /&gt;
    local routine = routines[toucher]&lt;br /&gt;
    if not routine then -- New user needs new routine&lt;br /&gt;
        routine = coroutine.create(main)&lt;br /&gt;
        routines[toucher] = routine -- Add to collection&lt;br /&gt;
        coroutine.resume(routine, toucher)&lt;br /&gt;
    end&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
LLEvents:on(&amp;quot;listen&amp;quot;, function(channel, name, id, message)&lt;br /&gt;
    coroutine.resume(routines[id], message) -- Resume a specific coroutine&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More Examples ===&lt;br /&gt;
&lt;br /&gt;
* Find more example scripts at [[Luau Example Scripts]]&lt;br /&gt;
&lt;br /&gt;
* [https://roblox.github.io/lua-style-guide/gotchas/ Lua Gotchas, Footguns and Other Hazards]&lt;br /&gt;
&lt;br /&gt;
[[Category:Lua]]&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=PBR_Materials&amp;diff=1218675</id>
		<title>PBR Materials</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=PBR_Materials&amp;diff=1218675"/>
		<updated>2026-03-01T08:26:35Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Small rewrite of the introduction.. hopefully less dense, more practical /* What is it and Why is it used? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{multi-lang|1=PBR_Materials|2=/en}}&lt;br /&gt;
&#039;&#039;&#039;Physically Based Rendering&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{{note|1=This page is being actively updated with new content and links as GLTF PBR Materials and related new features are being released. Contributions to this page may be from Second Life residents and staff members alike.}}&lt;br /&gt;
&lt;br /&gt;
[[Category:glTF]]&lt;br /&gt;
&lt;br /&gt;
== What is it and Why is it used? ==&lt;br /&gt;
&lt;br /&gt;
The term “Physically Based Rendering” or “PBR” is a technical term for composing images in computer software.  It refers to a collection of complex mathematical algorithms that attempt to &#039;&#039;accurately represent the ways that light interacts with objects in the real world.&#039;&#039; In other words, focus is put on the real physical properties of materials.&lt;br /&gt;
&lt;br /&gt;
In the real world, light&#039;s unique behavior against metal is how the human eye can recognize &amp;quot;that object is made of metal&amp;quot; without us actually reaching out and touching it.   The way a metal reflects light differs from that of a polished plastic or some other material, and these differences have been quantified by science.&lt;br /&gt;
&lt;br /&gt;
Older techniques, such as the existing Blinn-Phong reflection model in Second Life, are more naive approximations of light that lack many nuances (eg. conductivity, microsurfaces, fresnel, energy conservation) that are now possible to simulate in real time.&lt;br /&gt;
&lt;br /&gt;
==== How it applies to Second Life ====&lt;br /&gt;
By mimicking real-world physics principles in the virtual world it allows for the creation of more immersive recognizable realistic spaces, but also it helps us relate to fantastical worlds a little better too.  While we may not be familiar with what a newly imagined creation is, a metal&#039;s inherent metal-ness and aged wood&#039;s inherent wood-ness remain constant, making it easier to intuitively understand what we are interacting with in a virtual environment.&lt;br /&gt;
&lt;br /&gt;
Because tying the mathematics to simulate materials in virtual spaces to how they behave in the real-world makes things more immediately recognizable, PBR has become the foundation for creating imagined worlds over the last decade.  The metallic shine of exoskeletal armor in superhero movies is driven by a PBR workflow, as is the plastic sheen of toys or the glint of frozen ice in animated classics.  Now we are moving to bring this standardized quality to your home in Second Life.&lt;br /&gt;
&lt;br /&gt;
Bringing PBR to Second Life means updating the basic calculations of how light is represented and interacts with the world of Second Life.  The goal is to integrate these changes while minimally changing how everything that presently exists in Second Life (designed prior to the introduction of PBR) looks.  While the preservation of creative intent and the aesthetic appeal of items users have enjoyed for over two decades of Second Life is always a priority, Second Life is an ever-evolving platform, and to continue to do so, some changes are inevitable.&lt;br /&gt;
&lt;br /&gt;
Lastly, while we are attempting to mimic real-world reflections and material properties, Second Life has to run on a wide variety of devices, so some shortcuts have to be made.  Reflections are not mirror-perfect, as has often been a long-standing hope and request from Residents.  While the addition of a reflection system does bring dreams of distortion free mirrors for our avatars in Second Life closer to reality, unfortunately, due to the calculation requirements of doing perfect reflections, mirrors are sadly still, for the moment, not practical. Please see the note in the [[#Understanding_and_Assisting_the_New_Reflections_System|section below]] for more info.&lt;br /&gt;
&lt;br /&gt;
== What does this mean for Residents? ==&lt;br /&gt;
[[File:PrePostPBR.png | thumb | right | The lamp in this screenshot uses [[PBR_Materials#Nomenclature_changes|Blinn-Phong]] (Classic SL Materials), and was created before PBR&#039;s integration into SL; and demonstrates some of the visual differences to existing content. The environment used for the screenshot is the viewer&#039;s default Midday preset (Note that the PBR viewer has a new Midday preset).&lt;br /&gt;
]]&lt;br /&gt;
=== Changes to Existing Content ===&lt;br /&gt;
The largest change by far is the addition of an environmental reflection system to Second Life.  For most existing items, this change shouldn’t have a drastic immediately observable impact.  A few things in your inventory that you already own may appear more reflective with the new graphics configuration and those reflections should feel more realistic and immersive with your current environment.  As a general rule: the “shinier” an object was before, the more environment reflections it will pick up and the more visual difference there will be.&lt;br /&gt;
&lt;br /&gt;
Another notable change is the addition of [https://en.wikipedia.org/wiki/Tone_mapping tonemapping] to the viewer. Tonemapping is a way of representing an image with a higher native dynamic range than the display can support. Tonemapping is a de-facto requirement of PBR pipelines. This means that colors in Second Life will generally appear more saturated with less detail being lost in shadows and highlights. [https://modelviewer.dev/examples/color.html Click here for some additional reading on the subject.]&lt;br /&gt;
&lt;br /&gt;
The tonemapping method used is called &#039;&#039;&#039;Academy Color Encoding System (&#039;&#039;&#039;aka &#039;&#039;&#039;&amp;quot;ACES&amp;quot;)&#039;&#039;&#039;, and can be read about [https://www.oscars.org/science-technology/sci-tech-projects/aces here] and [https://docs.nvidia.com/gameworks/index.html#devices/shield-hdr-dev-guide/hdr-dev-guide-nvidia-shield.htm here].&lt;br /&gt;
&lt;br /&gt;
[[PBR_Materials#Nomenclature_changes|Blinn-Phong]] content making use of the Specular parameters may look different if the items were never viewed under local lights, as the PBR viewer allows for the environment (sun &#039;&#039;and&#039;&#039; sky) to contribute to specular reflections. As such, if the object receives blue specular reflections from the sky, these reflections are tinted, and may look odd. This effect is the same as prior viewers, as if the object was lit by a blue local light.&lt;br /&gt;
&lt;br /&gt;
=== Removal of Advanced Lighting Model ( ALM ) Graphics Option ===&lt;br /&gt;
There have been changes to the &#039;&#039;&#039;Graphics &amp;gt; Advanced Settings...&#039;&#039;&#039; Preferences. The most notable of which is the removal of the “Atmospheric Shaders”, &amp;quot;Local lights&amp;quot; and “Advanced Lighting Model” options.&lt;br /&gt;
&lt;br /&gt;
For those users on lower-end hardware who depended heavily upon those options to navigate Second Life with an acceptable framerate, we recommend the following settings:&lt;br /&gt;
*{{code|Transparent Water: Disabled}}&lt;br /&gt;
*{{code|Screen Space Ambient Occlusion: Disabled}}&lt;br /&gt;
*{{code|Shadows: None}}&lt;br /&gt;
*{{code|Screen Space Reflections: Disabled}}&lt;br /&gt;
*{{code|Reflection Detail: Static Only}}&lt;br /&gt;
*{{code|Reflection Coverage: Manual only}}&lt;br /&gt;
&lt;br /&gt;
The {{code|Local Lights}} setting has been superceded by updates made to the &#039;&#039;&#039;World &amp;gt; Improve graphics speed...&#039;&#039;&#039; &amp;quot;Quality &amp;amp; Speed&amp;quot; slider options. Users on low-end hardware should ensure that their &amp;quot;Quality &amp;amp; Speed&amp;quot; slider is set at an appropriate level for their hardware.&lt;br /&gt;
&lt;br /&gt;
== What does this mean for Creators? ==&lt;br /&gt;
=== Nomenclature changes ===&lt;br /&gt;
The PBR project represents a large step towards integrating standard rendering techniques used in the games industry. As such, the nomenclature of some items has changed, notably, what was once called &#039;&#039;&#039;[[:Category:Materials|Materials]]&#039;&#039;&#039; is now referred to as &#039;&#039;&#039;Blinn-Phong&#039;&#039;&#039;. This does not represent any changes to the underlying rendering techniques (beyond those mentioned above); and as such Blinn-Phong &#039;&#039;is not&#039;&#039; the same as PBR Spec/Gloss workflows, seen in some game engines such as Unity.&lt;br /&gt;
&lt;br /&gt;
=== Importing PBR Materials from External Software: ===&lt;br /&gt;
For creators who work with external tools such as Blender, Adobe Substance Painter, Cinema4D, 3D-Coat, or have used the Unreal or Godot game engines, the use of PBR texture sets should already be familiar.  In fact, some creators have been using tools that use PBR workflows to create content for Second Life and have then been forced to sacrifice visual quality to convert that information into Second Life’s existing Blinn-Phong materials system.  &lt;br /&gt;
&lt;br /&gt;
Second Life is adopting the “Metallic/Roughness” PBR model, and in its ongoing commitment to using Open Source standards whenever it is practical to do so, the glTF 2.0 file format has been chosen as the upload format for PBR Material assets.  One of the primary goals of implementing PBR Materials is to have more continuity from content creation applications towards Second Life, and have more consistent content behavior once it’s inworld.&lt;br /&gt;
&lt;br /&gt;
=== Using Imported Materials in Second Life ===&lt;br /&gt;
[[File:Editing_Material.jpg|thumb|right|Material Editing]]&lt;br /&gt;
For people who build exclusively within Second Life, the addition of PBR also means that there will be a new [[LlGetInventoryType|Inventory Type]] called “Material”.  These new Materials can be purchased on the Marketplace and are shareable like any other permitted object in Second Life.&lt;br /&gt;
&lt;br /&gt;
PBR materials come as a bundle of textures.  These all travel as a single unit and are applied all at once.  If you wish to change tint, transparency, or other similar parameters of the Material you’ll need to modify the Material from the &amp;quot;Editing Material&amp;quot; floater.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Meshes uploaded after the PBR release will look slightly different (and more accurate to your editor of choice) due to a change in the way Second Life handles mesh assets (Previously, some data required for accurate tangent generation was discarded at upload time - this is no longer the case). This will mean that a PBR material applied to a mesh uploaded before PBR launched (28th November 2023) might look incorrect; a simple reupload of the mesh will solve this. &#039;&#039;&#039;This is not required, but recommended.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
Applying existing materials works similarly to existing textures. You have two means of doing so:&lt;br /&gt;
&lt;br /&gt;
==== Drag and Drop ====&lt;br /&gt;
Simply drag and drop onto the face of a prim or a mesh.  For example, if you have a tiled floor material and you place it on a selected prim cube face that face will now look like a tile floor and reflect light like a tiled floor would with all the material qualities contained in the material.&lt;br /&gt;
[[File:DragAndDrop_Mat_Wiki.png | thumb | none | Drag and Drop Functionality.]]&lt;br /&gt;
&lt;br /&gt;
==== Switch to PBR and select from Inventory ====&lt;br /&gt;
Alternatively, select a face on the prim or mesh you want to apply your material to and choose the “Blinn-Phong” drop-down and change it to “PBR Metallic Roughness”, then select “Choose an item from your inventory” and apply.&lt;br /&gt;
[[File:PBRSelectorV3.png |left| thumb | none | Menu drop-down demonstration.]][[File:Material_Dropdown.jpg|thumb|none|Dropdown as of v7.0.1.689]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Texture Transforms ====&lt;br /&gt;
With PBR materials, texture transforms work in a slightly different way to Blinn-Phong transforms. Blinn-Phong (and OpenGL) has the texture origin in the lower left corner of the texture, whereas glTF materials (and Vulkan) has the texture origin in the &amp;lt;b&amp;gt;upper left&amp;lt;/b&amp;gt; corner.&lt;br /&gt;
&lt;br /&gt;
This means that PBR materials will react differently (namely, they are inverted in the Y (glTF v) axis) to Blinn-Phong materials.&lt;br /&gt;
For a simple solution to convert a Blinn-Phong texture transform to a PBR texture transform, please see [https://community.secondlife.com/forums/topic/507448-lsl-math-for-gltf-transformation/?do=findComment&amp;amp;comment=2678959 this forum thread].&lt;br /&gt;
&lt;br /&gt;
For more information, [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#images see here] and [https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform here].&lt;br /&gt;
&lt;br /&gt;
=== Media-on-a-Prim ===&lt;br /&gt;
Media on a prim will continue to work largely as it has done prior to PBR&#039;s launch.&lt;br /&gt;
&lt;br /&gt;
When using MOAP on a face with a PBR texture, it will function as if the media texture has an override to the &#039;&#039;&#039;Base Color&#039;&#039;&#039; and &#039;&#039;&#039;Emissive&#039;&#039;&#039; maps. Note that the emissive map is overridden, however the emissive tint is not (so, to disable the emissive map you can set the emissive tint to black &amp;lt;0,0,0&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Tools &amp;amp; Tutorials ==&lt;br /&gt;
As the PBR system is new, it is expected that existing users may be confused at first on how everything works. As such, Linden Lab and some third parties have provided some tutorials and informational videos:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=JtjGf06B8ZA Second Life University - PBR Materials]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=BJ1eRTlWDYk Second Life University - How to create PBR Materials]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://substance3d.adobe.com/tutorials/courses/pbrguide Allegorithmic (Adobe) PBR Guide]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The above tutorials require knowledge and access to Adobe&#039;s Substance Painter (subscription based software on Adobe website, or sold as perpetual license with one year of updates on Steam). Below are some open source tools and links found Googling &#039;Blender&#039;, &#039;glTF&#039; (requiring Blender 3.3)&lt;br /&gt;
*&#039;&#039;&#039;[https://www.khronos.org/blog/art-pipeline-for-gltf Khronos Art Pipeline tutorial]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://materialmaker.org Material Maker software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://aiaicapta.in/gltf-packer/ glTF Packer software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://armorpaint.org Armorpaint software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://armorlab.org Armorlab software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://youtu.be/KTPdNUGwIGc glTF Blender Tutorial]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://youtu.be/p7OPRoT6FkY Exporting glTF Alpha Textures tutorial]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== PBR Stand-In Textures and Outdated Viewers ==&lt;br /&gt;
It is worth mentioning that during the adoption period of a new system, a substantial portion of Second Life Residents view the world through third party viewers and mobile viewer solutions that will not have updated to be able to see the new content.  Anyone viewing a PBR Material on a viewer that cannot display it will see the “underlying” non-PBR texture.  By default, this is a pine box or a completely blank texture.  Those who wish their content to be viewable by as many people as possible, might consider creating a Diffuse texture ( with baked lighting, the kind that is easily generated with tools like Substance Painter&#039;s “Baked Lighting Filter” ) and applying it as a regular texture to the object they’re placing the PBR material on, prior to applying the PBR material.  While this is an extra step in content creation, and complicates things somewhat, if you wish for your content to be appreciated by all, it’s worth considering adding this extra step to your workflow.  People designing PBR materials for distribution and sale might also consider offering a “Diffuse Only fallback” texture to accompany the PBR material specifically for people who cannot see the PBR content.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Once a PBR Material is applied, the Blinn-Phong textures cannot be updated. To remedy this, remove the PBR Material, then make changes to the Blinn-Phong textures and then reapply the PBR Material.}}&lt;br /&gt;
&lt;br /&gt;
== Understanding and Assisting the New Reflections System ==&lt;br /&gt;
Those creators that work at house-scale, or produce items that can be walked through, can gain additional control over the lighting in their creations by taking the time to fully understand the new toolkit that influences environmental reflections. There is a new type of volume that can be created and appended to object linksets specifically for scenes with these kinds of spaces.  These volumes define a custom area where reflections are calculated, overriding the default solution.  So-called “Reflection Probes” should be placed in a manner such that &#039;&#039;the fewest number of probes fills the largest amount of space possible with minimal overlap&#039;&#039;.  One probe per room is a good reference point for a general living space like a house.  If multiple probes exist in a given area they can cause visual artifacts and negatively impact performance  (also known as viewer lag).  &#039;&#039;&#039;&#039;&#039;Do not affix reflection probes to small creations such as furniture or decor.&#039;&#039;&#039;&#039;&#039;  Small items such as tables, chairs, musical instruments, candle sticks etc should use the reflection sample volume in the space in which they are placed.  They should not have one appended.  It is &#039;&#039;&#039;strongly&#039;&#039;&#039; recommended that any object that contains a reflection sample probe be left as “modify”, so the positioning of the probe can be adjusted or even removed by the owner of the item should they wish.&lt;br /&gt;
&lt;br /&gt;
{{Enote|Reflection probes &#039;&#039;&#039;are not&#039;&#039;&#039; intended for use with planar mirrors, and will look incorrect when being used to do so. Planar mirrors have been flagged for future work, but are not directly in the scope of the PBR project.}}&lt;br /&gt;
&amp;lt;!-- This image has been removed from public view as it is factually incorrect; as it references the blue tint on the floor as originating from the ocean, and not the sky. Note that the public project viewer currently also has a bug wherein the sky contribution on objects is too powerful; this has since been fixed however a public build containing the fix is yet to be released. [[File:ProblePlacementDemo_v.png | right | A visual demonstration of how manual probes interact with the surrounding environment and enhance it.]] --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== When is it Recommended to Create a Reflection Probe? ==&lt;br /&gt;
Manually placed probes are good for cleaning up undesirable noise and lighting from automatically placed probes that may be visually disruptive or confusing.  If you look in the image of the room in a house, on the left hand side of it, you can see a blue tint on the floor, which is the reflected blue light from the sky.  The probe filling half the room blocks this, and when it is extended to completely fill the room and just slightly beyond the thickness of the walls, (lower copy of the image) the problem is solved.  The probe does not need to be a part of the linkset for the rest of the room in order to function; however, once you have them placed it’s possible to add them into linksets like any other prim.&lt;br /&gt;
&lt;br /&gt;
=== Constructing a Reflection Sample Volume ( aka Reflection Probe ) ===&lt;br /&gt;
Start off in the Build Menu &#039;&#039;(Ctrl+3)&#039;&#039; by rezzing a basic prim. Under the &amp;lt;code&amp;gt;Features&amp;lt;/code&amp;gt; tab, at the bottom check the box labelled &amp;lt;code&amp;gt;Reflection Probe&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[File:ProbeUI.jpg | thumb | none | Manual Probe Creation UI.]]&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Reflection Probe&amp;lt;/code&amp;gt;:  Enabling this creates a Reflection Sampling Volume within the bounding box of the prim.&lt;br /&gt;
* &amp;lt;code&amp;gt;Box / Sphere Drop-down&amp;lt;/code&amp;gt;: Choose whether you want this sample to project reflections within a box shaped volume or a spherical one.&lt;br /&gt;
* &amp;lt;code&amp;gt;Dynamic&amp;lt;/code&amp;gt;: Allow skinned objects (e.g. Avatars, animesh, etc.) to be captured in the reflection.&lt;br /&gt;
* &amp;lt;code&amp;gt;Ambiance&amp;lt;/code&amp;gt;: Affects how much objects within the volume are lit as if they have bounced light hitting them from faked indirect illumination. &#039;&#039;See [[PBR_Materials#Fine-Tuning_Reflection_Sample_Volumes|&amp;quot;Fine-Tuning Reflection Sample Volumes&amp;quot;]] for more info.&lt;br /&gt;
* &amp;lt;code&amp;gt;Near Clip&amp;lt;/code&amp;gt;: Sometimes, there will be a large obstruction in the center of the volume you wish to use as a reflection probe. For example, suppose that there is a large architectural supporting structure in the middle of the room, such as a column. If the probe gets placed internally inside this, it will reflect the internal surface of the column instead of the room in which it is placed.  Increasing Near Clip will make the sampling volume exclude objects n meters from the center so they don’t get included in the reflection. &#039;&#039;See [[PBR_Materials#Fine-Tuning_Reflection_Sample_Volumes|&amp;quot;Fine-Tuning Reflection Sample Volumes&amp;quot;]] for more info.&lt;br /&gt;
&lt;br /&gt;
Select an appropriate probe shape for your scene. For example, if you want to create a reflection probe that covers a room, select a &amp;lt;code&amp;gt;Box&amp;lt;/code&amp;gt; shape, or for other objects a &amp;lt;code&amp;gt;Sphere&amp;lt;/code&amp;gt; shape is recommended.&lt;br /&gt;
&lt;br /&gt;
Move the reflection probe and resize it as necessary to fit your desired purpose. For the room example, resize the box so the probe just touches the walls, ceiling and floor of the room.&lt;br /&gt;
&lt;br /&gt;
Naming your probes is also recommended, so other people ( or yourself later on ) will remember why there’s a large transparent prim in the middle of your build.&lt;br /&gt;
&lt;br /&gt;
If you wish to come back and edit the reflection probe later on, enable both &amp;lt;code&amp;gt;Show Reflection Probe Volumes&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Select Reflection Probes&amp;lt;/code&amp;gt; under &amp;lt;code&amp;gt;Build &amp;gt; Options&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Fine-Tuning Reflection Sample Volumes ===&lt;br /&gt;
{{KBwarning|Sample volumes do not react to prim parameter changes.  Options such as “Hollow” or “Taper” or “Shear” do not change how the sampling area behaves.}}&lt;br /&gt;
&lt;br /&gt;
That said, it may be helpful to visualize &amp;lt;code&amp;gt;Near Clip&amp;lt;/code&amp;gt; as the hollowing out of your reflection sample volume, but the hollow and near-clip are not in any way linked and doing so is purely a convenience.  Reflection probe sample volumes only respond to changes in scale and position; however, different probe volumes behave differently when resized.&lt;br /&gt;
* &amp;lt;code&amp;gt;Box&amp;lt;/code&amp;gt; Probes: These are affected by position and non-uniform scale.&lt;br /&gt;
Example: If I create a box reflection probe and scale it to 10m,30m,10m size, the full volume will be affected by the probe.&lt;br /&gt;
* &amp;lt;code&amp;gt;Sphere&amp;lt;/code&amp;gt; Probes: These are affected by position and &#039;&#039;&#039;uniform&#039;&#039;&#039; scale only, otherwise the smallest dimension is used.&lt;br /&gt;
Example: If I create a spherical probe and scale it to 10m,30m, and 10m size , the probe will sample a 10 meter diameter spherical volume at the center of the probe.  However, a 30m,30m,30m sphere will create a sampling volume of a 30 meter diameter sphere.&lt;br /&gt;
[[File:SphereProbeErrror.jpg | thumb | none | A diagram of how spherical probes function (or, don&#039;t function as you may expect) while non-uniformly-scaled.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; controls how ambient light (Found in your [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]]) affects, or does not affect, the contents of the reflection probe, and the intensity of indirect lighting (aka Irradiance).&lt;br /&gt;
This value is influenced by the &amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; value found in the user&#039;s [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]], wherein if the Ambiance value given in the [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]] is higher than the value defined by the probe itself, it inherits the ambiance value of the [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]], or alternately if the ambiance value defined by the probe is higher, the probe&#039;s value is used.&lt;br /&gt;
&lt;br /&gt;
There are a few operation modes that are set with the &amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; value, in tandem with above:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Probe Ambiance behavior&lt;br /&gt;
! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0.00 || Ambient color from the environment applies at full intensity. &#039;&#039;&#039;Default for pre-PBR skies.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0.01 .. 0.99 || Blends ambiance with indirect lighting (sun + emissive or illuminated surfaces) within the probe.&lt;br /&gt;
|-&lt;br /&gt;
| 1.00 || Indirect light applies at full intensity, without ambient color. &#039;&#039;&#039;Default for PBR skies.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 1.01 .. 4.00 || Multiplier for indirect light, makes everything brighter within the probe.&lt;br /&gt;
|-&lt;br /&gt;
| 4.01 .. 100.00 || Multiplier for indirect light, but only further affects light from the sun. (Local lights are capped to 4.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Complex Reflection Probe placement ===&lt;br /&gt;
&lt;br /&gt;
At times, you may wish to place a reflection probe in an area where the reflection probe types (Sphere or Box) do not conform to the shape of the area. For example, a Box probe in a loft room has a triangular shape, which results in &amp;quot;probe bleed&amp;quot; wherein the influence volume affects an area larger than what is needed, and thus bleeds out onto the exterior roof, which is undesirable.&lt;br /&gt;
&lt;br /&gt;
In these cases, you may need to place multiple probes in order to blend together the sample volumes to achieve the desired result. In the loft room example, it&#039;s best to start with 3 box probes, which cover the floor, and each side of the roof. Then, use an additional box probe as &amp;quot;fill&amp;quot; to cover the gap inbetween the 3 probes. The main fill probe may need to be rotated at an angle to get the most coverage possible. If it is not possible to fill the gap between the 3 probes with this fill probe, you may wish to add more box probes to act as &amp;quot;secondary fill&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[TODO: Put photo examples of the above here!]&lt;br /&gt;
&lt;br /&gt;
Alternately, Sphere probes may be used to achieve the same result. Sphere probes have considerably softer blending than box probes, so this may give you the best results in severe bleed conditions.&lt;br /&gt;
&lt;br /&gt;
=== Unsupported use-cases ===&lt;br /&gt;
{{KBwarning|Do not attempt to do anything listed below. These uses are liable to either intentionally be disabled, or stop working in future updates.}}&lt;br /&gt;
You may be tempted to use reflection probes in the following ways, however these use-cases are either intentionally disabled or result in undefined behavior, and may work now but may not in future. &#039;&#039;&#039;You have been warned.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Under any circumstances, you should NOT:&#039;&#039;&#039;&lt;br /&gt;
* Wear a reflection probe. This intentionally does not work. (Reflection probes are a property of the scene, not an individual object / avatar)&lt;br /&gt;
* Attach a reflection probe to a physics-enabled object, e.g. vehicles. This intentionally does not work. (As above, the reflection probes are part of the scene; not part of an individual object. Reflection probes by design do not update in real-time, thus the object would always have incorrect reflections anyway.)&lt;br /&gt;
&lt;br /&gt;
== PBR Material Composition ==&lt;br /&gt;
PBR-defined values are measured by optical sensors and other capture tools and recorded in databases.  These parameters are then put into commercial software to be used by content creators.  Using software designed to create PBR content is always recommended; however, understanding how PBR materials are assembled can assist with editing and compiling them.  Having a direct understanding of what each individual color channel contributes is essential for editing PBR textures without relying upon some of the more advanced toolkits available.&lt;br /&gt;
&lt;br /&gt;
All PBR Values are listed from 0 to 1.0, though in an actual image, the values range from 0 - 255.&lt;br /&gt;
PBR Materials are composed of a set of four specifically designed textures; they are as follows:&lt;br /&gt;
&lt;br /&gt;
===Base Color [ RGB ] + Transparency [ A ]===&lt;br /&gt;
[RGB]: This is the unlit color of the surface.  This differs from the “Diffuse” texture that Second Life uses.  Diffuse textures often include faked reflection and specular information as well as added Ambient Occlusion shadows.  Base Color textures do not get any of this added information.  For metals ( as defined by the metalness value ), Base Color also determines specular reflection color, whereas, in non-PBR systems, this is defined by the Specular texture and tint.  In certain PBR texturing applications, Base Color is sometimes also referred to as “Albedo”.&lt;br /&gt;
&amp;lt;br /&amp;gt;[ A ]: Alpha Channel, dictates the transparency of the entire material overall.&lt;br /&gt;
&lt;br /&gt;
The Base color texture should be devoid of lighting information.&lt;br /&gt;
&lt;br /&gt;
===Occlusion [R] / Roughness [G] / Metalness [B]===&lt;br /&gt;
This texture is composed of 3 unrelated grayscale images stored in 3 different color channels of an RGB texture.&lt;br /&gt;
&amp;lt;br /&amp;gt;[R]: (Ambient) Occlusion is lighting data, &#039;&#039;&#039;removing&#039;&#039;&#039; the need to bake down shadows on to the Base Color map. Note that &#039;&#039;white&#039;&#039; (&amp;lt;1,1,1&amp;gt;) means no occlusion is applied, and &#039;&#039;black&#039;&#039; (&amp;lt;0,0,0&amp;gt;) applies full occlusion.&lt;br /&gt;
&amp;lt;br /&amp;gt;[G]: Roughness data ranges from 0 to 1.0, but the actual range of physical surfaces ranges from approximately 0.05 to 0.985.  No surface is perfectly smooth or completely rough.  The rougher a surface is the less mirror-like it behaves.  &lt;br /&gt;
&amp;lt;br /&amp;gt;[B]: Metalness values are mostly black or white.  Either the material is a conductive metal like copper, or it’s a non-metal like fabric.  0.0 is Non-Metallic, 1.0 is Metallic.  There are almost no materials with gray metalness values.&lt;br /&gt;
&amp;lt;br /&amp;gt;The alpha channel is ignored, as per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification].&lt;br /&gt;
&lt;br /&gt;
{{Enote|The Occlusion map &#039;&#039;&#039;is not&#039;&#039;&#039; controlled by the &amp;quot;Screen Space Ambient Occlusion&amp;quot; toggle in the Advanced Graphics Settings. &#039;&#039;&#039;The Occlusion map is &#039;&#039;always&#039;&#039; enabled.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
===Emissive [ RGB ]===&lt;br /&gt;
This determines the amount and the color of unlit (ignores ambient light conditions) areas of your material. When giving an object a white emissive map, the object will act as if the Blinn-Phong &amp;quot;Fullbright&amp;quot; option was checked. This map is useful for items which are expected to emit light, e.g. a table lamp, where the lamp shade would appear to glow when the lamp is turned on. If you wish to toggle the lamp on and off, it&#039;s recommended to change the Emissive Tint value to black (functionally disabling the emissive map, thus turning the lamp off), and then changing the tint back to your desired color to turn the lamp back on. Leaving the Emissive slot empty is recommended when it’s unused.&lt;br /&gt;
&lt;br /&gt;
Note that glow (The postprocessing effect), is controlled by a separate parameter in the build floater and is intentionally not part of the PBR material window. Glow is modulated by the emissive map, so black areas of an object&#039;s emissive map will not glow, similar to how Blinn-Phong emissive maps and glow work.&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the alpha channel is ignored.&lt;br /&gt;
&lt;br /&gt;
===Normal [ RGB ]===&lt;br /&gt;
The normal maps generated by your baking application / normal map generation toolkit should be compatible with Second Life in most cases.  The most common pitfall is using normal maps generated with “inverted” Green channels, such as those that are used for Direct3D and Unreal Engine.  Normal texture data redirects light in a different direction based on the vectors indicated by the color, so if the green channel is backward, it’ll seem to be “pointing the wrong way”.  To phrase the problem a different way, all the things that should be bumps look like dents, and vice versa.  If this occurs, double check that your settings aren’t for Direct3D and try re-generating it.  Also, taking it into image editing software and inverting the green channel only sometimes is a sufficient fix.&lt;br /&gt;
&lt;br /&gt;
Normal map tangent spaces are an extremely technical subject that most users need not be concerned with as most modern applications default to the correct setting.  However, if your normal maps look drastically different inside Second Life, compared to your source application, and you’ve already confirmed it’s not an inverted green channel, then checking which tangent space settings are being used is the next step.  PBR Normals use Mikkelsen Tangent Space. (Often abbreviated MikkT)  If you are unsure what this means, use similar workflows and settings for Second Life PBR as those that are generally recommended for the Godot 4 game engine.&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the blue channel of a normal map is only allowed to contain values above 0.5 to a max of 1 (255).&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the alpha channel is ignored.&lt;br /&gt;
&lt;br /&gt;
== Material Reference Libraries ==&lt;br /&gt;
As PBR is &#039;&#039;Physically Based&#039;&#039;, you may wish to know how to recreate a real-life material in PBR form. What color should you use? What metalness value should it have?&lt;br /&gt;
&lt;br /&gt;
Fortunately, reference libraries exist which can tell you how to recreate a given material in a PBR workflow.&lt;br /&gt;
&lt;br /&gt;
=== List of Reference Color Palettes &amp;amp; libraries ===&lt;br /&gt;
*&#039;&#039;&#039;[https://physicallybased.info/ Physically Based]&#039;&#039;&#039;&lt;br /&gt;
** Select the &#039;&#039;Godot&#039;&#039; Engine, with the Color Space as &#039;&#039;sRGB Linear&#039;&#039; and Color Representation as &#039;&#039;0-1&#039;&#039; (Depending on what you are doing, you may need to convert the value into gamma-corrected sRGB space, which can be done [https://davengrace.com/dave/cspace/ here.] Enter the value on the 3rd row.)&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=TcTh-1X2FsQ Grzegorz Baran Library]&#039;&#039;&#039;&lt;br /&gt;
**Available on [https://www.youtube.com/watch?v=TcTh-1X2FsQ YouTube (video format, older)], or [https://gbar.gumroad.com/l/cbwkvo PDF (most up-to-date)].&lt;br /&gt;
*&#039;&#039;&#039;[https://seblagarde.wordpress.com/2014/04/14/dontnod-physically-based-rendering-chart-for-unreal-engine-4/ Dontnod Entertainment Library]&#039;&#039;&#039;&lt;br /&gt;
** A very small library, but useful nonetheless.&lt;br /&gt;
*&#039;&#039;&#039;[https://docs.studio-397.com/developers-guide/general-reference/pbr-an-introduction-authoring-guide Studio 397]&#039;&#039;&#039;&lt;br /&gt;
** A small collection of various materials.&lt;br /&gt;
*&#039;&#039;&#039;[http://wiki.polycount.com/wiki/PBR Polycount Wiki]&#039;&#039;&#039;&lt;br /&gt;
** Not so much a library in itself, but links out to other libraries. &#039;&#039;&#039;See the Color Charts section&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== List of PBR asset libraries ===&lt;br /&gt;
*&#039;&#039;&#039;[https://ambientcg.com/ ambientCG]&#039;&#039;&#039;&lt;br /&gt;
** Provides CC0 licensed HDRi, Metal-Rough materials and .sbar (Substance Painter material) files.&lt;br /&gt;
*&#039;&#039;&#039;[https://polyhaven.com/ Poly Haven]&#039;&#039;&#039;&lt;br /&gt;
** Provides CC0 licensed HDRi, Metal-Rough materials and models.&lt;br /&gt;
&lt;br /&gt;
== Uploading Materials ==&lt;br /&gt;
There are two different methods of creating PBR material assets:&lt;br /&gt;
&lt;br /&gt;
=== Method 1 : Direct Upload a glTF File ===&lt;br /&gt;
The most convenient method to create an entirely new material is to directly upload a glTF file from your computer.&lt;br /&gt;
&lt;br /&gt;
This is accessible via &amp;lt;code&amp;gt;Build &amp;gt; Upload &amp;gt; Material...&amp;lt;/code&amp;gt; &lt;br /&gt;
[[File:PBRBuildMenu.jpg | thumb | none | PBR Upload in main build menu. ]]&lt;br /&gt;
&lt;br /&gt;
Select a .GLB or a .GLTF file from your computer and Open it:&lt;br /&gt;
This will give the following window:&lt;br /&gt;
[[File:PBRMatMenu.jpg | thumb | none| The layout of the PBR material creation and editing interface.]]&lt;br /&gt;
&lt;br /&gt;
All of the editable material properties that most creators are familiar with are integrated into the Material&#039;s vertically sizable window on upload.  &lt;br /&gt;
&lt;br /&gt;
Clicking “Save” pays the upload fees and creates the Material assets using the local filename. Using &amp;quot;Save As...&amp;quot; provides a window to name all the assets to unique inworld names. There is a 63 char limit for names of inventory assets but the naming floater on this Materials upload &amp;lt;b&amp;gt;has no length restrictions&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Textures will be uploaded to the inventory&#039;s default Textures folder (NoMod, NoCopy, Transfer) with an appended &amp;quot;type&amp;quot; added to the given name. (ie. asset name (Base Color), asset name (Normal), asset name (Metallic Roughness). In naming the asset keep in mind that the bracketed appendix is included in the name&#039;s 63 character limit. The PBR object is uploaded (Mod, NoCopy, Transfer) to the inventory&#039;s Material folder with the appended text (Material). Presently, naming the glTF asset from Save As... will NOT append (Material) to the inventory&#039;s name. Saving from the locally named file will.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Each individual texture used to compose the Material object is charged a separate upload fee. (ie. If you have 4 textures (Base Color, Metallic Roughness, Emissive, Normal) you are charged 4 times. If you only have a Normal and Metallic-Roughness texture you will be charged for 2, etc.)}}&lt;br /&gt;
&lt;br /&gt;
=== Method 2 : Edit a Blank. Create PBR Materials without a glTF File ===&lt;br /&gt;
If you have the necessary textures to compose a PBR material but do not have a glTF file you will need to build a Material from inventory.  Make sure your textures are in the correct format, with the correct data in the proper channels.  This requires some understanding of how [[PBR_Materials#PBR_Material_Composition:|PBR materials work]].  Once you have that, upload your textures as you normally would.&lt;br /&gt;
&lt;br /&gt;
[[File:PBRMenuUI.jpg | thumb | none | The PBR Materials UI in the Build Floater.]]&lt;br /&gt;
&lt;br /&gt;
* Find the &amp;quot;Materials&amp;quot; folder in your inventory, right click, and select &amp;quot;New Material&amp;quot; from the context menu.&lt;br /&gt;
* Name your new material something appropriate (E.g. &amp;quot;Red Bricks&amp;quot; for a red brick wall, etc.)&lt;br /&gt;
* Right click on your new material, and select &amp;quot;Open&amp;quot; from the context menu.&lt;br /&gt;
* Upload your PBR textures using the standard texture workflow (Usually, under &amp;lt;code&amp;gt;Build &amp;gt; Upload &amp;gt; Image&amp;lt;/code&amp;gt;).&lt;br /&gt;
* In the material window, select the appropriate maps that you just uploaded in their respective slots.&lt;br /&gt;
{{KBwarning|While there may be a temptation to try and place a Blinn-Phong “Diffuse” texture into the “Base Color” slot and a Blinn-Phong “Specular&amp;quot; into the “Metallic/Roughness” slot, doing so will not produce desirable results.  However, trying this won’t generate any warnings as there is no way to check that you’ve put the right texture type into the inputs, and the material will save and be applicable to objects.  &#039;&#039;&#039;Unfortunately, what will be created is not a functional PBR material.  Please do not do this.&#039;&#039;&#039;}}&lt;br /&gt;
* Once done, verify the material parameters are correct (E.g. Base color tint, M/R factor, Emissive tint, etc.)&lt;br /&gt;
* Save your new material.&lt;br /&gt;
* EITHER: [[PBR Materials#Drag_and_Drop|Drag and drop the material onto a rezzed prim]], OR edit the prim, click the &amp;quot;Blinn-Phong&amp;quot; drop-down, select &amp;quot;PBR Metallic-Roughness&amp;quot;, and click &amp;quot;Choose from Inventory&amp;quot;, and select your new material.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- * Create a test cube and select a face, switch it to be a PBR material.&lt;br /&gt;
* Select “Choose from Inventory”&lt;br /&gt;
* Instead of choosing something from your inventory, choose “Blank” and click “Ok”&lt;br /&gt;
* Choose “Edit Selected”,  this will open up the material creation UI.&lt;br /&gt;
* Choose your appropriate maps from your texture inventory and slot them into their matching locations.&lt;br /&gt;
{{KBwarning|While there may be a temptation to try and place a pre-PBR “Diffuse” texture into the “Base Color” slot and a pre-PBR “Specular&amp;quot; into the “Metallic/Roughness” slot, doing so will not produce desirable results.  However, trying this won’t generate any warnings as there is no way to check that you’ve put the right texture type into the inputs, and the material will save and be applicable to objects.  &#039;&#039;&#039;Unfortunately, what will be created is not a functional PBR material.  Please do not do this.&#039;&#039;&#039;}}&lt;br /&gt;
* Click “Save to Inventory”, this will bring up a prompt to name your new PBR material. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Creating Color Variants ===&lt;br /&gt;
After you’ve uploaded your first Material for your project, if you’re a store owner who would like to release more than one color palette for your creation, as many clothing and furniture designers find it useful to do, &#039;&#039;&#039;it’s important to not upload a new glTF file for every single variant of your Material&#039;&#039;&#039;.  Most variant materials, only the base color will change.  The Occlusion, Roughness, Metalness, Emissive and Normal map texture slots will remain the same.  Since these maps have already been uploaded once, if we upload a second glTF File, they will be duplicated.  Having different copies of the same texture, with differing UUID’s means that they will clog up download bandwidth and video card memory (not to mention your inventory as well).  This is very bad for Second Life.  So, for this reason, it’s recommended that when you create texture variants, you upload the additional copies of the base-color texture separately.  Then open your newly uploaded material in edit mode, choose the Base Color texture, and change it out for one of your newly uploaded Base Color textures, and click “Save As”.  This will create a second copy of your material that uses all the correct texture maps without needlessly duplicating them and causing additional lag.&lt;br /&gt;
&lt;br /&gt;
{{KBtip| You can also avoid uploading additional textures altogether by uploading a white color variant, then making use of the Base Color Tint parameter!}}&lt;br /&gt;
&lt;br /&gt;
== Double Sided Parameter : Uses and Dangers ==&lt;br /&gt;
“Double Sided” is a new property unique to Materials.  When “Double Sided” is checked, the surface upon which this material is placed will be drawn twice; once for the outward-facing portion of the surface, and a second time for the inward-facing side of the surface that is normally invisible without a double-sided material.  This option should only be used for very specific meshes that were designed to be used with double-sided materials, since placing materials with this parameter checked on normal objects will simply cause the viewer to draw it twice ( and thereby create additional viewer lag ) for no observable change.  It is &#039;&#039;&#039;very strongly recommended&#039;&#039;&#039; that this option be unchecked for any material that is to be distributed for general use.  Even more so if the “Modify” permission is revoked.  If you wish to distribute a version of a material that has “Double Sided” checked, please include a second copy of the material that has “Double Sided” unchecked as well, with an accompanying explanation to the next user as to why this was done.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
*LeafTextures_DoubleSided&lt;br /&gt;
*LeafTextures_SingleSided&lt;br /&gt;
&lt;br /&gt;
When designing mesh content for use with the double-sided material parameter, it is also &#039;&#039;very strongly recommended that you separate the triangles you intend to use the double-sided material upon into a separate mesh “face”&#039;&#039;, so as to not unintentionally render the portions of the mesh that already have triangles designed to represent the internal portion of the object a second time.&lt;br /&gt;
&lt;br /&gt;
== LSL Scripting ==&lt;br /&gt;
&lt;br /&gt;
Documentation for the LSL interface with glTF materials can be found on the following pages:&lt;br /&gt;
*[[GLTF Overrides]]&lt;br /&gt;
*[https://wiki.secondlife.com/wiki/Category:LSL_Material Category:LSL Material]&lt;br /&gt;
&lt;br /&gt;
== Recommended Application Settings ==&lt;br /&gt;
The glTF file format is widely adopted and many applications have export functionality for this type of file.&lt;br /&gt;
Below is a non-comprehensive list of some of the more popular applications that export glTF files.&lt;br /&gt;
&lt;br /&gt;
{{KBtip|&#039;&#039;&#039;The recommended HDRi for use in Second Life content creation is available at [https://github.com/Jenna-Huntsman/Second-Life-Resources/tree/main/PBR/HDRi this Github repository].&#039;&#039;&#039; This HDRi will closely match the lighting of objects under the PBR &amp;quot;Midday&amp;quot; environment preset.}}&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|While every effort has been made to ensure the recommended HDRi matches SL as close as possible, you may need to adjust the &amp;quot;Environment Exposure&amp;quot; parameter in your editing program to get 1:1 results with SL.}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===[https://www.blender.org/ Blender]===&lt;br /&gt;
&lt;br /&gt;
{{KBwarning|Older Blender versions have bugs in their glTF export tools which make their output incompatible with Second Life. &#039;&#039;&#039;Use of Blender versions of 3.3 and above are highly recommended to avoid issues.&#039;&#039;&#039;}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{{KBcaution|Please ensure that you correctly set up an HDRi in your viewport, as otherwise the rendering in Blender and in SL will not match. Please see [[PBR Materials#Recommended_Application_Settings|here for a reference HDRi]]. &#039;&#039;&#039;Please see [https://www.polygonartists.com/hdri-environment-maps-in-blender/ this tutorial].&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
Upon starting your project:&lt;br /&gt;
&lt;br /&gt;
Under: &amp;lt;code&amp;gt;Scene &amp;gt; Render Properties &amp;gt; Color Management&amp;lt;/code&amp;gt;:&lt;br /&gt;
*{{code|View Transform: Standard}}&lt;br /&gt;
*{{code|Sequencer: Linear ACEScg}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Create your materials within Blender using Principled BSDF Shader Nodes and the glTF Settings node.  Export them according to the official &#039;&#039;&#039;[https://docs.blender.org/manual/en/3.6/addons/import_export/scene_gltf2.html Blender Documentation.]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Second Life does not support BSDF Clearcoat, Subsurface, Anisotropy or Transmission parameters at this time.}}&lt;br /&gt;
&lt;br /&gt;
For materials creation, 2 example .blend files are provided, one using separate Occlusion, Roughness and Metallic textures (&amp;quot;Long Form&amp;quot;), and one that handles a pre-packed ORM map (&amp;quot;Short Form&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
The below files are intended for use with Blender versions 3.3 and above.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[https://github.com/Jenna-Huntsman/Second-Life-Resources/blob/main/glTF%20Node%20Tree%20-%20Long%20Form.blend glTF Node Tree - Long Form.blend]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[https://github.com/Jenna-Huntsman/Second-Life-Resources/blob/main/glTF%20Node%20Tree%20-%20Short%20Form.blend glTF Node Tree - Short Form.blend]&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.adobe.com/products/substance3d-painter.html Adobe Substance 3D Painter] === &lt;br /&gt;
{{KBcaution|While Substance Painter is generally considered to be stable, in some (rare) situations Substance may output a malformed glTF which will be rejected by the viewer. If this happens, import your materials into Blender, then export a glTF from Blender - this should be fixed in a future version of Substance.}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{{KBcaution|Please ensure that you correctly set up an HDRi in your viewport, as otherwise the rendering in Substance and in SL will not match. Please see [[PBR Materials#Recommended_Application_Settings|here for a reference HDRi]]. Please see [http://y2u.be/6goXJ2CJzaM this tutorial].}}&lt;br /&gt;
When starting your project:&lt;br /&gt;
&lt;br /&gt;
*{{code|Template: PBR - Metallic Roughness Alpha-test (starter_assets)}}&lt;br /&gt;
&lt;br /&gt;
OR&lt;br /&gt;
&lt;br /&gt;
*{{code|Template: PBR - Metallic Roughness Alpha-blend (starter_assets)}}&lt;br /&gt;
&lt;br /&gt;
Verify the following settings:&lt;br /&gt;
*{{code|Document Resolution: [Set as desired]}}&lt;br /&gt;
*{{code|Normal Map Format: OpenGL (Y+)}}&lt;br /&gt;
*{{code|Compute Tangents Per Fragment: YES}}&lt;br /&gt;
&lt;br /&gt;
Under &amp;quot;Color Management&amp;quot;:&lt;br /&gt;
{{KBtip| For a more in-depth explanation of the below settings, and issues that you may encounter, visit [https://mrlixm.github.io/blog/substance-painter-color-management/#aces-workflow this page].}}&lt;br /&gt;
*{{code|Color Management: OpenColorIO}}&lt;br /&gt;
*{{code|OpenColorIO Configuration: ACES 1.0.3}}&lt;br /&gt;
*{{code|Standard sRGB color space: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|8 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|16 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|Material color space default: Utility - sRGB - Texture}}&lt;br /&gt;
&lt;br /&gt;
When exporting:&lt;br /&gt;
&lt;br /&gt;
Settings Tab - Global Settings&lt;br /&gt;
*{{code|Output template: glTF PBR Metal Roughness}}&lt;br /&gt;
*{{code|Size: Based on each Texture Set&#039;s size}}&lt;br /&gt;
*{{code|Padding: Dilation + default background color - 8 bit}} &#039;&#039;&#039;OR&#039;&#039;&#039; {{code|Padding: Dilation infinite}}&lt;br /&gt;
{{KBtip|If you encounter color banding (AKA posterization) in your base color texture after export, you may wish to enable dithering.}}&lt;br /&gt;
*To enable dithering, open the {{code|Export Textures}} window, under the {{code|Settings}} tab, enable an override for the Base Color texture and swap the output format from {{code|8 bits}} to {{code|8 bits + dithering}}&lt;br /&gt;
&lt;br /&gt;
Settings Tab - Per-Material settings&lt;br /&gt;
*{{code|Output maps &amp;gt; Normal Map: 8 bits}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt;[[File:Substance_Painter_-_Export_Global_settings.png|thumb|Settings Tab - Global Settings]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt;[[File:Substance_Painter_-_Export_Per-Material_settings.png|thumb|Settings Tab - Per-Material settings]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.adobe.com/products/substance3d-designer.html Adobe Substance 3D Designer] === &lt;br /&gt;
{{KBcaution|Substance Designer &#039;&#039;does not support glTF output&#039;&#039;, unlike Substance Painter.}}&lt;br /&gt;
&lt;br /&gt;
Under preferences, verify the following &#039;&#039;&#039;Project&#039;&#039;&#039; settings:&lt;br /&gt;
*{{code|Color Management: OpenColorIO}}&lt;br /&gt;
*{{code|OpenColorIO Configuration: ACES 1.0.3}}&lt;br /&gt;
*{{code|8 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|16 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|Floating point images: ACES - ACEScg}}&lt;br /&gt;
*{{code|2D and 3D View Display Default: sRGB}}&lt;br /&gt;
&lt;br /&gt;
When exporting (Export Outputs):&lt;br /&gt;
*{{code|basecolor: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|normal: Utility - Raw}}&lt;br /&gt;
*{{code|roughness: Utility - Raw}}&lt;br /&gt;
*{{code|metallic: Utility - Raw}}&lt;br /&gt;
*{{code|ambientocclusion: Utility - Raw}}&lt;br /&gt;
&lt;br /&gt;
If you use a custom node to output a [[PBR_Materials#Occlusion_.5BR.5D_.2F_Roughness_.5BG.5D_.2F_Metalness_.5BB.5D|pre-compiled ORM map]], this should also be set to Raw.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.maxon.net/en/cinema-4d Cinema 4D] === &lt;br /&gt;
Please follow the documentation provided in this [https://www.bakedpixels.nl/blog/export-to-gltf-with-cinema-4d blog post].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===[https://3dcoat.com/ 3DCoat]===&lt;br /&gt;
{{KBwarning|As of version &amp;quot;2022-58&amp;quot;, 3DCoat is unable to produce a spec-compliant glTF file, and thus should not be used for Second Life. &#039;&#039;&#039;Use at your own risk - you &#039;&#039;will&#039;&#039; have issues!}}&lt;br /&gt;
&lt;br /&gt;
glTF export has been implemented since 3DCoat version 2020 and is found in the &amp;lt;code&amp;gt;File &amp;gt;Export &amp;gt; Export to glTF &amp;gt; glTF Separate (gltf + .bin + textures)&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://quixel.com/mixer Quixel Mixer] === &lt;br /&gt;
Quixel Mixer has no glTF output, however, it does generate the functional textures, though they do need to be edited and combined in photo editing software.&lt;br /&gt;
Export them by going to &amp;lt;code&amp;gt;Export Target &amp;gt; Custom&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Texture Preset: Metalness Maps&lt;br /&gt;
&lt;br /&gt;
Select: &lt;br /&gt;
&lt;br /&gt;
Albedo , Roughness, Normal, AO , Metalness  and ( if need be ) Emissive.&lt;br /&gt;
&lt;br /&gt;
Click “Export to Disk” and open the folder that the files were placed into.&lt;br /&gt;
&lt;br /&gt;
Albedo is the Base Color texture in this case.&lt;br /&gt;
&lt;br /&gt;
AO , Roughness , Metalness get combined into the ORM map as per [[PBR_Materials#PBR_Material_Composition|this explanation.]]&lt;br /&gt;
&lt;br /&gt;
Normal Map : Quixel generates Direct3D Normal Maps. The green channel needs to be inverted as [[PBR_Materials#Normal_.5B_RGB_.5D_|per here.]]&lt;br /&gt;
&lt;br /&gt;
Upload to Second Life using [[PBR_Materials#Method_2_:_Edit_a_Blank._Create_PBR_Materials_without_a_GLTF_File|Method 2.]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.autodesk.eu/products/3ds-max/overview Autodesk 3DS Max 2023] === &lt;br /&gt;
Autodesk just added the ability to create glTF files using their new glTF Material and glTF Export functionalities as outlined in &#039;&#039;&#039;[https://help.autodesk.com/view/3DSMAX/2023/ENU/?guid=GUID-EFBB037D-C4EB-42D2-9CE1-30FCAD483C31 Autodesk&#039;s official documentation]&#039;&#039;&#039;.  All prior versions of Autodesk software do not have this functionality.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.materialmaker.org/ Material Maker] ===&lt;br /&gt;
While [https://www.materialmaker.org/ Material Maker] does not currently export directly to glTF ([https://github.com/RodZill4/material-maker/issues/479 This may be added in future]), materials created with this program are compatible with Second Life.&lt;br /&gt;
&lt;br /&gt;
Export your materials using the &#039;&#039;&#039;Godot 4 ORM&#039;&#039;&#039; export preset.&lt;br /&gt;
&lt;br /&gt;
Upload to Second Life using [[PBR_Materials#Method_2_:_Edit_a_Blank._Create_PBR_Materials_without_a_GLTF_File|Method 2.]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== Adobe Photoshop ===&lt;br /&gt;
&#039;&#039;&#039;While Photoshop is not officially a PBR authoring tool&#039;&#039;&#039;, the changes to Alpha Blending (changing into Linear space from sRGB) may require you to change your PS settings to get consistent results. Please see [http://www.kiransprojects.com/blog/2014/photoshop-blending-is-broken/ this article] for more information. (See the section titled &amp;quot;A Partial Solution&amp;quot;). Alternatively, [https://www.reddit.com/r/photoshop/comments/61clf8/how_to_fix_photoshops_incorrect_and_ugly_color/ this Reddit post] also gives a few options on how to achieve blending in the correct manner.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== GIMP ===&lt;br /&gt;
&#039;&#039;&#039;While GIMP is not officially a PBR authoring tool&#039;&#039;&#039;, the changes to Alpha Blending (changing into Linear space from sRGB) in Second Life will make textures created in GIMP display their alphas correctly (GIMP defaults to linear alpha calculation).&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting content ==&lt;br /&gt;
&lt;br /&gt;
If you upload a piece of PBR content which does not match your editor, the advice from Linden Lab is to &#039;&#039;&#039;STOP: File a Feedback ticket. Do not attempt to &amp;quot;fix&amp;quot; the content.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
That said, there are some troubleshooting steps which you can do yourself:&lt;br /&gt;
* Check that you are using the PBR Linden Midday preset (called &amp;quot;Midday&amp;quot;) &#039;&#039;not &amp;quot;Midday (Legacy)&amp;quot;&#039;&#039;. Other environments may not match the reference HDRi, causing some visual differences. (This is intended behaviour, and content should be able to be viewed under any light correctly, but for troubleshooting purposes this may be required).&lt;br /&gt;
** At present, there is a bug with the PBR Linden Midday preset which results in an excessive blue sheen, due to the use of an over-saturated (unrealistic) sky color, among some other issues. This has been fixed in the upcoming glTF Maintenance viewer.&lt;br /&gt;
* Are you using a reflection probe? If not, does the problem reproduce if a manual reflection probe is placed over the object?&lt;br /&gt;
** This is because a common source of the &amp;quot;blue sheen&amp;quot; in interior scenes is the use of an auto-probe. Auto-probes sample their surroundings, and combined with the approximate (and often incorrect) placement this will mean the sky is sampled on all sides, thus meaning the reflected light from the surroundings (which counters the blue light from the sky) is absent, leading to a larger-than-expected level of sky contribution on the object. Auto-probe placement can be worse in skyboxes or sky platforms&lt;br /&gt;
* Triple-check the settings used for your editor match the ones given [[PBR_Materials#Recommended_Application_Settings|here]], including the reference HDRi. &#039;&#039;&#039;Any&#039;&#039;&#039; deviation from these settings may cause visual differences between your editor and in-world.&lt;br /&gt;
* Check your content against a glTF reference viewer; such as:&lt;br /&gt;
** [https://modelviewer.dev/editor/ Modelviewer.dev]&lt;br /&gt;
** [https://github.khronos.org/glTF-Sample-Viewer-Release/ Khronos Sample Viewer]&lt;br /&gt;
&lt;br /&gt;
If the above steps fail, then please &#039;&#039;&#039;[https://feedback.secondlife.com/ file some Feedback.]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A good Feedback ticket will include:&lt;br /&gt;
* An LM to a location where the problem can be examined in-world.&lt;br /&gt;
* A copy of the glTF content, attached to the ticket.&lt;br /&gt;
* Screenshots of the representation in-world, in a reference viewer, and in-editor.&lt;br /&gt;
** You should include screenshots of the object inside a manually placed reflection probe, and outside.&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=PBR_Materials&amp;diff=1218674</id>
		<title>PBR Materials</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=PBR_Materials&amp;diff=1218674"/>
		<updated>2026-03-01T07:12:39Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Separated the wall of text a bit. /* What is it and Why is it used? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{multi-lang|1=PBR_Materials|2=/en}}&lt;br /&gt;
&#039;&#039;&#039;Physically Based Rendering&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{{note|1=This page is being actively updated with new content and links as GLTF PBR Materials and related new features are being released. Contributions to this page may be from Second Life residents and staff members alike.}}&lt;br /&gt;
&lt;br /&gt;
[[Category:glTF]]&lt;br /&gt;
&lt;br /&gt;
== What is it and Why is it used? ==&lt;br /&gt;
&lt;br /&gt;
The term “Physically Based Rendering” or “PBR” is a technical term that may need some defining for most people since its use is unique to composing images in computer software.  The term itself is an abbreviation for a collection of complex mathematical algorithms that attempt to accurately represent the ways that light reflects off and interacts with objects in the real world.&lt;br /&gt;
&lt;br /&gt;
In the real world, it is the behavior of light on a piece of metal that allows us, the observer, to recognize “that object is made of metal” without actually reaching out and touching it.  The way a metal reflects light differs from that of a polished plastic or some other material, and these differences have been quantified by science.&lt;br /&gt;
&lt;br /&gt;
==== How it applies to Second Life ====&lt;br /&gt;
By mimicking real-world physics principles in the virtual world it allows for the creation of more immersive recognizable realistic spaces, but also it helps us relate to fantastical worlds a little better too.  While we may not be familiar with what a newly imagined creation is, a metal&#039;s inherent metal-ness and aged wood&#039;s inherent wood-ness remain constant, making it easier to intuitively understand what we are interacting with in a virtual environment.&lt;br /&gt;
&lt;br /&gt;
Because tying the mathematics to simulate materials in virtual spaces to how they behave in the real-world makes things more immediately recognizable, PBR has become the foundation for creating imagined worlds over the last decade.  The metallic shine of exoskeletal armor in superhero movies is driven by a PBR workflow, as is the plastic sheen of toys or the glint of frozen ice in animated classics.  Now we are moving to bring this standardized quality to your home in Second Life.&lt;br /&gt;
&lt;br /&gt;
Bringing PBR to Second Life means updating the basic calculations of how light is represented and interacts with the world of Second Life.  The goal is to integrate these changes while minimally changing how everything that presently exists in Second Life (designed prior to the introduction of PBR) looks.  While the preservation of creative intent and the aesthetic appeal of items users have enjoyed for over two decades of Second Life is always a priority, Second Life is an ever-evolving platform, and to continue to do so, some changes are inevitable.&lt;br /&gt;
&lt;br /&gt;
Lastly, while we are attempting to mimic real-world reflections and material properties, Second Life has to run on a wide variety of devices, so some shortcuts have to be made.  Reflections are not mirror-perfect, as has often been a long-standing hope and request from Residents.  While the addition of a reflection system does bring dreams of distortion free mirrors for our avatars in Second Life closer to reality, unfortunately, due to the calculation requirements of doing perfect reflections, mirrors are sadly still, for the moment, not practical. Please see the note in the [[#Understanding_and_Assisting_the_New_Reflections_System|section below]] for more info.&lt;br /&gt;
&lt;br /&gt;
== What does this mean for Residents? ==&lt;br /&gt;
[[File:PrePostPBR.png | thumb | right | The lamp in this screenshot uses [[PBR_Materials#Nomenclature_changes|Blinn-Phong]] (Classic SL Materials), and was created before PBR&#039;s integration into SL; and demonstrates some of the visual differences to existing content. The environment used for the screenshot is the viewer&#039;s default Midday preset (Note that the PBR viewer has a new Midday preset).&lt;br /&gt;
]]&lt;br /&gt;
=== Changes to Existing Content ===&lt;br /&gt;
The largest change by far is the addition of an environmental reflection system to Second Life.  For most existing items, this change shouldn’t have a drastic immediately observable impact.  A few things in your inventory that you already own may appear more reflective with the new graphics configuration and those reflections should feel more realistic and immersive with your current environment.  As a general rule: the “shinier” an object was before, the more environment reflections it will pick up and the more visual difference there will be.&lt;br /&gt;
&lt;br /&gt;
Another notable change is the addition of [https://en.wikipedia.org/wiki/Tone_mapping tonemapping] to the viewer. Tonemapping is a way of representing an image with a higher native dynamic range than the display can support. Tonemapping is a de-facto requirement of PBR pipelines. This means that colors in Second Life will generally appear more saturated with less detail being lost in shadows and highlights. [https://modelviewer.dev/examples/color.html Click here for some additional reading on the subject.]&lt;br /&gt;
&lt;br /&gt;
The tonemapping method used is called &#039;&#039;&#039;Academy Color Encoding System (&#039;&#039;&#039;aka &#039;&#039;&#039;&amp;quot;ACES&amp;quot;)&#039;&#039;&#039;, and can be read about [https://www.oscars.org/science-technology/sci-tech-projects/aces here] and [https://docs.nvidia.com/gameworks/index.html#devices/shield-hdr-dev-guide/hdr-dev-guide-nvidia-shield.htm here].&lt;br /&gt;
&lt;br /&gt;
[[PBR_Materials#Nomenclature_changes|Blinn-Phong]] content making use of the Specular parameters may look different if the items were never viewed under local lights, as the PBR viewer allows for the environment (sun &#039;&#039;and&#039;&#039; sky) to contribute to specular reflections. As such, if the object receives blue specular reflections from the sky, these reflections are tinted, and may look odd. This effect is the same as prior viewers, as if the object was lit by a blue local light.&lt;br /&gt;
&lt;br /&gt;
=== Removal of Advanced Lighting Model ( ALM ) Graphics Option ===&lt;br /&gt;
There have been changes to the &#039;&#039;&#039;Graphics &amp;gt; Advanced Settings...&#039;&#039;&#039; Preferences. The most notable of which is the removal of the “Atmospheric Shaders”, &amp;quot;Local lights&amp;quot; and “Advanced Lighting Model” options.&lt;br /&gt;
&lt;br /&gt;
For those users on lower-end hardware who depended heavily upon those options to navigate Second Life with an acceptable framerate, we recommend the following settings:&lt;br /&gt;
*{{code|Transparent Water: Disabled}}&lt;br /&gt;
*{{code|Screen Space Ambient Occlusion: Disabled}}&lt;br /&gt;
*{{code|Shadows: None}}&lt;br /&gt;
*{{code|Screen Space Reflections: Disabled}}&lt;br /&gt;
*{{code|Reflection Detail: Static Only}}&lt;br /&gt;
*{{code|Reflection Coverage: Manual only}}&lt;br /&gt;
&lt;br /&gt;
The {{code|Local Lights}} setting has been superceded by updates made to the &#039;&#039;&#039;World &amp;gt; Improve graphics speed...&#039;&#039;&#039; &amp;quot;Quality &amp;amp; Speed&amp;quot; slider options. Users on low-end hardware should ensure that their &amp;quot;Quality &amp;amp; Speed&amp;quot; slider is set at an appropriate level for their hardware.&lt;br /&gt;
&lt;br /&gt;
== What does this mean for Creators? ==&lt;br /&gt;
=== Nomenclature changes ===&lt;br /&gt;
The PBR project represents a large step towards integrating standard rendering techniques used in the games industry. As such, the nomenclature of some items has changed, notably, what was once called &#039;&#039;&#039;[[:Category:Materials|Materials]]&#039;&#039;&#039; is now referred to as &#039;&#039;&#039;Blinn-Phong&#039;&#039;&#039;. This does not represent any changes to the underlying rendering techniques (beyond those mentioned above); and as such Blinn-Phong &#039;&#039;is not&#039;&#039; the same as PBR Spec/Gloss workflows, seen in some game engines such as Unity.&lt;br /&gt;
&lt;br /&gt;
=== Importing PBR Materials from External Software: ===&lt;br /&gt;
For creators who work with external tools such as Blender, Adobe Substance Painter, Cinema4D, 3D-Coat, or have used the Unreal or Godot game engines, the use of PBR texture sets should already be familiar.  In fact, some creators have been using tools that use PBR workflows to create content for Second Life and have then been forced to sacrifice visual quality to convert that information into Second Life’s existing Blinn-Phong materials system.  &lt;br /&gt;
&lt;br /&gt;
Second Life is adopting the “Metallic/Roughness” PBR model, and in its ongoing commitment to using Open Source standards whenever it is practical to do so, the glTF 2.0 file format has been chosen as the upload format for PBR Material assets.  One of the primary goals of implementing PBR Materials is to have more continuity from content creation applications towards Second Life, and have more consistent content behavior once it’s inworld.&lt;br /&gt;
&lt;br /&gt;
=== Using Imported Materials in Second Life ===&lt;br /&gt;
[[File:Editing_Material.jpg|thumb|right|Material Editing]]&lt;br /&gt;
For people who build exclusively within Second Life, the addition of PBR also means that there will be a new [[LlGetInventoryType|Inventory Type]] called “Material”.  These new Materials can be purchased on the Marketplace and are shareable like any other permitted object in Second Life.&lt;br /&gt;
&lt;br /&gt;
PBR materials come as a bundle of textures.  These all travel as a single unit and are applied all at once.  If you wish to change tint, transparency, or other similar parameters of the Material you’ll need to modify the Material from the &amp;quot;Editing Material&amp;quot; floater.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Meshes uploaded after the PBR release will look slightly different (and more accurate to your editor of choice) due to a change in the way Second Life handles mesh assets (Previously, some data required for accurate tangent generation was discarded at upload time - this is no longer the case). This will mean that a PBR material applied to a mesh uploaded before PBR launched (28th November 2023) might look incorrect; a simple reupload of the mesh will solve this. &#039;&#039;&#039;This is not required, but recommended.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
Applying existing materials works similarly to existing textures. You have two means of doing so:&lt;br /&gt;
&lt;br /&gt;
==== Drag and Drop ====&lt;br /&gt;
Simply drag and drop onto the face of a prim or a mesh.  For example, if you have a tiled floor material and you place it on a selected prim cube face that face will now look like a tile floor and reflect light like a tiled floor would with all the material qualities contained in the material.&lt;br /&gt;
[[File:DragAndDrop_Mat_Wiki.png | thumb | none | Drag and Drop Functionality.]]&lt;br /&gt;
&lt;br /&gt;
==== Switch to PBR and select from Inventory ====&lt;br /&gt;
Alternatively, select a face on the prim or mesh you want to apply your material to and choose the “Blinn-Phong” drop-down and change it to “PBR Metallic Roughness”, then select “Choose an item from your inventory” and apply.&lt;br /&gt;
[[File:PBRSelectorV3.png |left| thumb | none | Menu drop-down demonstration.]][[File:Material_Dropdown.jpg|thumb|none|Dropdown as of v7.0.1.689]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Texture Transforms ====&lt;br /&gt;
With PBR materials, texture transforms work in a slightly different way to Blinn-Phong transforms. Blinn-Phong (and OpenGL) has the texture origin in the lower left corner of the texture, whereas glTF materials (and Vulkan) has the texture origin in the &amp;lt;b&amp;gt;upper left&amp;lt;/b&amp;gt; corner.&lt;br /&gt;
&lt;br /&gt;
This means that PBR materials will react differently (namely, they are inverted in the Y (glTF v) axis) to Blinn-Phong materials.&lt;br /&gt;
For a simple solution to convert a Blinn-Phong texture transform to a PBR texture transform, please see [https://community.secondlife.com/forums/topic/507448-lsl-math-for-gltf-transformation/?do=findComment&amp;amp;comment=2678959 this forum thread].&lt;br /&gt;
&lt;br /&gt;
For more information, [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#images see here] and [https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform here].&lt;br /&gt;
&lt;br /&gt;
=== Media-on-a-Prim ===&lt;br /&gt;
Media on a prim will continue to work largely as it has done prior to PBR&#039;s launch.&lt;br /&gt;
&lt;br /&gt;
When using MOAP on a face with a PBR texture, it will function as if the media texture has an override to the &#039;&#039;&#039;Base Color&#039;&#039;&#039; and &#039;&#039;&#039;Emissive&#039;&#039;&#039; maps. Note that the emissive map is overridden, however the emissive tint is not (so, to disable the emissive map you can set the emissive tint to black &amp;lt;0,0,0&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Tools &amp;amp; Tutorials ==&lt;br /&gt;
As the PBR system is new, it is expected that existing users may be confused at first on how everything works. As such, Linden Lab and some third parties have provided some tutorials and informational videos:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=JtjGf06B8ZA Second Life University - PBR Materials]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=BJ1eRTlWDYk Second Life University - How to create PBR Materials]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://substance3d.adobe.com/tutorials/courses/pbrguide Allegorithmic (Adobe) PBR Guide]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The above tutorials require knowledge and access to Adobe&#039;s Substance Painter (subscription based software on Adobe website, or sold as perpetual license with one year of updates on Steam). Below are some open source tools and links found Googling &#039;Blender&#039;, &#039;glTF&#039; (requiring Blender 3.3)&lt;br /&gt;
*&#039;&#039;&#039;[https://www.khronos.org/blog/art-pipeline-for-gltf Khronos Art Pipeline tutorial]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://materialmaker.org Material Maker software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://aiaicapta.in/gltf-packer/ glTF Packer software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://armorpaint.org Armorpaint software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://armorlab.org Armorlab software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://youtu.be/KTPdNUGwIGc glTF Blender Tutorial]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://youtu.be/p7OPRoT6FkY Exporting glTF Alpha Textures tutorial]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== PBR Stand-In Textures and Outdated Viewers ==&lt;br /&gt;
It is worth mentioning that during the adoption period of a new system, a substantial portion of Second Life Residents view the world through third party viewers and mobile viewer solutions that will not have updated to be able to see the new content.  Anyone viewing a PBR Material on a viewer that cannot display it will see the “underlying” non-PBR texture.  By default, this is a pine box or a completely blank texture.  Those who wish their content to be viewable by as many people as possible, might consider creating a Diffuse texture ( with baked lighting, the kind that is easily generated with tools like Substance Painter&#039;s “Baked Lighting Filter” ) and applying it as a regular texture to the object they’re placing the PBR material on, prior to applying the PBR material.  While this is an extra step in content creation, and complicates things somewhat, if you wish for your content to be appreciated by all, it’s worth considering adding this extra step to your workflow.  People designing PBR materials for distribution and sale might also consider offering a “Diffuse Only fallback” texture to accompany the PBR material specifically for people who cannot see the PBR content.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Once a PBR Material is applied, the Blinn-Phong textures cannot be updated. To remedy this, remove the PBR Material, then make changes to the Blinn-Phong textures and then reapply the PBR Material.}}&lt;br /&gt;
&lt;br /&gt;
== Understanding and Assisting the New Reflections System ==&lt;br /&gt;
Those creators that work at house-scale, or produce items that can be walked through, can gain additional control over the lighting in their creations by taking the time to fully understand the new toolkit that influences environmental reflections. There is a new type of volume that can be created and appended to object linksets specifically for scenes with these kinds of spaces.  These volumes define a custom area where reflections are calculated, overriding the default solution.  So-called “Reflection Probes” should be placed in a manner such that &#039;&#039;the fewest number of probes fills the largest amount of space possible with minimal overlap&#039;&#039;.  One probe per room is a good reference point for a general living space like a house.  If multiple probes exist in a given area they can cause visual artifacts and negatively impact performance  (also known as viewer lag).  &#039;&#039;&#039;&#039;&#039;Do not affix reflection probes to small creations such as furniture or decor.&#039;&#039;&#039;&#039;&#039;  Small items such as tables, chairs, musical instruments, candle sticks etc should use the reflection sample volume in the space in which they are placed.  They should not have one appended.  It is &#039;&#039;&#039;strongly&#039;&#039;&#039; recommended that any object that contains a reflection sample probe be left as “modify”, so the positioning of the probe can be adjusted or even removed by the owner of the item should they wish.&lt;br /&gt;
&lt;br /&gt;
{{Enote|Reflection probes &#039;&#039;&#039;are not&#039;&#039;&#039; intended for use with planar mirrors, and will look incorrect when being used to do so. Planar mirrors have been flagged for future work, but are not directly in the scope of the PBR project.}}&lt;br /&gt;
&amp;lt;!-- This image has been removed from public view as it is factually incorrect; as it references the blue tint on the floor as originating from the ocean, and not the sky. Note that the public project viewer currently also has a bug wherein the sky contribution on objects is too powerful; this has since been fixed however a public build containing the fix is yet to be released. [[File:ProblePlacementDemo_v.png | right | A visual demonstration of how manual probes interact with the surrounding environment and enhance it.]] --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== When is it Recommended to Create a Reflection Probe? ==&lt;br /&gt;
Manually placed probes are good for cleaning up undesirable noise and lighting from automatically placed probes that may be visually disruptive or confusing.  If you look in the image of the room in a house, on the left hand side of it, you can see a blue tint on the floor, which is the reflected blue light from the sky.  The probe filling half the room blocks this, and when it is extended to completely fill the room and just slightly beyond the thickness of the walls, (lower copy of the image) the problem is solved.  The probe does not need to be a part of the linkset for the rest of the room in order to function; however, once you have them placed it’s possible to add them into linksets like any other prim.&lt;br /&gt;
&lt;br /&gt;
=== Constructing a Reflection Sample Volume ( aka Reflection Probe ) ===&lt;br /&gt;
Start off in the Build Menu &#039;&#039;(Ctrl+3)&#039;&#039; by rezzing a basic prim. Under the &amp;lt;code&amp;gt;Features&amp;lt;/code&amp;gt; tab, at the bottom check the box labelled &amp;lt;code&amp;gt;Reflection Probe&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[File:ProbeUI.jpg | thumb | none | Manual Probe Creation UI.]]&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Reflection Probe&amp;lt;/code&amp;gt;:  Enabling this creates a Reflection Sampling Volume within the bounding box of the prim.&lt;br /&gt;
* &amp;lt;code&amp;gt;Box / Sphere Drop-down&amp;lt;/code&amp;gt;: Choose whether you want this sample to project reflections within a box shaped volume or a spherical one.&lt;br /&gt;
* &amp;lt;code&amp;gt;Dynamic&amp;lt;/code&amp;gt;: Allow skinned objects (e.g. Avatars, animesh, etc.) to be captured in the reflection.&lt;br /&gt;
* &amp;lt;code&amp;gt;Ambiance&amp;lt;/code&amp;gt;: Affects how much objects within the volume are lit as if they have bounced light hitting them from faked indirect illumination. &#039;&#039;See [[PBR_Materials#Fine-Tuning_Reflection_Sample_Volumes|&amp;quot;Fine-Tuning Reflection Sample Volumes&amp;quot;]] for more info.&lt;br /&gt;
* &amp;lt;code&amp;gt;Near Clip&amp;lt;/code&amp;gt;: Sometimes, there will be a large obstruction in the center of the volume you wish to use as a reflection probe. For example, suppose that there is a large architectural supporting structure in the middle of the room, such as a column. If the probe gets placed internally inside this, it will reflect the internal surface of the column instead of the room in which it is placed.  Increasing Near Clip will make the sampling volume exclude objects n meters from the center so they don’t get included in the reflection. &#039;&#039;See [[PBR_Materials#Fine-Tuning_Reflection_Sample_Volumes|&amp;quot;Fine-Tuning Reflection Sample Volumes&amp;quot;]] for more info.&lt;br /&gt;
&lt;br /&gt;
Select an appropriate probe shape for your scene. For example, if you want to create a reflection probe that covers a room, select a &amp;lt;code&amp;gt;Box&amp;lt;/code&amp;gt; shape, or for other objects a &amp;lt;code&amp;gt;Sphere&amp;lt;/code&amp;gt; shape is recommended.&lt;br /&gt;
&lt;br /&gt;
Move the reflection probe and resize it as necessary to fit your desired purpose. For the room example, resize the box so the probe just touches the walls, ceiling and floor of the room.&lt;br /&gt;
&lt;br /&gt;
Naming your probes is also recommended, so other people ( or yourself later on ) will remember why there’s a large transparent prim in the middle of your build.&lt;br /&gt;
&lt;br /&gt;
If you wish to come back and edit the reflection probe later on, enable both &amp;lt;code&amp;gt;Show Reflection Probe Volumes&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Select Reflection Probes&amp;lt;/code&amp;gt; under &amp;lt;code&amp;gt;Build &amp;gt; Options&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Fine-Tuning Reflection Sample Volumes ===&lt;br /&gt;
{{KBwarning|Sample volumes do not react to prim parameter changes.  Options such as “Hollow” or “Taper” or “Shear” do not change how the sampling area behaves.}}&lt;br /&gt;
&lt;br /&gt;
That said, it may be helpful to visualize &amp;lt;code&amp;gt;Near Clip&amp;lt;/code&amp;gt; as the hollowing out of your reflection sample volume, but the hollow and near-clip are not in any way linked and doing so is purely a convenience.  Reflection probe sample volumes only respond to changes in scale and position; however, different probe volumes behave differently when resized.&lt;br /&gt;
* &amp;lt;code&amp;gt;Box&amp;lt;/code&amp;gt; Probes: These are affected by position and non-uniform scale.&lt;br /&gt;
Example: If I create a box reflection probe and scale it to 10m,30m,10m size, the full volume will be affected by the probe.&lt;br /&gt;
* &amp;lt;code&amp;gt;Sphere&amp;lt;/code&amp;gt; Probes: These are affected by position and &#039;&#039;&#039;uniform&#039;&#039;&#039; scale only, otherwise the smallest dimension is used.&lt;br /&gt;
Example: If I create a spherical probe and scale it to 10m,30m, and 10m size , the probe will sample a 10 meter diameter spherical volume at the center of the probe.  However, a 30m,30m,30m sphere will create a sampling volume of a 30 meter diameter sphere.&lt;br /&gt;
[[File:SphereProbeErrror.jpg | thumb | none | A diagram of how spherical probes function (or, don&#039;t function as you may expect) while non-uniformly-scaled.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; controls how ambient light (Found in your [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]]) affects, or does not affect, the contents of the reflection probe, and the intensity of indirect lighting (aka Irradiance).&lt;br /&gt;
This value is influenced by the &amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; value found in the user&#039;s [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]], wherein if the Ambiance value given in the [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]] is higher than the value defined by the probe itself, it inherits the ambiance value of the [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]], or alternately if the ambiance value defined by the probe is higher, the probe&#039;s value is used.&lt;br /&gt;
&lt;br /&gt;
There are a few operation modes that are set with the &amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; value, in tandem with above:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Probe Ambiance behavior&lt;br /&gt;
! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0.00 || Ambient color from the environment applies at full intensity. &#039;&#039;&#039;Default for pre-PBR skies.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0.01 .. 0.99 || Blends ambiance with indirect lighting (sun + emissive or illuminated surfaces) within the probe.&lt;br /&gt;
|-&lt;br /&gt;
| 1.00 || Indirect light applies at full intensity, without ambient color. &#039;&#039;&#039;Default for PBR skies.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 1.01 .. 4.00 || Multiplier for indirect light, makes everything brighter within the probe.&lt;br /&gt;
|-&lt;br /&gt;
| 4.01 .. 100.00 || Multiplier for indirect light, but only further affects light from the sun. (Local lights are capped to 4.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Complex Reflection Probe placement ===&lt;br /&gt;
&lt;br /&gt;
At times, you may wish to place a reflection probe in an area where the reflection probe types (Sphere or Box) do not conform to the shape of the area. For example, a Box probe in a loft room has a triangular shape, which results in &amp;quot;probe bleed&amp;quot; wherein the influence volume affects an area larger than what is needed, and thus bleeds out onto the exterior roof, which is undesirable.&lt;br /&gt;
&lt;br /&gt;
In these cases, you may need to place multiple probes in order to blend together the sample volumes to achieve the desired result. In the loft room example, it&#039;s best to start with 3 box probes, which cover the floor, and each side of the roof. Then, use an additional box probe as &amp;quot;fill&amp;quot; to cover the gap inbetween the 3 probes. The main fill probe may need to be rotated at an angle to get the most coverage possible. If it is not possible to fill the gap between the 3 probes with this fill probe, you may wish to add more box probes to act as &amp;quot;secondary fill&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[TODO: Put photo examples of the above here!]&lt;br /&gt;
&lt;br /&gt;
Alternately, Sphere probes may be used to achieve the same result. Sphere probes have considerably softer blending than box probes, so this may give you the best results in severe bleed conditions.&lt;br /&gt;
&lt;br /&gt;
=== Unsupported use-cases ===&lt;br /&gt;
{{KBwarning|Do not attempt to do anything listed below. These uses are liable to either intentionally be disabled, or stop working in future updates.}}&lt;br /&gt;
You may be tempted to use reflection probes in the following ways, however these use-cases are either intentionally disabled or result in undefined behavior, and may work now but may not in future. &#039;&#039;&#039;You have been warned.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Under any circumstances, you should NOT:&#039;&#039;&#039;&lt;br /&gt;
* Wear a reflection probe. This intentionally does not work. (Reflection probes are a property of the scene, not an individual object / avatar)&lt;br /&gt;
* Attach a reflection probe to a physics-enabled object, e.g. vehicles. This intentionally does not work. (As above, the reflection probes are part of the scene; not part of an individual object. Reflection probes by design do not update in real-time, thus the object would always have incorrect reflections anyway.)&lt;br /&gt;
&lt;br /&gt;
== PBR Material Composition ==&lt;br /&gt;
PBR-defined values are measured by optical sensors and other capture tools and recorded in databases.  These parameters are then put into commercial software to be used by content creators.  Using software designed to create PBR content is always recommended; however, understanding how PBR materials are assembled can assist with editing and compiling them.  Having a direct understanding of what each individual color channel contributes is essential for editing PBR textures without relying upon some of the more advanced toolkits available.&lt;br /&gt;
&lt;br /&gt;
All PBR Values are listed from 0 to 1.0, though in an actual image, the values range from 0 - 255.&lt;br /&gt;
PBR Materials are composed of a set of four specifically designed textures; they are as follows:&lt;br /&gt;
&lt;br /&gt;
===Base Color [ RGB ] + Transparency [ A ]===&lt;br /&gt;
[RGB]: This is the unlit color of the surface.  This differs from the “Diffuse” texture that Second Life uses.  Diffuse textures often include faked reflection and specular information as well as added Ambient Occlusion shadows.  Base Color textures do not get any of this added information.  For metals ( as defined by the metalness value ), Base Color also determines specular reflection color, whereas, in non-PBR systems, this is defined by the Specular texture and tint.  In certain PBR texturing applications, Base Color is sometimes also referred to as “Albedo”.&lt;br /&gt;
&amp;lt;br /&amp;gt;[ A ]: Alpha Channel, dictates the transparency of the entire material overall.&lt;br /&gt;
&lt;br /&gt;
The Base color texture should be devoid of lighting information.&lt;br /&gt;
&lt;br /&gt;
===Occlusion [R] / Roughness [G] / Metalness [B]===&lt;br /&gt;
This texture is composed of 3 unrelated grayscale images stored in 3 different color channels of an RGB texture.&lt;br /&gt;
&amp;lt;br /&amp;gt;[R]: (Ambient) Occlusion is lighting data, &#039;&#039;&#039;removing&#039;&#039;&#039; the need to bake down shadows on to the Base Color map. Note that &#039;&#039;white&#039;&#039; (&amp;lt;1,1,1&amp;gt;) means no occlusion is applied, and &#039;&#039;black&#039;&#039; (&amp;lt;0,0,0&amp;gt;) applies full occlusion.&lt;br /&gt;
&amp;lt;br /&amp;gt;[G]: Roughness data ranges from 0 to 1.0, but the actual range of physical surfaces ranges from approximately 0.05 to 0.985.  No surface is perfectly smooth or completely rough.  The rougher a surface is the less mirror-like it behaves.  &lt;br /&gt;
&amp;lt;br /&amp;gt;[B]: Metalness values are mostly black or white.  Either the material is a conductive metal like copper, or it’s a non-metal like fabric.  0.0 is Non-Metallic, 1.0 is Metallic.  There are almost no materials with gray metalness values.&lt;br /&gt;
&amp;lt;br /&amp;gt;The alpha channel is ignored, as per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification].&lt;br /&gt;
&lt;br /&gt;
{{Enote|The Occlusion map &#039;&#039;&#039;is not&#039;&#039;&#039; controlled by the &amp;quot;Screen Space Ambient Occlusion&amp;quot; toggle in the Advanced Graphics Settings. &#039;&#039;&#039;The Occlusion map is &#039;&#039;always&#039;&#039; enabled.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
===Emissive [ RGB ]===&lt;br /&gt;
This determines the amount and the color of unlit (ignores ambient light conditions) areas of your material. When giving an object a white emissive map, the object will act as if the Blinn-Phong &amp;quot;Fullbright&amp;quot; option was checked. This map is useful for items which are expected to emit light, e.g. a table lamp, where the lamp shade would appear to glow when the lamp is turned on. If you wish to toggle the lamp on and off, it&#039;s recommended to change the Emissive Tint value to black (functionally disabling the emissive map, thus turning the lamp off), and then changing the tint back to your desired color to turn the lamp back on. Leaving the Emissive slot empty is recommended when it’s unused.&lt;br /&gt;
&lt;br /&gt;
Note that glow (The postprocessing effect), is controlled by a separate parameter in the build floater and is intentionally not part of the PBR material window. Glow is modulated by the emissive map, so black areas of an object&#039;s emissive map will not glow, similar to how Blinn-Phong emissive maps and glow work.&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the alpha channel is ignored.&lt;br /&gt;
&lt;br /&gt;
===Normal [ RGB ]===&lt;br /&gt;
The normal maps generated by your baking application / normal map generation toolkit should be compatible with Second Life in most cases.  The most common pitfall is using normal maps generated with “inverted” Green channels, such as those that are used for Direct3D and Unreal Engine.  Normal texture data redirects light in a different direction based on the vectors indicated by the color, so if the green channel is backward, it’ll seem to be “pointing the wrong way”.  To phrase the problem a different way, all the things that should be bumps look like dents, and vice versa.  If this occurs, double check that your settings aren’t for Direct3D and try re-generating it.  Also, taking it into image editing software and inverting the green channel only sometimes is a sufficient fix.&lt;br /&gt;
&lt;br /&gt;
Normal map tangent spaces are an extremely technical subject that most users need not be concerned with as most modern applications default to the correct setting.  However, if your normal maps look drastically different inside Second Life, compared to your source application, and you’ve already confirmed it’s not an inverted green channel, then checking which tangent space settings are being used is the next step.  PBR Normals use Mikkelsen Tangent Space. (Often abbreviated MikkT)  If you are unsure what this means, use similar workflows and settings for Second Life PBR as those that are generally recommended for the Godot 4 game engine.&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the blue channel of a normal map is only allowed to contain values above 0.5 to a max of 1 (255).&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the alpha channel is ignored.&lt;br /&gt;
&lt;br /&gt;
== Material Reference Libraries ==&lt;br /&gt;
As PBR is &#039;&#039;Physically Based&#039;&#039;, you may wish to know how to recreate a real-life material in PBR form. What color should you use? What metalness value should it have?&lt;br /&gt;
&lt;br /&gt;
Fortunately, reference libraries exist which can tell you how to recreate a given material in a PBR workflow.&lt;br /&gt;
&lt;br /&gt;
=== List of Reference Color Palettes &amp;amp; libraries ===&lt;br /&gt;
*&#039;&#039;&#039;[https://physicallybased.info/ Physically Based]&#039;&#039;&#039;&lt;br /&gt;
** Select the &#039;&#039;Godot&#039;&#039; Engine, with the Color Space as &#039;&#039;sRGB Linear&#039;&#039; and Color Representation as &#039;&#039;0-1&#039;&#039; (Depending on what you are doing, you may need to convert the value into gamma-corrected sRGB space, which can be done [https://davengrace.com/dave/cspace/ here.] Enter the value on the 3rd row.)&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=TcTh-1X2FsQ Grzegorz Baran Library]&#039;&#039;&#039;&lt;br /&gt;
**Available on [https://www.youtube.com/watch?v=TcTh-1X2FsQ YouTube (video format, older)], or [https://gbar.gumroad.com/l/cbwkvo PDF (most up-to-date)].&lt;br /&gt;
*&#039;&#039;&#039;[https://seblagarde.wordpress.com/2014/04/14/dontnod-physically-based-rendering-chart-for-unreal-engine-4/ Dontnod Entertainment Library]&#039;&#039;&#039;&lt;br /&gt;
** A very small library, but useful nonetheless.&lt;br /&gt;
*&#039;&#039;&#039;[https://docs.studio-397.com/developers-guide/general-reference/pbr-an-introduction-authoring-guide Studio 397]&#039;&#039;&#039;&lt;br /&gt;
** A small collection of various materials.&lt;br /&gt;
*&#039;&#039;&#039;[http://wiki.polycount.com/wiki/PBR Polycount Wiki]&#039;&#039;&#039;&lt;br /&gt;
** Not so much a library in itself, but links out to other libraries. &#039;&#039;&#039;See the Color Charts section&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== List of PBR asset libraries ===&lt;br /&gt;
*&#039;&#039;&#039;[https://ambientcg.com/ ambientCG]&#039;&#039;&#039;&lt;br /&gt;
** Provides CC0 licensed HDRi, Metal-Rough materials and .sbar (Substance Painter material) files.&lt;br /&gt;
*&#039;&#039;&#039;[https://polyhaven.com/ Poly Haven]&#039;&#039;&#039;&lt;br /&gt;
** Provides CC0 licensed HDRi, Metal-Rough materials and models.&lt;br /&gt;
&lt;br /&gt;
== Uploading Materials ==&lt;br /&gt;
There are two different methods of creating PBR material assets:&lt;br /&gt;
&lt;br /&gt;
=== Method 1 : Direct Upload a glTF File ===&lt;br /&gt;
The most convenient method to create an entirely new material is to directly upload a glTF file from your computer.&lt;br /&gt;
&lt;br /&gt;
This is accessible via &amp;lt;code&amp;gt;Build &amp;gt; Upload &amp;gt; Material...&amp;lt;/code&amp;gt; &lt;br /&gt;
[[File:PBRBuildMenu.jpg | thumb | none | PBR Upload in main build menu. ]]&lt;br /&gt;
&lt;br /&gt;
Select a .GLB or a .GLTF file from your computer and Open it:&lt;br /&gt;
This will give the following window:&lt;br /&gt;
[[File:PBRMatMenu.jpg | thumb | none| The layout of the PBR material creation and editing interface.]]&lt;br /&gt;
&lt;br /&gt;
All of the editable material properties that most creators are familiar with are integrated into the Material&#039;s vertically sizable window on upload.  &lt;br /&gt;
&lt;br /&gt;
Clicking “Save” pays the upload fees and creates the Material assets using the local filename. Using &amp;quot;Save As...&amp;quot; provides a window to name all the assets to unique inworld names. There is a 63 char limit for names of inventory assets but the naming floater on this Materials upload &amp;lt;b&amp;gt;has no length restrictions&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Textures will be uploaded to the inventory&#039;s default Textures folder (NoMod, NoCopy, Transfer) with an appended &amp;quot;type&amp;quot; added to the given name. (ie. asset name (Base Color), asset name (Normal), asset name (Metallic Roughness). In naming the asset keep in mind that the bracketed appendix is included in the name&#039;s 63 character limit. The PBR object is uploaded (Mod, NoCopy, Transfer) to the inventory&#039;s Material folder with the appended text (Material). Presently, naming the glTF asset from Save As... will NOT append (Material) to the inventory&#039;s name. Saving from the locally named file will.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Each individual texture used to compose the Material object is charged a separate upload fee. (ie. If you have 4 textures (Base Color, Metallic Roughness, Emissive, Normal) you are charged 4 times. If you only have a Normal and Metallic-Roughness texture you will be charged for 2, etc.)}}&lt;br /&gt;
&lt;br /&gt;
=== Method 2 : Edit a Blank. Create PBR Materials without a glTF File ===&lt;br /&gt;
If you have the necessary textures to compose a PBR material but do not have a glTF file you will need to build a Material from inventory.  Make sure your textures are in the correct format, with the correct data in the proper channels.  This requires some understanding of how [[PBR_Materials#PBR_Material_Composition:|PBR materials work]].  Once you have that, upload your textures as you normally would.&lt;br /&gt;
&lt;br /&gt;
[[File:PBRMenuUI.jpg | thumb | none | The PBR Materials UI in the Build Floater.]]&lt;br /&gt;
&lt;br /&gt;
* Find the &amp;quot;Materials&amp;quot; folder in your inventory, right click, and select &amp;quot;New Material&amp;quot; from the context menu.&lt;br /&gt;
* Name your new material something appropriate (E.g. &amp;quot;Red Bricks&amp;quot; for a red brick wall, etc.)&lt;br /&gt;
* Right click on your new material, and select &amp;quot;Open&amp;quot; from the context menu.&lt;br /&gt;
* Upload your PBR textures using the standard texture workflow (Usually, under &amp;lt;code&amp;gt;Build &amp;gt; Upload &amp;gt; Image&amp;lt;/code&amp;gt;).&lt;br /&gt;
* In the material window, select the appropriate maps that you just uploaded in their respective slots.&lt;br /&gt;
{{KBwarning|While there may be a temptation to try and place a Blinn-Phong “Diffuse” texture into the “Base Color” slot and a Blinn-Phong “Specular&amp;quot; into the “Metallic/Roughness” slot, doing so will not produce desirable results.  However, trying this won’t generate any warnings as there is no way to check that you’ve put the right texture type into the inputs, and the material will save and be applicable to objects.  &#039;&#039;&#039;Unfortunately, what will be created is not a functional PBR material.  Please do not do this.&#039;&#039;&#039;}}&lt;br /&gt;
* Once done, verify the material parameters are correct (E.g. Base color tint, M/R factor, Emissive tint, etc.)&lt;br /&gt;
* Save your new material.&lt;br /&gt;
* EITHER: [[PBR Materials#Drag_and_Drop|Drag and drop the material onto a rezzed prim]], OR edit the prim, click the &amp;quot;Blinn-Phong&amp;quot; drop-down, select &amp;quot;PBR Metallic-Roughness&amp;quot;, and click &amp;quot;Choose from Inventory&amp;quot;, and select your new material.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- * Create a test cube and select a face, switch it to be a PBR material.&lt;br /&gt;
* Select “Choose from Inventory”&lt;br /&gt;
* Instead of choosing something from your inventory, choose “Blank” and click “Ok”&lt;br /&gt;
* Choose “Edit Selected”,  this will open up the material creation UI.&lt;br /&gt;
* Choose your appropriate maps from your texture inventory and slot them into their matching locations.&lt;br /&gt;
{{KBwarning|While there may be a temptation to try and place a pre-PBR “Diffuse” texture into the “Base Color” slot and a pre-PBR “Specular&amp;quot; into the “Metallic/Roughness” slot, doing so will not produce desirable results.  However, trying this won’t generate any warnings as there is no way to check that you’ve put the right texture type into the inputs, and the material will save and be applicable to objects.  &#039;&#039;&#039;Unfortunately, what will be created is not a functional PBR material.  Please do not do this.&#039;&#039;&#039;}}&lt;br /&gt;
* Click “Save to Inventory”, this will bring up a prompt to name your new PBR material. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Creating Color Variants ===&lt;br /&gt;
After you’ve uploaded your first Material for your project, if you’re a store owner who would like to release more than one color palette for your creation, as many clothing and furniture designers find it useful to do, &#039;&#039;&#039;it’s important to not upload a new glTF file for every single variant of your Material&#039;&#039;&#039;.  Most variant materials, only the base color will change.  The Occlusion, Roughness, Metalness, Emissive and Normal map texture slots will remain the same.  Since these maps have already been uploaded once, if we upload a second glTF File, they will be duplicated.  Having different copies of the same texture, with differing UUID’s means that they will clog up download bandwidth and video card memory (not to mention your inventory as well).  This is very bad for Second Life.  So, for this reason, it’s recommended that when you create texture variants, you upload the additional copies of the base-color texture separately.  Then open your newly uploaded material in edit mode, choose the Base Color texture, and change it out for one of your newly uploaded Base Color textures, and click “Save As”.  This will create a second copy of your material that uses all the correct texture maps without needlessly duplicating them and causing additional lag.&lt;br /&gt;
&lt;br /&gt;
{{KBtip| You can also avoid uploading additional textures altogether by uploading a white color variant, then making use of the Base Color Tint parameter!}}&lt;br /&gt;
&lt;br /&gt;
== Double Sided Parameter : Uses and Dangers ==&lt;br /&gt;
“Double Sided” is a new property unique to Materials.  When “Double Sided” is checked, the surface upon which this material is placed will be drawn twice; once for the outward-facing portion of the surface, and a second time for the inward-facing side of the surface that is normally invisible without a double-sided material.  This option should only be used for very specific meshes that were designed to be used with double-sided materials, since placing materials with this parameter checked on normal objects will simply cause the viewer to draw it twice ( and thereby create additional viewer lag ) for no observable change.  It is &#039;&#039;&#039;very strongly recommended&#039;&#039;&#039; that this option be unchecked for any material that is to be distributed for general use.  Even more so if the “Modify” permission is revoked.  If you wish to distribute a version of a material that has “Double Sided” checked, please include a second copy of the material that has “Double Sided” unchecked as well, with an accompanying explanation to the next user as to why this was done.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
*LeafTextures_DoubleSided&lt;br /&gt;
*LeafTextures_SingleSided&lt;br /&gt;
&lt;br /&gt;
When designing mesh content for use with the double-sided material parameter, it is also &#039;&#039;very strongly recommended that you separate the triangles you intend to use the double-sided material upon into a separate mesh “face”&#039;&#039;, so as to not unintentionally render the portions of the mesh that already have triangles designed to represent the internal portion of the object a second time.&lt;br /&gt;
&lt;br /&gt;
== LSL Scripting ==&lt;br /&gt;
&lt;br /&gt;
Documentation for the LSL interface with glTF materials can be found on the following pages:&lt;br /&gt;
*[[GLTF Overrides]]&lt;br /&gt;
*[https://wiki.secondlife.com/wiki/Category:LSL_Material Category:LSL Material]&lt;br /&gt;
&lt;br /&gt;
== Recommended Application Settings ==&lt;br /&gt;
The glTF file format is widely adopted and many applications have export functionality for this type of file.&lt;br /&gt;
Below is a non-comprehensive list of some of the more popular applications that export glTF files.&lt;br /&gt;
&lt;br /&gt;
{{KBtip|&#039;&#039;&#039;The recommended HDRi for use in Second Life content creation is available at [https://github.com/Jenna-Huntsman/Second-Life-Resources/tree/main/PBR/HDRi this Github repository].&#039;&#039;&#039; This HDRi will closely match the lighting of objects under the PBR &amp;quot;Midday&amp;quot; environment preset.}}&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|While every effort has been made to ensure the recommended HDRi matches SL as close as possible, you may need to adjust the &amp;quot;Environment Exposure&amp;quot; parameter in your editing program to get 1:1 results with SL.}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===[https://www.blender.org/ Blender]===&lt;br /&gt;
&lt;br /&gt;
{{KBwarning|Older Blender versions have bugs in their glTF export tools which make their output incompatible with Second Life. &#039;&#039;&#039;Use of Blender versions of 3.3 and above are highly recommended to avoid issues.&#039;&#039;&#039;}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{{KBcaution|Please ensure that you correctly set up an HDRi in your viewport, as otherwise the rendering in Blender and in SL will not match. Please see [[PBR Materials#Recommended_Application_Settings|here for a reference HDRi]]. &#039;&#039;&#039;Please see [https://www.polygonartists.com/hdri-environment-maps-in-blender/ this tutorial].&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
Upon starting your project:&lt;br /&gt;
&lt;br /&gt;
Under: &amp;lt;code&amp;gt;Scene &amp;gt; Render Properties &amp;gt; Color Management&amp;lt;/code&amp;gt;:&lt;br /&gt;
*{{code|View Transform: Standard}}&lt;br /&gt;
*{{code|Sequencer: Linear ACEScg}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Create your materials within Blender using Principled BSDF Shader Nodes and the glTF Settings node.  Export them according to the official &#039;&#039;&#039;[https://docs.blender.org/manual/en/3.6/addons/import_export/scene_gltf2.html Blender Documentation.]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Second Life does not support BSDF Clearcoat, Subsurface, Anisotropy or Transmission parameters at this time.}}&lt;br /&gt;
&lt;br /&gt;
For materials creation, 2 example .blend files are provided, one using separate Occlusion, Roughness and Metallic textures (&amp;quot;Long Form&amp;quot;), and one that handles a pre-packed ORM map (&amp;quot;Short Form&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
The below files are intended for use with Blender versions 3.3 and above.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[https://github.com/Jenna-Huntsman/Second-Life-Resources/blob/main/glTF%20Node%20Tree%20-%20Long%20Form.blend glTF Node Tree - Long Form.blend]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[https://github.com/Jenna-Huntsman/Second-Life-Resources/blob/main/glTF%20Node%20Tree%20-%20Short%20Form.blend glTF Node Tree - Short Form.blend]&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.adobe.com/products/substance3d-painter.html Adobe Substance 3D Painter] === &lt;br /&gt;
{{KBcaution|While Substance Painter is generally considered to be stable, in some (rare) situations Substance may output a malformed glTF which will be rejected by the viewer. If this happens, import your materials into Blender, then export a glTF from Blender - this should be fixed in a future version of Substance.}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{{KBcaution|Please ensure that you correctly set up an HDRi in your viewport, as otherwise the rendering in Substance and in SL will not match. Please see [[PBR Materials#Recommended_Application_Settings|here for a reference HDRi]]. Please see [http://y2u.be/6goXJ2CJzaM this tutorial].}}&lt;br /&gt;
When starting your project:&lt;br /&gt;
&lt;br /&gt;
*{{code|Template: PBR - Metallic Roughness Alpha-test (starter_assets)}}&lt;br /&gt;
&lt;br /&gt;
OR&lt;br /&gt;
&lt;br /&gt;
*{{code|Template: PBR - Metallic Roughness Alpha-blend (starter_assets)}}&lt;br /&gt;
&lt;br /&gt;
Verify the following settings:&lt;br /&gt;
*{{code|Document Resolution: [Set as desired]}}&lt;br /&gt;
*{{code|Normal Map Format: OpenGL (Y+)}}&lt;br /&gt;
*{{code|Compute Tangents Per Fragment: YES}}&lt;br /&gt;
&lt;br /&gt;
Under &amp;quot;Color Management&amp;quot;:&lt;br /&gt;
{{KBtip| For a more in-depth explanation of the below settings, and issues that you may encounter, visit [https://mrlixm.github.io/blog/substance-painter-color-management/#aces-workflow this page].}}&lt;br /&gt;
*{{code|Color Management: OpenColorIO}}&lt;br /&gt;
*{{code|OpenColorIO Configuration: ACES 1.0.3}}&lt;br /&gt;
*{{code|Standard sRGB color space: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|8 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|16 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|Material color space default: Utility - sRGB - Texture}}&lt;br /&gt;
&lt;br /&gt;
When exporting:&lt;br /&gt;
&lt;br /&gt;
Settings Tab - Global Settings&lt;br /&gt;
*{{code|Output template: glTF PBR Metal Roughness}}&lt;br /&gt;
*{{code|Size: Based on each Texture Set&#039;s size}}&lt;br /&gt;
*{{code|Padding: Dilation + default background color - 8 bit}} &#039;&#039;&#039;OR&#039;&#039;&#039; {{code|Padding: Dilation infinite}}&lt;br /&gt;
{{KBtip|If you encounter color banding (AKA posterization) in your base color texture after export, you may wish to enable dithering.}}&lt;br /&gt;
*To enable dithering, open the {{code|Export Textures}} window, under the {{code|Settings}} tab, enable an override for the Base Color texture and swap the output format from {{code|8 bits}} to {{code|8 bits + dithering}}&lt;br /&gt;
&lt;br /&gt;
Settings Tab - Per-Material settings&lt;br /&gt;
*{{code|Output maps &amp;gt; Normal Map: 8 bits}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt;[[File:Substance_Painter_-_Export_Global_settings.png|thumb|Settings Tab - Global Settings]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt;[[File:Substance_Painter_-_Export_Per-Material_settings.png|thumb|Settings Tab - Per-Material settings]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.adobe.com/products/substance3d-designer.html Adobe Substance 3D Designer] === &lt;br /&gt;
{{KBcaution|Substance Designer &#039;&#039;does not support glTF output&#039;&#039;, unlike Substance Painter.}}&lt;br /&gt;
&lt;br /&gt;
Under preferences, verify the following &#039;&#039;&#039;Project&#039;&#039;&#039; settings:&lt;br /&gt;
*{{code|Color Management: OpenColorIO}}&lt;br /&gt;
*{{code|OpenColorIO Configuration: ACES 1.0.3}}&lt;br /&gt;
*{{code|8 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|16 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|Floating point images: ACES - ACEScg}}&lt;br /&gt;
*{{code|2D and 3D View Display Default: sRGB}}&lt;br /&gt;
&lt;br /&gt;
When exporting (Export Outputs):&lt;br /&gt;
*{{code|basecolor: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|normal: Utility - Raw}}&lt;br /&gt;
*{{code|roughness: Utility - Raw}}&lt;br /&gt;
*{{code|metallic: Utility - Raw}}&lt;br /&gt;
*{{code|ambientocclusion: Utility - Raw}}&lt;br /&gt;
&lt;br /&gt;
If you use a custom node to output a [[PBR_Materials#Occlusion_.5BR.5D_.2F_Roughness_.5BG.5D_.2F_Metalness_.5BB.5D|pre-compiled ORM map]], this should also be set to Raw.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.maxon.net/en/cinema-4d Cinema 4D] === &lt;br /&gt;
Please follow the documentation provided in this [https://www.bakedpixels.nl/blog/export-to-gltf-with-cinema-4d blog post].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===[https://3dcoat.com/ 3DCoat]===&lt;br /&gt;
{{KBwarning|As of version &amp;quot;2022-58&amp;quot;, 3DCoat is unable to produce a spec-compliant glTF file, and thus should not be used for Second Life. &#039;&#039;&#039;Use at your own risk - you &#039;&#039;will&#039;&#039; have issues!}}&lt;br /&gt;
&lt;br /&gt;
glTF export has been implemented since 3DCoat version 2020 and is found in the &amp;lt;code&amp;gt;File &amp;gt;Export &amp;gt; Export to glTF &amp;gt; glTF Separate (gltf + .bin + textures)&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://quixel.com/mixer Quixel Mixer] === &lt;br /&gt;
Quixel Mixer has no glTF output, however, it does generate the functional textures, though they do need to be edited and combined in photo editing software.&lt;br /&gt;
Export them by going to &amp;lt;code&amp;gt;Export Target &amp;gt; Custom&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Texture Preset: Metalness Maps&lt;br /&gt;
&lt;br /&gt;
Select: &lt;br /&gt;
&lt;br /&gt;
Albedo , Roughness, Normal, AO , Metalness  and ( if need be ) Emissive.&lt;br /&gt;
&lt;br /&gt;
Click “Export to Disk” and open the folder that the files were placed into.&lt;br /&gt;
&lt;br /&gt;
Albedo is the Base Color texture in this case.&lt;br /&gt;
&lt;br /&gt;
AO , Roughness , Metalness get combined into the ORM map as per [[PBR_Materials#PBR_Material_Composition|this explanation.]]&lt;br /&gt;
&lt;br /&gt;
Normal Map : Quixel generates Direct3D Normal Maps. The green channel needs to be inverted as [[PBR_Materials#Normal_.5B_RGB_.5D_|per here.]]&lt;br /&gt;
&lt;br /&gt;
Upload to Second Life using [[PBR_Materials#Method_2_:_Edit_a_Blank._Create_PBR_Materials_without_a_GLTF_File|Method 2.]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.autodesk.eu/products/3ds-max/overview Autodesk 3DS Max 2023] === &lt;br /&gt;
Autodesk just added the ability to create glTF files using their new glTF Material and glTF Export functionalities as outlined in &#039;&#039;&#039;[https://help.autodesk.com/view/3DSMAX/2023/ENU/?guid=GUID-EFBB037D-C4EB-42D2-9CE1-30FCAD483C31 Autodesk&#039;s official documentation]&#039;&#039;&#039;.  All prior versions of Autodesk software do not have this functionality.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.materialmaker.org/ Material Maker] ===&lt;br /&gt;
While [https://www.materialmaker.org/ Material Maker] does not currently export directly to glTF ([https://github.com/RodZill4/material-maker/issues/479 This may be added in future]), materials created with this program are compatible with Second Life.&lt;br /&gt;
&lt;br /&gt;
Export your materials using the &#039;&#039;&#039;Godot 4 ORM&#039;&#039;&#039; export preset.&lt;br /&gt;
&lt;br /&gt;
Upload to Second Life using [[PBR_Materials#Method_2_:_Edit_a_Blank._Create_PBR_Materials_without_a_GLTF_File|Method 2.]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== Adobe Photoshop ===&lt;br /&gt;
&#039;&#039;&#039;While Photoshop is not officially a PBR authoring tool&#039;&#039;&#039;, the changes to Alpha Blending (changing into Linear space from sRGB) may require you to change your PS settings to get consistent results. Please see [http://www.kiransprojects.com/blog/2014/photoshop-blending-is-broken/ this article] for more information. (See the section titled &amp;quot;A Partial Solution&amp;quot;). Alternatively, [https://www.reddit.com/r/photoshop/comments/61clf8/how_to_fix_photoshops_incorrect_and_ugly_color/ this Reddit post] also gives a few options on how to achieve blending in the correct manner.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== GIMP ===&lt;br /&gt;
&#039;&#039;&#039;While GIMP is not officially a PBR authoring tool&#039;&#039;&#039;, the changes to Alpha Blending (changing into Linear space from sRGB) in Second Life will make textures created in GIMP display their alphas correctly (GIMP defaults to linear alpha calculation).&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting content ==&lt;br /&gt;
&lt;br /&gt;
If you upload a piece of PBR content which does not match your editor, the advice from Linden Lab is to &#039;&#039;&#039;STOP: File a Feedback ticket. Do not attempt to &amp;quot;fix&amp;quot; the content.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
That said, there are some troubleshooting steps which you can do yourself:&lt;br /&gt;
* Check that you are using the PBR Linden Midday preset (called &amp;quot;Midday&amp;quot;) &#039;&#039;not &amp;quot;Midday (Legacy)&amp;quot;&#039;&#039;. Other environments may not match the reference HDRi, causing some visual differences. (This is intended behaviour, and content should be able to be viewed under any light correctly, but for troubleshooting purposes this may be required).&lt;br /&gt;
** At present, there is a bug with the PBR Linden Midday preset which results in an excessive blue sheen, due to the use of an over-saturated (unrealistic) sky color, among some other issues. This has been fixed in the upcoming glTF Maintenance viewer.&lt;br /&gt;
* Are you using a reflection probe? If not, does the problem reproduce if a manual reflection probe is placed over the object?&lt;br /&gt;
** This is because a common source of the &amp;quot;blue sheen&amp;quot; in interior scenes is the use of an auto-probe. Auto-probes sample their surroundings, and combined with the approximate (and often incorrect) placement this will mean the sky is sampled on all sides, thus meaning the reflected light from the surroundings (which counters the blue light from the sky) is absent, leading to a larger-than-expected level of sky contribution on the object. Auto-probe placement can be worse in skyboxes or sky platforms&lt;br /&gt;
* Triple-check the settings used for your editor match the ones given [[PBR_Materials#Recommended_Application_Settings|here]], including the reference HDRi. &#039;&#039;&#039;Any&#039;&#039;&#039; deviation from these settings may cause visual differences between your editor and in-world.&lt;br /&gt;
* Check your content against a glTF reference viewer; such as:&lt;br /&gt;
** [https://modelviewer.dev/editor/ Modelviewer.dev]&lt;br /&gt;
** [https://github.khronos.org/glTF-Sample-Viewer-Release/ Khronos Sample Viewer]&lt;br /&gt;
&lt;br /&gt;
If the above steps fail, then please &#039;&#039;&#039;[https://feedback.secondlife.com/ file some Feedback.]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A good Feedback ticket will include:&lt;br /&gt;
* An LM to a location where the problem can be examined in-world.&lt;br /&gt;
* A copy of the glTF content, attached to the ticket.&lt;br /&gt;
* Screenshots of the representation in-world, in a reference viewer, and in-editor.&lt;br /&gt;
** You should include screenshots of the object inside a manually placed reflection probe, and outside.&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlLinksetDataFindKeys&amp;diff=1218673</id>
		<title>LlLinksetDataFindKeys</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlLinksetDataFindKeys&amp;diff=1218673"/>
		<updated>2026-03-01T02:10:17Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added speculative limits.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|func=llLinksetDataFindKeys&lt;br /&gt;
|p1_type=string|p1_name=pattern|p1_desc=A regular expression describing which keys to return.&lt;br /&gt;
|p2_type=integer|p2_name=start|p2_desc=The first key to return.&lt;br /&gt;
|p3_type=integer|p3_name=count|p3_desc=The number of keys to return.&lt;br /&gt;
|return_type=list&lt;br /&gt;
|return_text=of the keys in the datastore.&lt;br /&gt;
|func_footnote&lt;br /&gt;
|func_desc=&lt;br /&gt;
The &#039;&#039;&#039;llLinksetDataFindKeys&#039;&#039;&#039; function returns a list of up to {{LSLP|count}} keys from the datastore that match {{LSLP|pattern}}, starting at the one indicated by {{LSLP|start}}. If {{LSLP|count}} is less than 1, then all keys between {{LSLP|start}} and the end which match {{LSLP|pattern}} are returned. If {{LSLP|count}} minus {{LSLP|start}} exceeds the number of matching keys, the returned list will be shorter than {{LSLP|count}}, down to a zero-length list if {{LSLP|start}} equals or exceeds the number of matching keys. The list is ordered alphabetically.&lt;br /&gt;
|func_footnote&lt;br /&gt;
|spec=&lt;br /&gt;
{{LSLP|pattern}} is a [https://en.wikipedia.org/wiki/Regular_expression Regular expression].&lt;br /&gt;
&lt;br /&gt;
|caveats=&lt;br /&gt;
*The maximum length for {{LSLP|pattern}} seems to be 800 characters.&lt;br /&gt;
*There seems to be a timing restriction. If the time limit is exceeded, the function fails silently.&lt;br /&gt;
**Returns an empty list and the script continues execution.&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataAvailable]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataCountKeys]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataDelete]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataDeleteProtected]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataListKeys]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataRead]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataReadProtected]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataReset]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataWrite]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llLinksetDataWriteProtected]]|}}&lt;br /&gt;
|also_events&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_articles&lt;br /&gt;
|examples=&lt;br /&gt;
When {{LSLP|pattern}} matches multiple keys, {{LSLP|start}} can be used to skip over some of the first matches.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        llLinksetDataWrite(&amp;quot;ThingA_config&amp;quot;, &amp;quot;value&amp;quot;);&lt;br /&gt;
        llLinksetDataWrite(&amp;quot;ThingA_data&amp;quot;,   &amp;quot;value&amp;quot;);&lt;br /&gt;
        llLinksetDataWrite(&amp;quot;ThingB_config&amp;quot;, &amp;quot;value&amp;quot;);&lt;br /&gt;
        llLinksetDataWrite(&amp;quot;ThingB_data&amp;quot;,   &amp;quot;value&amp;quot;);&lt;br /&gt;
        llLinksetDataWrite(&amp;quot;ThingC_config&amp;quot;, &amp;quot;value&amp;quot;);&lt;br /&gt;
        llLinksetDataWrite(&amp;quot;ThingC_data&amp;quot;,   &amp;quot;value&amp;quot;);&lt;br /&gt;
        list keys;&lt;br /&gt;
&lt;br /&gt;
        // Return 1 key starting from the first match.&lt;br /&gt;
        keys = llLinksetDataFindKeys(&amp;quot;Thing&amp;quot;, 0, 1); &lt;br /&gt;
        llOwnerSay(llList2CSV(keys)); // ThingA_config&lt;br /&gt;
&lt;br /&gt;
        // Return 1 key after skipping the first 3 matches.&lt;br /&gt;
        keys = llLinksetDataFindKeys(&amp;quot;Thing&amp;quot;, 3, 1); &lt;br /&gt;
        llOwnerSay(llList2CSV(keys)); // ThingB_data&lt;br /&gt;
&lt;br /&gt;
        // Return up to 10 keys after skipping the first match.&lt;br /&gt;
        keys = llLinksetDataFindKeys(&amp;quot;_data&amp;quot;, 1, 10); &lt;br /&gt;
        llOwnerSay(llList2CSV(keys)); // ThingB_data, ThingC_data&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following regular expression code can be used to find UUID keys. (Such as those use to identify user UUID)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
        list keysFound = llLinksetDataFindKeys(&amp;quot;(?i)^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$&amp;quot;,  0, 0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
The following code can also be used to find UUID keys in LinksetData memory.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
        list keysFound = llLinksetDataFindKeys(&amp;quot;^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$&amp;quot;,  0, 0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&#039;&#039;Warning: Keep track of how many UUIDs will be returned, as too many can overwhelm script memory and cause a stack-heap crash.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The script below uses llLinksetDataFindKeys for blacklist management.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
integer gDialogChannel;&lt;br /&gt;
integer gDialogHandle;&lt;br /&gt;
integer gManagingBlocks;&lt;br /&gt;
&lt;br /&gt;
startDialog(key person)&lt;br /&gt;
{&lt;br /&gt;
    gManagingBlocks = 0;&lt;br /&gt;
    gDialogHandle = llListen(gDialogChannel, &amp;quot;&amp;quot;, person, &amp;quot;&amp;quot;);&lt;br /&gt;
    llDialog(person, &amp;quot;\nSelect action&amp;quot;, [&amp;quot;List blocks&amp;quot;, &amp;quot;Add block&amp;quot;, &amp;quot;Remove block&amp;quot;], gDialogChannel);&lt;br /&gt;
    llSetTimerEvent(60);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
stopDialog()&lt;br /&gt;
{&lt;br /&gt;
    llSetTimerEvent(0);&lt;br /&gt;
    llListenRemove(gDialogHandle);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
    on_rez(integer sp)&lt;br /&gt;
    {&lt;br /&gt;
        llResetScript();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        gDialogChannel = (integer)(llFrand(-10000000)-10000000);&lt;br /&gt;
        llListen(PUBLIC_CHANNEL, &amp;quot;&amp;quot;, NULL_KEY, &amp;quot;&amp;quot;);;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    timer()&lt;br /&gt;
    {&lt;br /&gt;
        stopDialog();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    touch_start(integer nd)&lt;br /&gt;
    {&lt;br /&gt;
        key toucherKey = llDetectedKey(0);&lt;br /&gt;
        if (toucherKey == llGetOwner())&lt;br /&gt;
        {&lt;br /&gt;
            startDialog(toucherKey);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
&lt;br /&gt;
        if (llGetAgentSize(id) == ZERO_VECTOR)&lt;br /&gt;
        {&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (channel == gDialogChannel)&lt;br /&gt;
        {&lt;br /&gt;
            stopDialog();&lt;br /&gt;
            if (gManagingBlocks)&lt;br /&gt;
            {&lt;br /&gt;
                message = llStringTrim(message, STRING_TRIM);&lt;br /&gt;
                if ((key)message)&lt;br /&gt;
                {&lt;br /&gt;
                    if (gManagingBlocks == 1)&lt;br /&gt;
                    {&lt;br /&gt;
                        llOwnerSay(&amp;quot;Addition request has been sent to the blacklist storage&amp;quot;);&lt;br /&gt;
                        llLinksetDataWrite(&amp;quot;blocklist:&amp;quot; + message, &amp;quot;1&amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                    else&lt;br /&gt;
                    {&lt;br /&gt;
                        llOwnerSay(&amp;quot;Removal request has been sent to the blacklist storage.&amp;quot;);&lt;br /&gt;
                        llLinksetDataDelete(&amp;quot;blocklist:&amp;quot; + message);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                    llOwnerSay(&amp;quot;The UUID &#039;&amp;quot; + message + &amp;quot;&#039; appears to be invalid.&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
                startDialog(id);&lt;br /&gt;
            }&lt;br /&gt;
            else if (message == &amp;quot;List blocks&amp;quot;)&lt;br /&gt;
            {&lt;br /&gt;
                list blocks = llLinksetDataFindKeys(&amp;quot;^blocklist:&amp;quot;, 0, 0);&lt;br /&gt;
                integer listLength = llGetListLength(blocks);&lt;br /&gt;
                llOwnerSay(&amp;quot;Blacklist items: &amp;quot; + (string)listLength);&lt;br /&gt;
                integer i;&lt;br /&gt;
                while (i &amp;lt; listLength)&lt;br /&gt;
                {&lt;br /&gt;
                    string record = llGetSubString(llList2String(blocks, i), 10, -1);&lt;br /&gt;
                    llOwnerSay(&amp;quot;- secondlife:///app/agent/&amp;quot; + record + &amp;quot;/about&amp;quot; + &amp;quot; - &amp;quot; + record);&lt;br /&gt;
                    ++i;&lt;br /&gt;
                }&lt;br /&gt;
                blocks = [];&lt;br /&gt;
                startDialog(id);&lt;br /&gt;
            }&lt;br /&gt;
            else if (message == &amp;quot;Add block&amp;quot; || message == &amp;quot;Remove block&amp;quot;)&lt;br /&gt;
            {&lt;br /&gt;
                string label = &amp;quot;add to&amp;quot;;&lt;br /&gt;
                gManagingBlocks = 1;&lt;br /&gt;
                if (message == &amp;quot;Remove block&amp;quot;)&lt;br /&gt;
                {&lt;br /&gt;
                    gManagingBlocks = 2;&lt;br /&gt;
                    label = &amp;quot;remove from&amp;quot;;&lt;br /&gt;
                }&lt;br /&gt;
                gDialogHandle = llListen(gDialogChannel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
                llTextBox(id, &amp;quot;\nPlease specify one single avatar UUID you&#039;d like to &amp;quot; + label + &amp;quot; the blacklist storage.&amp;quot;, gDialogChannel);&lt;br /&gt;
                llSetTimerEvent(60);&lt;br /&gt;
            }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (llGetListLength(llLinksetDataFindKeys(&amp;quot;blocklist:&amp;quot; + (string)id, 0, 1)) &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            llRegionSayTo(id, 0, &amp;quot;You&#039;re blacklisted.&amp;quot;);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        llRegionSayTo(id, 0, &amp;quot;Hello there, secondlife:///app/agent/&amp;quot; + (string)id + &amp;quot;/about - your message: &amp;quot; + message);&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    linkset_data(integer action, string name, string value)&lt;br /&gt;
    {&lt;br /&gt;
        if (action == LINKSETDATA_RESET || action == LINKSETDATA_DELETE || action == LINKSETDATA_UPDATE)&lt;br /&gt;
        {&lt;br /&gt;
            llOwnerSay(&amp;quot;Blacklist storage modified.&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
|notes=&lt;br /&gt;
{{LSL Regular Expressions}}&lt;br /&gt;
|cat1=Script&lt;br /&gt;
|cat2=LinksetData&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Lua_Alpha&amp;diff=1218652</id>
		<title>Lua Alpha</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Lua_Alpha&amp;diff=1218652"/>
		<updated>2026-02-26T03:22:19Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Mention for memory limit.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Warning|This functionality is in alpha. Instability is to be expected, and there may be very sharp edges. At this point it is expected that Luau can crash regions and perform other types of undesirable behavior.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🚨 PLEASE NOTE Memory and performance characteristics, and API specifics may change! Scripts are currently being run in unoptimized form for development purposes.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
= Second Life Lua (SLua) Alpha =&lt;br /&gt;
&lt;br /&gt;
[[File:Luau.png|720px|thumb|right|Luau logo]]&lt;br /&gt;
&lt;br /&gt;
We&#039;re thrilled to announce the launch of the SLua Alpha for Second Life! This significant update introduces the [https://lua.org Lua scripting language], offering creators enhanced performance, improved memory efficiency, higher memory limit (128 KB), and a more versatile scripting environment.&lt;br /&gt;
&lt;br /&gt;
To get started, [[Try SLua|read the instructions here.]]&lt;br /&gt;
&lt;br /&gt;
== What is SLua? ==&lt;br /&gt;
&lt;br /&gt;
SLua is scripting for Second Life based on [https://luau.org Luau], a fast, small, safe, and gradually typed embeddable scripting language derived from Lua. It is designed to be backwards compatible with Lua 5.1, incorporating features from future Lua releases and expanding the feature set with type annotations and a state-of-the-art type inference system. Luau is largely implemented from scratch, with the language runtime being a heavily modified version of the Lua 5.1 runtime, featuring a completely rewritten interpreter and other performance innovations.&lt;br /&gt;
&lt;br /&gt;
== Why Lua? ==&lt;br /&gt;
&lt;br /&gt;
The decision to integrate Lua into Second Life was driven by its ability to meet all the requirements for a scripting engine within the platform. Lua offers a high-quality scripting experience to creators, addressing many of the limitations present in the current LSL (Linden Scripting Language) environment. Its lightweight nature and performance optimizations make it an ideal choice for enhancing the scripting capabilities in Second Life. For more information on why Lua was chosen, please see the [[Lua FAQ]].&lt;br /&gt;
&lt;br /&gt;
== How to Get Started with SLua ==&lt;br /&gt;
&lt;br /&gt;
In order to play with SLua, you&#039;ll need to download our Lua project viewer, and either log onto our [https://lindenlab.freshdesk.com/support/solutions/articles/31000156725-accessing-aditi Aditi beta grid] or go to the SLua-enabled beta regions on the main grid.&lt;br /&gt;
&lt;br /&gt;
* Access the latest build of the SLua-enabled Second Life Viewer from [https://releasenotes.secondlife.com/viewer.html here].&lt;br /&gt;
&lt;br /&gt;
SLua-enabled regions on the main grid:&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Landing/128/128/2 SLua Beta Landing]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Porridge/128/128/2 SLua Beta Porridge]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Eraserhead/128/128/2 SLua Beta Eraserhead]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Glass/128/128/2 SLua Beta Glass]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Void/128/128/2 SLua Beta Void]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Anderson/128/128/2 SLua Beta Anderson]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Snausage/128/128/2 SLua Beta Snausage (Adult)]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20Nicolse/128/128/2 SLua Beta Nicolse]&lt;br /&gt;
* [secondlife:///secondlife/SLua%20Beta%20IsNeat/128/128/2 SLua Beta IsNeat]&lt;br /&gt;
&lt;br /&gt;
SLua-enabled regions on the beta grid:&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Yardang/241/235/27 SLua Yardang]&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Tombolo/241/235/27 SLua Tombolo]&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Mesa/241/235/27 SLua Mesa]&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Tideland/241/235/27 SLua Tideland]&lt;br /&gt;
&lt;br /&gt;
When editing a script in the new Lua project viewer, you&#039;ll notice a new &#039;&#039;&#039;Compiler&#039;&#039;&#039; drop-down near the save button. This drop-down will allow you to select which compiler will be used, as well as which script runtime will be used (LSO2, Mono, Luau).&lt;br /&gt;
&lt;br /&gt;
[[File:Compiler_dropdown.png|Compiler selection dropdown]]&lt;br /&gt;
&lt;br /&gt;
Compiler drop-down options:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LSL: Legacy (LSO2)&#039;&#039;&#039; - Scripts written in LSL, to be run on the old LSO2 VM&lt;br /&gt;
* &#039;&#039;&#039;LSL: Mono&#039;&#039;&#039;- Scripts written in LSL, to be run on the Mono VM&lt;br /&gt;
* &#039;&#039;&#039;Lua&#039;&#039;&#039; - Scripts written in Lua, to be run on the SLua VM&lt;br /&gt;
* &#039;&#039;&#039;LSL: 2025 VM&#039;&#039;&#039;- Scripts written in LSL, to be run on the SLua VM&lt;br /&gt;
&lt;br /&gt;
=== Transitioning from LSL to SLua ===&lt;br /&gt;
* &#039;&#039;&#039;Function Namespacing:&#039;&#039;&#039;&lt;br /&gt;
** In SLua, Linden Lab functions have been moved under the &#039;&#039;&#039;ll&#039;&#039;&#039; namespace.&lt;br /&gt;
** For example:&lt;br /&gt;
*** &#039;&#039;llSay&#039;&#039; becomes &#039;&#039;ll.Say&#039;&#039;&lt;br /&gt;
*** &#039;&#039;llGetPos&#039;&#039; becomes &#039;&#039;ll.GetPos&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Lists&#039;&#039;&#039;&lt;br /&gt;
** Lua indexes begin from 1, unlike LSL where indexes begin from 0.&lt;br /&gt;
** Lua uses &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;{}&amp;lt;/syntaxhighlight&amp;gt; for &#039;&#039;tables&#039;&#039;, unlike LSL where &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;[]&amp;lt;/syntaxhighlight&amp;gt; is used for &#039;&#039;lists&#039;&#039;.&lt;br /&gt;
* Types&lt;br /&gt;
** SLua doesn&#039;t support the usual vector/rotation syntax &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;&amp;lt;x, y, z&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** Instead, these values are created with the functions &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;vector(x, y, z)&amp;lt;/syntaxhighlight&amp;gt;, &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;rotation(x, y, z, s)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** Similar functions exist for &#039;&#039;&#039;uuid&#039;&#039;&#039; (key) and &#039;&#039;&#039;integer&#039;&#039;&#039; (distinct from the built-in &#039;&#039;&#039;number&#039;&#039;&#039; type)&lt;br /&gt;
&lt;br /&gt;
=== SLua Libraries ===&lt;br /&gt;
* &#039;&#039;&#039;Coroutines:&#039;&#039;&#039;&lt;br /&gt;
** SLua supports coroutines, allowing for cooperative multitasking within scripts.&lt;br /&gt;
** Key functions include:&lt;br /&gt;
*** &#039;&#039;coroutine.create&#039;&#039;&lt;br /&gt;
*** &#039;&#039;coroutine.status&#039;&#039;&lt;br /&gt;
*** &#039;&#039;coroutine.resume&#039;&#039;&lt;br /&gt;
** Refer to the [https://luau.org/library#coroutine-library coroutine library documentation] for more details.&lt;br /&gt;
* &#039;&#039;&#039;Bitwise Operations:&#039;&#039;&#039;&lt;br /&gt;
** SLua includes a &#039;&#039;bit32&#039;&#039; library for bitwise operations, enabling more efficient data manipulation.&lt;br /&gt;
** Refer to the [https://luau.org/library#bit32-library bit32 library documentation] for more details.&lt;br /&gt;
* &#039;&#039;&#039;JSON to Table Translation:&#039;&#039;&#039;&lt;br /&gt;
** SLua includes a modified [https://github.com/openresty/lua-cjson lua-cjson library] that translates tables into JSON objects and arrays and back.&lt;br /&gt;
** Vectors and quaternions are converted to strings, which may be changed back using the &#039;&#039;tovector&#039;&#039; and &#039;&#039;toquaternion&#039;&#039; functions, respectfully.&lt;br /&gt;
** Buffers are encoded as Base64 strings. They may be decoded using the &#039;&#039;llbase64.decode&#039;&#039; function.&lt;br /&gt;
** Functions:&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;json_text = lljson.encode(some_table)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;some_table = lljson.decode(json_text)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Base64 Encoding:&#039;&#039;&#039;&lt;br /&gt;
** A Base64 library capable of handling SLua strings and binary buffers, unlike classic &#039;&#039;ll&#039;&#039; namespace functions.&lt;br /&gt;
** Leveraged by the JSON functionality, but can also be called separately.&lt;br /&gt;
** Functions:&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;base64string = llbase64.encode(string_or_buffer)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;str = llbase64.decode(base64string)&amp;lt;/syntaxhighlight&amp;gt; for strings&lt;br /&gt;
*** &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot; inline&amp;gt;buf = llbase64.decode(base64string, true)&amp;lt;/syntaxhighlight&amp;gt; for buffers&lt;br /&gt;
* &#039;&#039;&#039;Standard Library:&#039;&#039;&#039;&lt;br /&gt;
** SLua comes equipped with a standard library of functions designed to manipulate built-in data types.&lt;br /&gt;
** Explore the [https://luau.org/library Luau Standard Library] for a comprehensive list of available functions.&lt;br /&gt;
&lt;br /&gt;
== Feedback and Support ==&lt;br /&gt;
&lt;br /&gt;
We encourage all creators to explore the new scripting capabilities and provide feedback. Your insights are invaluable in refining and enhancing this feature. For more information and to share your experiences, please refer to our [[Lua FAQ]].&lt;br /&gt;
&lt;br /&gt;
== Example Scripts ==&lt;br /&gt;
&lt;br /&gt;
To help you get started, we&#039;ve assembled some example scripts that demonstrate the capabilities of SLua. These scripts cover various functionalities and can serve as a foundation for your own creations. Please feel free to propose changes to these scripts, or modify them to your heart&#039;s desire!&lt;br /&gt;
&lt;br /&gt;
=== default_script.lua ===&lt;br /&gt;
This script is roughly equivalent to the default &amp;quot;new script&amp;quot; that gets created for LSL.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function state_entry()&lt;br /&gt;
   ll.Say(0, &amp;quot;Hello, Avatar!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
LLEvents:on(&amp;quot;touch_start&amp;quot;, function(detected: {DetectedEvent})&lt;br /&gt;
   ll.Say(0, &amp;quot;Touched.&amp;quot;)&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
-- Simulate the state_entry event&lt;br /&gt;
state_entry()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dialog.lua ===&lt;br /&gt;
This script demonstrates how one can interact with dialog menus.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Define the menu buttons and dialog message.&lt;br /&gt;
local buttons = {&amp;quot;-&amp;quot;, &amp;quot;Red&amp;quot;, &amp;quot;Green&amp;quot;, &amp;quot;Yellow&amp;quot;}&lt;br /&gt;
local dialogInfo = &amp;quot;\nPlease make a choice.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local ToucherID = nil&lt;br /&gt;
local dialogChannel = nil&lt;br /&gt;
local listenHandle = nil&lt;br /&gt;
local timerHandle = nil&lt;br /&gt;
&lt;br /&gt;
-- This function is called when the script first starts.&lt;br /&gt;
function state_entry()&lt;br /&gt;
    -- Get the object&#039;s key and compute a dialog channel number.&lt;br /&gt;
    local key = ll.GetKey()&lt;br /&gt;
    -- Extract the last 7 characters of the key and convert it from hex.&lt;br /&gt;
    dialogChannel = -1 - tonumber(string.sub(tostring(key), -7, -1), 16)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Called when the object is touched.&lt;br /&gt;
LLEvents:on(&amp;quot;touch_start&amp;quot;, function(detected: {DetectedEvent})&lt;br /&gt;
    ToucherID = detected[1]:getKey()&lt;br /&gt;
    -- If there is already a listen handle, then remove it&lt;br /&gt;
    if listenHandle then&lt;br /&gt;
        ll.ListenRemove(listenHandle)&lt;br /&gt;
    end&lt;br /&gt;
    listenHandle = ll.Listen(dialogChannel, &amp;quot;&amp;quot;, ToucherID, &amp;quot;&amp;quot;)&lt;br /&gt;
    ll.Dialog(ToucherID, dialogInfo, buttons, dialogChannel)&lt;br /&gt;
    -- Set a 60-second timer for response.&lt;br /&gt;
    if timerHandle then&lt;br /&gt;
        LLTimers:off(timerHandle)&lt;br /&gt;
    end&lt;br /&gt;
    timerHandle = LLTimers:once(60,timeoutListen)&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
-- Called when a dialog response is received.&lt;br /&gt;
LLEvents:on(&amp;quot;listen&amp;quot;, function(channel, name, sender_id, message)&lt;br /&gt;
    if message == &amp;quot;-&amp;quot; then&lt;br /&gt;
        -- Redisplay the dialog if the &amp;quot;-&amp;quot; option is selected.&lt;br /&gt;
        ll.Dialog(ToucherID, dialogInfo, buttons, dialogChannel)&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
    -- Stop the timer, and stop the listening handler.&lt;br /&gt;
    ll.ListenRemove(listenHandle)&lt;br /&gt;
    listenHandle = nil&lt;br /&gt;
    if timerHandle then&lt;br /&gt;
        LLTimers:off(timerHandle)&lt;br /&gt;
    end&lt;br /&gt;
    -- Let the user know what they selected&lt;br /&gt;
    ll.Say(0, `You selected {message}`)&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
-- Called when the timer expires.&lt;br /&gt;
function timeoutListen()&lt;br /&gt;
    -- Stop the timer and clean up the listener.&lt;br /&gt;
    if listenHandle then&lt;br /&gt;
        ll.ListenRemove(listenHandle)&lt;br /&gt;
        ll.Whisper(0, &amp;quot;Sorry. You snooze; you lose.&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Invoke state_entry on startup, since simulator doesn&#039;t invoke &lt;br /&gt;
-- it like it does in LSL&lt;br /&gt;
state_entry()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== user_input_coroutine.lua ===&lt;br /&gt;
This script demonstrates [https://www.lua.org/pil/9.html coroutines] and how they can simplify the overarching logic of a script, enabling us to write the bulk of our multi-event code within a centralized function instead of fragmenting across separate event handlers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Wait for user input mid-function before doing something useful with it.&lt;br /&gt;
main = function(toucher)&lt;br /&gt;
    local handle = ll.Listen(0, &amp;quot;&amp;quot;, toucher, &amp;quot;&amp;quot;)&lt;br /&gt;
    local event = touch_start   -- save function for later&lt;br /&gt;
    touch_start = nil           -- disable touch_start&lt;br /&gt;
&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Do you want pants or gloves?&amp;quot;)&lt;br /&gt;
    local clothing = coroutine.yield() -- pause the routine&#039;s execution here&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;For men or women?&amp;quot;)&lt;br /&gt;
    local gender = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Favorite color?&amp;quot;)&lt;br /&gt;
    local color = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Here&#039;s &amp;quot;..color..&amp;quot; &amp;quot;..clothing..&amp;quot; for &amp;quot;..gender)&lt;br /&gt;
&lt;br /&gt;
    ll.ListenRemove(handle)&lt;br /&gt;
    touch_start = event -- restore touch_start&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
LLEvents:on(&amp;quot;touch_start&amp;quot;,function(detected: {DetectedEvent})&lt;br /&gt;
    local toucher = detected[1]:getKey()&lt;br /&gt;
    routine = coroutine.create(main)    -- new coroutine&lt;br /&gt;
    coroutine.resume(routine, toucher)  -- run coroutine (with one argument)&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
-- When the coroutine is suspended, incoming events can be handled&lt;br /&gt;
-- and we can resume() execution of the routine&lt;br /&gt;
-- and pass any number of arguments to be returned by yield()&lt;br /&gt;
LLEvents:on(&amp;quot;listen&amp;quot;, function(channel, name, id, message)&lt;br /&gt;
    coroutine.resume(routine, message)&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== multi_user_input_coroutine.lua ===&lt;br /&gt;
Following from the above example, how can we handle multiple users? This is where coroutines shine.&lt;br /&gt;
&lt;br /&gt;
Instead of disabling touches to prevent others from interacting with the object, we can create new copies of the coroutine each time an avatar touches the object. We can then resume whichever coroutine is needed, based on the avatar, while all of them track their own progress separately and automagically.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Key: avatar uuid; Value: coroutine thread&lt;br /&gt;
routines = {}&lt;br /&gt;
&lt;br /&gt;
main = function(toucher)&lt;br /&gt;
    local handle = ll.Listen(0, &amp;quot;&amp;quot;, toucher, &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Do you want pants or gloves?&amp;quot;)&lt;br /&gt;
    local clothing = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;For men or women?&amp;quot;)&lt;br /&gt;
    local gender = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Favorite color?&amp;quot;)&lt;br /&gt;
    local color = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Here&#039;s &amp;quot;..color..&amp;quot; &amp;quot;..clothing..&amp;quot; for &amp;quot;..gender)&lt;br /&gt;
&lt;br /&gt;
    ll.ListenRemove(handle)&lt;br /&gt;
    routines[toucher] = nil -- Remove from collection&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
LLEvents:on(&amp;quot;touch_start&amp;quot;,function(detected: {DetectedEvent})&lt;br /&gt;
    local toucher = detected[1]:getKey()&lt;br /&gt;
    local routine = routines[toucher]&lt;br /&gt;
    if not routine then -- New user needs new routine&lt;br /&gt;
        routine = coroutine.create(main)&lt;br /&gt;
        routines[toucher] = routine -- Add to collection&lt;br /&gt;
        coroutine.resume(routine, toucher)&lt;br /&gt;
    end&lt;br /&gt;
end)&lt;br /&gt;
&lt;br /&gt;
LLEvents:on(&amp;quot;listen&amp;quot;, function(channel, name, id, message)&lt;br /&gt;
    coroutine.resume(routines[id], message) -- Resume a specific coroutine&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More Examples ===&lt;br /&gt;
&lt;br /&gt;
* Find more example scripts at [[Luau Example Scripts]]&lt;br /&gt;
&lt;br /&gt;
* [https://roblox.github.io/lua-style-guide/gotchas/ Lua Gotchas, Footguns and Other Hazards]&lt;br /&gt;
&lt;br /&gt;
[[Category:Lua]]&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlGetUsedMemory&amp;diff=1218635</id>
		<title>LlGetUsedMemory</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlGetUsedMemory&amp;diff=1218635"/>
		<updated>2026-02-18T02:52:24Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: GC cite.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|func_id=???|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|func=llGetUsedMemory&lt;br /&gt;
|return_type=integer&lt;br /&gt;
|func_desc&lt;br /&gt;
|Return_text=of the number of bytes of memory currently in use by the script.&lt;br /&gt;
|func_footer&lt;br /&gt;
|spec=&lt;br /&gt;
This function&#039;s behavior is dependent upon the VM the script is using. [[#Mono|Mono]] is the new VM, [[#LSO|LSO]] is the old VM. The big difference between Mono and LSO is that Mono scripts run faster and can utilize four times more memory.&lt;br /&gt;
&lt;br /&gt;
===Mono===&lt;br /&gt;
&lt;br /&gt;
In Mono the value returned is the amount of memory currently in use by the script. This is achieved by searching the script&#039;s stack and counting all reachable resources. In terms of garbage collection, memory that hasn&#039;t been GC&#039;d doesn&#039;t count against the script&#039;s memory limit when it&#039;s no longer reachable.{{Footnote|Explanation given by Harold Linden in the official Discord server.}}&lt;br /&gt;
&lt;br /&gt;
The reason why Mono/[[SLua Alpha|SLua]] implements its memory limits this way is because there&#039;s a single VM/GC governing their scripts, and you don&#039;t want to &amp;quot;stop the world&amp;quot; to run the garbage collector just to see if you can get a single script back under its limit.&lt;br /&gt;
&lt;br /&gt;
===LSO===&lt;br /&gt;
&lt;br /&gt;
LSO scripts always use 16KiB of memory.&lt;br /&gt;
&lt;br /&gt;
|caveats=&lt;br /&gt;
*Scripts compiled to LSO always report 16KB memory used.&lt;br /&gt;
*This can be called at any time and does &#039;&#039;&#039;not&#039;&#039;&#039; require [[llScriptProfiler]]&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&lt;br /&gt;
Calling llGetUsedMemory can look like this:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
integer used_memory = llGetUsedMemory();&lt;br /&gt;
llOwnerSay((string)used_memory + &amp;quot; bytes of memory currently used.&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llGetFreeMemory]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llSetMemoryLimit]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llScriptProfiler]]|}}&lt;br /&gt;
|also_events&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes=The amount of used memory is updated only at the start of a server frame, meaning the values from this function can fluctuate. To ensure stable values, it&#039;s possible to wait until the next frame, e.g. [[llSleep]](0.01) before the call to llGetUsedMemory.&lt;br /&gt;
|lso&lt;br /&gt;
|cat1=Script&lt;br /&gt;
|cat2=Memory&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
|history = Date of Release  [[ Release_Notes/Second_Life_Server/11#11.06.17.233176 | 17/06/2011 ]]&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlGetFreeMemory&amp;diff=1218634</id>
		<title>LlGetFreeMemory</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlGetFreeMemory&amp;diff=1218634"/>
		<updated>2026-02-18T02:47:31Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Citation needed, fixed invisible footnote.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|func_id=225|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|func=llGetFreeMemory&lt;br /&gt;
|return_type=integer&lt;br /&gt;
|func_desc&lt;br /&gt;
|Return_text=of the number of free bytes of memory the script can use.&lt;br /&gt;
|func_footer&lt;br /&gt;
|spec=&lt;br /&gt;
This function&#039;s behavior is dependent upon the VM the script is using. [[#Mono|Mono]] is the new VM, [[#LSO|LSO]] is the old VM. The big difference between Mono and LSO is that Mono scripts run faster and can utilize four times more memory.&lt;br /&gt;
&lt;br /&gt;
===Mono===&lt;br /&gt;
&lt;br /&gt;
In Mono the value returned is the amount of free memory available to the script prior to garbage collection being run. This means that memory that is awaiting garbage collection counts against the scripts 64KiB allotment. {{Cn}}&lt;br /&gt;
&lt;br /&gt;
In addition to this, Mono does not enforce the memory restrictions as strictly as the LSO VM did{{Footnote|http://www.langnetsymposium.com/2009/talks/17-JimPurbrick-SecondLife.html}}{{Footnote|In LSO, all types are immutable, every operation results in Heap values being duplicated}}. (With LSO it was &amp;lt;u&amp;gt;impossible&amp;lt;/u&amp;gt; to exceed the 16KiB memory cap.)&lt;br /&gt;
&lt;br /&gt;
===LSO===&lt;br /&gt;
&lt;br /&gt;
In LSO, the value returned by this function is the amount of memory that the Stack can use of the Heap has yet to allocate for itself.&lt;br /&gt;
&lt;br /&gt;
The LSL memory space is divided into four sections: Byte-code, Stack, Free Memory, Heap. Free Memory isn&#039;t an allocated block of memory, it&#039;s just the space between Stack and Heap. The size of all four sections combined is 16384 bytes (16KiB).&lt;br /&gt;
&lt;br /&gt;
[[String]]s, [[list]]s and [[key]]s are stored in the Heap. Heap pointers (for [[string]]s, [[list]]s &amp;amp; [[key]]s), [[integer]]s, [[float]]s, [[vector]]s and [[rotation]]s are all temporarily stored in the stack as the script executes.&lt;br /&gt;
&lt;br /&gt;
As the script executes the Stack grows and shrinks in size depending upon the complexity of the expressions being executed. Likewise the Heap grows as the script executes but unlike the Stack, it never shrinks in size. When there is no free memory left for Stack or Heap to use they collide and a Stack-Heap Collision error is thrown causing the script to crash.&lt;br /&gt;
&lt;br /&gt;
The Heap can become fragmented and blocks of memory in it can become unusable (due to their size). There is no defragment function{{Footnote|Due to the design of the LSO VM, a defragment function is impossible.}} but there are scripting techniques that can be used to reduce fragmentation.&lt;br /&gt;
|caveats=&lt;br /&gt;
*The number of free bytes the Heap can use may be greater but not less.&lt;br /&gt;
*&amp;quot;&#039;&#039;the number of free bytes of memory the script can use&#039;&#039;&amp;quot; means that if a memory limit is set (via [[llSetMemoryLimit]]), this function will report the free space between the script&#039;s current memory usage and the defined memory limit, &#039;&#039;&#039;not&#039;&#039;&#039; the uncapped memory limit.&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&lt;br /&gt;
Calling [[llGetFreeMemory]] can look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
llOwnerSay((string)llGetFreeMemory() + &amp;quot; bytes of free memory available for allocation.&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llGetUsedMemory]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llScriptProfiler]]|A more advanced function for analyzing script performance, and can also measure memory usage over time.}}&lt;br /&gt;
{{LSL DefineRow||[[llSetMemoryLimit]]|Define a memory limit (lower than the maximum for the appropriate VM).}}&lt;br /&gt;
|also_events&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_articles=&lt;br /&gt;
{{LSL DefineRow||[[LSL Errors#Script_run-time_error:_Stack-Heap_Collision|Stack-Heap Collision]]|[[LSL Errors|LSL Error]] caused by the script running out of memory.}}&lt;br /&gt;
|notes=The amount of free memory is updated only at the start of a server frame, meaning the values from this function can fluctuate. To ensure stable values, use a single-frame sleep, e.g. [[llSleep]](0.01) before the call to llGetFreeMemory.&lt;br /&gt;
|lso=This function does not count free memory, and the name of this function makes this function difficult for people to learn. People say this function may be redefined or superseded by another more useful function when the LSL VM moves to [[Mono]].&lt;br /&gt;
&lt;br /&gt;
We can concisely specify [[llGetFreeMemory]] in the context of the classic Unix model for a parallel task/ thread/ process. Think of the task of the script always holding 16384 bytes (16 KiB). Let the byte code and then stack grow up from the bottom, let the heap grow down from the top. [[llGetFreeMemory]] then returns the &amp;quot;historic lowest heap pointer minus the stack end pointer&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
llGetFreeMemory does not count the bytes freed, [[llGetFreeMemory]] instead counts all the bytes never yet used.&lt;br /&gt;
|cat1=Script&lt;br /&gt;
|cat2=Memory&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlGetUsedMemory&amp;diff=1218633</id>
		<title>LlGetUsedMemory</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlGetUsedMemory&amp;diff=1218633"/>
		<updated>2026-02-18T02:29:41Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|func_id=???|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|func=llGetUsedMemory&lt;br /&gt;
|return_type=integer&lt;br /&gt;
|func_desc&lt;br /&gt;
|Return_text=of the number of bytes of memory currently in use by the script.&lt;br /&gt;
|func_footer&lt;br /&gt;
|spec=&lt;br /&gt;
This function&#039;s behavior is dependent upon the VM the script is using. [[#Mono|Mono]] is the new VM, [[#LSO|LSO]] is the old VM. The big difference between between Mono and LSO is that Mono scripts run faster and can utilize four times more memory.&lt;br /&gt;
&lt;br /&gt;
===Mono===&lt;br /&gt;
&lt;br /&gt;
In Mono the value returned is the amount of memory currently in use by the script. This is achieved by searching the script&#039;s stack and counting all reachable resources. In terms of garbage collection, memory that hasn&#039;t been GC&#039;d doesn&#039;t count against the script&#039;s memory limit when it&#039;s no longer reachable.&lt;br /&gt;
&lt;br /&gt;
The reason why Mono/[[SLua Alpha|SLua]] implements its memory limits this way is because there&#039;s a single VM/GC governing their scripts, and you don&#039;t want to &amp;quot;stop the world&amp;quot; to run the garbage collector just to see if you can get a single script back under its limit.&lt;br /&gt;
&lt;br /&gt;
===LSO===&lt;br /&gt;
&lt;br /&gt;
LSO scripts always use 16KiB of memory.&lt;br /&gt;
&lt;br /&gt;
|caveats=&lt;br /&gt;
*Scripts compiled to LSO always report 16KB memory used.&lt;br /&gt;
*This can be called at any time and does &#039;&#039;&#039;not&#039;&#039;&#039; require [[llScriptProfiler]]&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&lt;br /&gt;
Calling llGetUsedMemory can look like this:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
integer used_memory = llGetUsedMemory();&lt;br /&gt;
llOwnerSay((string)used_memory + &amp;quot; bytes of memory currently used.&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llGetFreeMemory]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llSetMemoryLimit]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llScriptProfiler]]|}}&lt;br /&gt;
|also_events&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes=The amount of used memory is updated only at the start of a server frame, meaning the values from this function can fluctuate. To ensure stable values, it&#039;s possible to wait until the next frame, e.g. [[llSleep]](0.01) before the call to llGetUsedMemory.&lt;br /&gt;
|lso&lt;br /&gt;
|cat1=Script&lt;br /&gt;
|cat2=Memory&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
|history = Date of Release  [[ Release_Notes/Second_Life_Server/11#11.06.17.233176 | 17/06/2011 ]]&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlGetUsedMemory&amp;diff=1218632</id>
		<title>LlGetUsedMemory</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlGetUsedMemory&amp;diff=1218632"/>
		<updated>2026-02-18T02:20:06Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: GC note.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|func_id=???|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|func=llGetUsedMemory&lt;br /&gt;
|return_type=integer&lt;br /&gt;
|func_desc&lt;br /&gt;
|Return_text=of the number of bytes of memory currently in use by the script.&lt;br /&gt;
|func_footer&lt;br /&gt;
|spec=&lt;br /&gt;
This function&#039;s behavior is dependent upon the VM the script is using. [[#Mono|Mono]] is the new VM, [[#LSO|LSO]] is the old VM. The big difference between between Mono and LSO is that Mono scripts run faster and can utilize four times more memory.&lt;br /&gt;
&lt;br /&gt;
===Mono===&lt;br /&gt;
&lt;br /&gt;
In Mono the value returned is the amount of memory currently in use by the script. This is achieved by searching the script&#039;s stack and counting all reachable resources. In terms of garbage collection, memory that hasn&#039;t been GC&#039;d doesn&#039;t count against the script&#039;s memory limit when it&#039;s no longer reachable.&lt;br /&gt;
&lt;br /&gt;
===LSO===&lt;br /&gt;
&lt;br /&gt;
LSO scripts always use 16KiB of memory.&lt;br /&gt;
&lt;br /&gt;
|caveats=&lt;br /&gt;
*Scripts compiled to LSO always report 16KB memory used.&lt;br /&gt;
*This can be called at any time and does &#039;&#039;&#039;not&#039;&#039;&#039; require [[llScriptProfiler]]&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&lt;br /&gt;
Calling llGetUsedMemory can look like this:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
integer used_memory = llGetUsedMemory();&lt;br /&gt;
llOwnerSay((string)used_memory + &amp;quot; bytes of memory currently used.&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llGetFreeMemory]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llSetMemoryLimit]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llScriptProfiler]]|}}&lt;br /&gt;
|also_events&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes=The amount of used memory is updated only at the start of a server frame, meaning the values from this function can fluctuate. To ensure stable values, it&#039;s possible to wait until the next frame, e.g. [[llSleep]](0.01) before the call to llGetUsedMemory.&lt;br /&gt;
|lso&lt;br /&gt;
|cat1=Script&lt;br /&gt;
|cat2=Memory&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
|history = Date of Release  [[ Release_Notes/Second_Life_Server/11#11.06.17.233176 | 17/06/2011 ]]&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Server_architecture&amp;diff=1218597</id>
		<title>Server architecture</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Server_architecture&amp;diff=1218597"/>
		<updated>2026-01-27T21:23:39Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: /* Simulator */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OSWikiLearnBox}}&lt;br /&gt;
&lt;br /&gt;
This wiki is mainly focused on the viewer, since that is the portion of the system that is open source.  However, because the viewer interacts with the Second Life servers in somewhat complicated ways, it is helpful to have an understanding of what components do what on the server side.&lt;br /&gt;
&lt;br /&gt;
Much of what you&#039;ll need to understand is actually covered in the [[protocol|protocol documentation]].  In particular, take a look at [[Authentication Flow]], which outlines the process by which the viewer establishes connections with a number of components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;(obsolete)&#039;&#039; if you want to see what the architecture may look like in the future, or if you would like to help shape it, visit the [[Architecture Working Group]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Components ==&lt;br /&gt;
=== Login server ===&lt;br /&gt;
CGI script running on login.agni.lindenlab.com.  Handles verification of user name and password.  Determines the login region based on home vs. last location vs. URL specified location.  Finds the simulator process running that region and verifies user is allowed to connect there.  Alerts the simulator to expect a connection.  Informs the viewer of where to connect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Spaceserver ===&lt;br /&gt;
Handles routing of messages based on grid x,y locations. Simulator talks to Space Server to *register* and also find who the neighbors are.  &lt;br /&gt;
&lt;br /&gt;
=== Dataserver ===&lt;br /&gt;
Handles connections to the central database, log database, inventory database(s) and search database(s).  Performs queries on behalf of the simulator process.&lt;br /&gt;
&lt;br /&gt;
=== Simulator ===&lt;br /&gt;
The primary SL server process. Each simulator process simulates one 256x256 meter region.&lt;br /&gt;
&lt;br /&gt;
There are about 26,000 regions in total in 2026. Each one is hosted on an AWS EC2 instance within an AWS ECS container.&lt;br /&gt;
&lt;br /&gt;
It handles storing object state, land parcel state, and terrain height-map state. It performs visibility calculations on objects and land and transmits the data to the viewer. It transmits texture data in a prioritized queue. Physics simulation is handled with the Havok physics library. Chat and instant messages are processed here. The viewer is handed off from one simulator to another as it moves between regions.&lt;br /&gt;
&lt;br /&gt;
Running at full tilt, a simulator will run at 45 frames/sec.  If it can&#039;t keep up, it will attempt time dilation without reducing frame rate.  More details on work performed during a frame are in [[Simulator Main Loop]].&lt;br /&gt;
&lt;br /&gt;
Simulators communicate with one another using a [[circuit]] via UDP.  A &amp;quot;circuit&amp;quot; is a UDP network connection.  Circuits are maintained between adjacent simulators.&lt;br /&gt;
&lt;br /&gt;
==== Simulator vs. Viewer ====&lt;br /&gt;
&lt;br /&gt;
It helps to understand the division of labor between the simulator and the viewer, since the split in Second Life is very different than most other virtual environments:&lt;br /&gt;
&lt;br /&gt;
*  Simulator&#039;s job:&lt;br /&gt;
**  Runs physics engine&lt;br /&gt;
**  Collision detection&lt;br /&gt;
**  keeps track of where everything is&lt;br /&gt;
**  Sends locations of stuff to viewer&lt;br /&gt;
**  Sends updates to viewers only when needed (only when collision occurs or other changes in direction, velocity etc.)&lt;br /&gt;
*  Viewer&#039;s job:&lt;br /&gt;
**  Handle locations of objects&lt;br /&gt;
**  Gets velocities and other physics information, and does simple physics to keep track of what is moving where&lt;br /&gt;
**  No collision detection&lt;br /&gt;
&lt;br /&gt;
=== Other servers ===&lt;br /&gt;
&lt;br /&gt;
* Central Backbone &lt;br /&gt;
* Agent Database&lt;br /&gt;
** Agent DB keeps track of metadata&amp;lt;-&amp;gt;item id (UUID) mapping&lt;br /&gt;
* Central DB (CDB)&lt;br /&gt;
** a list of who owns what, used for billing, etc&lt;br /&gt;
* Find DB (replica of Central DB used for search)&lt;br /&gt;
* Map server - renders overall map with OpenGL&lt;br /&gt;
* RPC server&lt;br /&gt;
** API for developers to manupulate Second Life without using the viewer&lt;br /&gt;
** Translates XMLRPC server into in-world requests&lt;br /&gt;
** Communicates with space server and CDB&lt;br /&gt;
* Region Conductor&lt;br /&gt;
** Looks for regions that should be running, but are down&lt;br /&gt;
** Start those regions on a sim host that has room for it&lt;br /&gt;
** Grid Restart Modes&lt;br /&gt;
*** Startup Mode: Regions are stuck on the current sim host&lt;br /&gt;
*** Normal Mode: Regions are sent to a random sim host&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlSensor&amp;diff=1218577</id>
		<title>LlSensor</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlSensor&amp;diff=1218577"/>
		<updated>2026-01-04T22:46:56Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added SCRIPTED caveat.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2={{LSL_Function/uuid|id|object=*|sim=*}}{{LSL Constants/Sensing|type}}&lt;br /&gt;
|func_id=28|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|sort=Sensor|func=llSensor&lt;br /&gt;
|func_desc=&lt;br /&gt;
Performs a single scan for {{LSLP|name}} and {{LSLP|id}} with {{LSLP|type}} within {{LSLP|radius}} meters and {{LSLP|arc}} radians of forward vector.&lt;br /&gt;
&lt;br /&gt;
Script execution continues immediately. When the scan is completed, a [[sensor]] or [[no_sensor]] event is put in the event queue.&lt;br /&gt;
|p1_type=string|p1_name=name|p1_hover=object or avatar name|p1_desc=object or avatar name[[#NameFormat|&amp;lt;span style=&amp;quot;color:red;&amp;quot;&amp;gt;!&amp;lt;/span&amp;gt;]]&lt;br /&gt;
|p2_type=key|p2_name=id|p2_desc=&lt;br /&gt;
|p3_type=integer|p3_name=type&lt;br /&gt;
|p3_desc=[[:Category:LSL_Integer/bit_field|bit field]] ({{#var:AGENT}}, {{#var:AGENT_BY_LEGACY_NAME}}, {{#var:AGENT_BY_USERNAME}}, {{#var:ACTIVE}}, {{#var:PASSIVE}}, and/or {{#var:SCRIPTED}})&lt;br /&gt;
|p3_hover=bit field (AGENT, AGENT_BY_LEGACY_NAME, AGENT_BY_USERNAME, ACTIVE, PASSIVE, and/or SCRIPTED)&lt;br /&gt;
|p4_type=float|p4_name=radius&lt;br /&gt;
|p4_desc=distance in meters from center, {{Interval|gte=0.0|center=radius|lte=96.0}}&lt;br /&gt;
|p4_hover=distance in meters from center, {{Interval/Hover|gte=0.0|center=radius|lte=96.0}}&lt;br /&gt;
|p5_type=float|p5_name=arc&lt;br /&gt;
|p5_desc=the max angle between the object&#039;s local X-axis and detectable objects, {{Interval|gte=0.0|lteh=PI|lte={{#var:PI}}|center=arc}}&lt;br /&gt;
|p5_hover=the max angle between the object&#039;s local X-axis and detectable objects, {{Interval/Hover|gte=0.0|lteh=PI|lte={{#var:PI}}|center=arc}}&lt;br /&gt;
|func_footnote=If {{LSLP|name}} and/or {{LSLP|id}} are empty, they are ignored.&amp;lt;br/&amp;gt;If {{LSLP|id}} is an invalid key or [[NULL_KEY]] it is treated as empty.&amp;lt;br id=&amp;quot;NameFormat&amp;quot;/&amp;gt;Depending upon which AGENT* flag is used determines the format requirements for {{LSLP|name}}&lt;br /&gt;
&lt;br /&gt;
See: [https://web.archive.org/web/20150426032518/http://www.lslwiki.net/lslwiki/wakka.php?wakka=llSensor llSensor] for an excellent explanation of {{LSLP|arc}}.&lt;br /&gt;
|return_text&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=&lt;br /&gt;
*AGENT, PASSIVE and ACTIVE behave inclusively. SCRIPTED is &#039;&#039;&#039;not&#039;&#039;&#039; inclusive and will exclude non-scripted targets (like avatars) from the detected set.&lt;br /&gt;
*Objects do not detect themselves, and attachments cannot detect their wearers (this includes HUD attachments).&lt;br /&gt;
*Attachments cannot be detected by llSensor.&lt;br /&gt;
*For an object to be detected, the center of its root prim (the same point it would report with [[llGetRootPosition]]) must be within the sensor beam.&lt;br /&gt;
*For an agent to be detected, a point near the pelvis must be inside the sensor beam (the same as llGetRootPosition would report in a script attached to that avatar). This point is indicated by red crosshairs when Advanced&amp;gt;Character&amp;gt;Display Agent Target is turned on.&lt;br /&gt;
**If the agent is sitting on an object, the root prim of the sat upon object becomes a second sensor target for the agent (but not if the avatar is outside the sensor arc, see {{Jira|SVC-5145}}). &lt;br /&gt;
*Sensors placed in the root prim of attachments will use the direction the avatar is facing as their forward vector. In mouselook, this means that it will be wherever the avatar is looking, while out of mouselook, this means whichever way the avatar is pointing. This does not include where the avatar&#039;s head is pointing, or what animation the avatar is doing, just the direction the avatar would move in if you walked forward. This is the case, regardless of where the object is attached.&lt;br /&gt;
*Sensors placed in prims other than the root prim of an attachment will have their forward direction offset relative to the root prim&#039;s forward direction, e.g. a sensor in a prim whose +X direction is the reverse of the root +X will look backward.&lt;br /&gt;
*llSensor does not detect objects or agents across region boundaries&lt;br /&gt;
*If {{LSLP|type}} is zero, the sensor will silently fail, neither [[sensor]] or [[no_sensor]] will be triggered.&lt;br /&gt;
*Only 32 objects will be scanned each time. (Increased from 16 with Release 2024-03-18.8333615376 on Tuesday, March 19, 2024)&lt;br /&gt;
*DAMAGEABLE is a filter flag; it cannot be used on its own. It must be combined with at least one other flag. For example, AGENT &amp;amp;vert; ACTIVE &amp;amp;vert; PASSIVE &amp;amp;vert; DAMAGEABLE will return all damageable agents and objects, whether physical or not.&lt;br /&gt;
|examples=&lt;br /&gt;
This sensor scans a 45 degree cone about the x-axis ([[PI]]/2 or [[PI_BY_TWO]] scans a hemisphere; [[PI]] is a spherical scan). Also it will only match an agent with the {{LSLGC|Avatar/Name|legacy name}} &amp;quot;Governor Linden&amp;quot;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;llSensor( &amp;quot;Governor Linden&amp;quot;, NULL_KEY, AGENT_BY_LEGACY_NAME, 96.0, PI/4 );&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This sensor detects all prims and agents with a given name within 15m of the sensor.  (AGENT, PASSIVE and ACTIVE behave inclusively.  SCRIPTED is not inclusive and will exclude non-scripted targets (like avatars) from the detected set.)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;llSensor( &amp;quot;&amp;quot;, NULL_KEY, ( AGENT | PASSIVE | ACTIVE ), 15.0, PI );&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Basic example script that when touched executes a sensor scan of agents. When the sensor event is processed it spits out messages to the owner of each detected agents&#039; name.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer total_number)&lt;br /&gt;
    {&lt;br /&gt;
        llSensor(&amp;quot;&amp;quot;, NULL_KEY, AGENT, 30.0, PI);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    sensor( integer detected )&lt;br /&gt;
    {&lt;br /&gt;
        while(detected--)&lt;br /&gt;
        {&lt;br /&gt;
            llOwnerSay(llDetectedName(detected));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llSensorRepeat]]| Runs a sensor on a timer}}&lt;br /&gt;
{{LSL DefineRow||[[llSensorRemove]]| Stops the llSensorRepeat timer}}&lt;br /&gt;
|also_events=&lt;br /&gt;
{{LSL DefineRow||[[sensor]]|Triggered when a sensor detects something}}&lt;br /&gt;
{{LSL DefineRow||[[no_sensor]]|Triggered when a sensor detects nothing}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes=&lt;br /&gt;
{{LSL Tip|You might want to use [[llGetAgentList]] instead of using sensors to get a list of all avatars within the same parcel or region.}}&lt;br /&gt;
====Loops &amp;amp; Repetition====&lt;br /&gt;
Using [[llSensor]] in a [[for]] loop is a beginners mistake, as events will not interrupt each other (the sensor event will not interrupt whatever event is currently being executed). To perform repeat sensor sweeps, [[llSensorRepeat]] is the better solution. While it is possible to call llSensor from a [[timer]] event, it is less efficient to do so; there is a limit to the number of events that can be processed in a second and using the timer just to call llSensor will result in your script getting less timeslice.&lt;br /&gt;
|permission&lt;br /&gt;
|negative_index&lt;br /&gt;
|cat1=Sensor&lt;br /&gt;
|cat2=Detected&lt;br /&gt;
|cat3=Username/As A Parameter&lt;br /&gt;
|cat4=Legacy Name/As A Parameter&lt;br /&gt;
|haiku={{Haiku|The hounds are straining.|Elusive quarry sighted.|The game&#039;s afoot!}}&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;div id=&amp;quot;box&amp;quot;&amp;gt;&lt;br /&gt;
==Gallery==&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:sensor_pi_by_four.png|arc = PI / 4&lt;br /&gt;
File:sensor_pi_by_two.png|arc = PI / 2&lt;br /&gt;
File:sensor_pi.png|arc = PI&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Internal_Textures&amp;diff=1218576</id>
		<title>Internal Textures</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Internal_Textures&amp;diff=1218576"/>
		<updated>2026-01-04T22:01:46Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added blank texture variation.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__ {{Multi-lang|category=LSL}}{{LSL Header}}&lt;br /&gt;
&lt;br /&gt;
Various Textures built into the viewer&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; {{prettytable}}&lt;br /&gt;
|-{{Hl2}}&lt;br /&gt;
!Texture Name&lt;br /&gt;
![[UUID]]&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|[[TEXTURE_DEFAULT]]&lt;br /&gt;
|89556747-24cb-43ed-920b-47caed15465f&lt;br /&gt;
|Default plywood texture&lt;br /&gt;
|-&lt;br /&gt;
|[[TEXTURE_PLYWOOD]]&lt;br /&gt;
|89556747-24cb-43ed-920b-47caed15465f&lt;br /&gt;
|Default plywood texture&lt;br /&gt;
|-&lt;br /&gt;
|[[TEXTURE_BLANK]]&lt;br /&gt;
|5748decc-f629-461c-9a36-a35a221fe21f&lt;br /&gt;
|Blank, all white texture in the Texture window&lt;br /&gt;
|-&lt;br /&gt;
|Blank *&lt;br /&gt;
|5b53359e-59dd-d8a2-04c3-9e65134da47a&lt;br /&gt;
|Neutral normal map, RGB 127,127,255 when the &amp;quot;Blank&amp;quot; button is clicked on the &amp;quot;Normal&amp;quot; texture slot.&lt;br /&gt;
|-&lt;br /&gt;
|[[TEXTURE_TRANSPARENT]]&lt;br /&gt;
|8dcd4a48-2d37-4909-9f78-f7a9eb4ef903&lt;br /&gt;
|*Default Transparent Texture&lt;br /&gt;
|-&lt;br /&gt;
|[[TEXTURE_MEDIA]]&lt;br /&gt;
|8b5fec65-8d8d-9dc5-cda8-8fdf2716e361&lt;br /&gt;
|Also triggers targeting&lt;br /&gt;
|-&lt;br /&gt;
|[[Invisiprim]] 1&lt;br /&gt;
|38b86f85-2575-52a9-a531-23108d8da837&lt;br /&gt;
|Hides alpha textures behind it&lt;br /&gt;
|-&lt;br /&gt;
|[[Invisiprim]] 2&lt;br /&gt;
|e97cf410-8e61-7005-ec06-629eba4cd1fb&lt;br /&gt;
|Hides alpha textures behind it&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_HEAD&lt;br /&gt;
|5a9f4a74-30f2-821c-b88d-70499d3e7183&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_UPPER&lt;br /&gt;
|ae2de45c-d252-50b8-5c6e-19f39ce79317&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_LOWER&lt;br /&gt;
|24daea5f-0539-cfcf-047f-fbc40b2786ba&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_EYES&lt;br /&gt;
|52cc6bb6-2ee5-e632-d3ad-50197b1dcb8a&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_SKIRT&lt;br /&gt;
|43529ce8-7faa-ad92-165a-bc4078371687&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_HAIR&lt;br /&gt;
|09aac1fb-6bce-0bee-7d44-caac6dbb6c63&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_LEFTARM&lt;br /&gt;
|ff62763f-d60a-9855-890b-0c96f8f8cd98&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_LEFTLEG&lt;br /&gt;
|8e915e25-31d1-cc95-ae08-d58a47488251&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_AUX1&lt;br /&gt;
|9742065b-19b5-297c-858a-29711d539043&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_AUX2&lt;br /&gt;
|03642e83-2bd1-4eb9-34b4-4c47ed586d2d&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|-&lt;br /&gt;
|IMG_USE_BAKED_AUX3&lt;br /&gt;
|edd51b77-fc10-ce7a-4b3d-011dfc349e4f&lt;br /&gt;
|Sets face to use specified channel for [https://community.secondlife.com/knowledgebase/english/bakes-on-mesh-r1512/ Bakes on Mesh]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Related Articles ==&lt;br /&gt;
&lt;br /&gt;
* [[User:Kizmut_Smit/ClientAssetKeys]]&lt;br /&gt;
* [[llSetTexture]]&lt;br /&gt;
&lt;br /&gt;
[[Category:LSL Texture]]&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlParseString2List&amp;diff=1218567</id>
		<title>LlParseString2List</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlParseString2List&amp;diff=1218567"/>
		<updated>2025-12-29T20:25:43Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added notes about vectors and rotations.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|func_id=214|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|func=llParseString2List|return_type=list&lt;br /&gt;
|p1_type=string|p1_name=src|p1_desc=source string&lt;br /&gt;
|p2_type=list|p2_name=separators|p2_desc=separators to be discarded&lt;br /&gt;
|p3_type=list|p3_name=spacers|p3_desc=spacers to be kept&lt;br /&gt;
|func_footnote=In most situations [[llParseStringKeepNulls]] should be used instead. Discarding null values is rarely desired.&lt;br /&gt;
&lt;br /&gt;
When dealing with [[vector]] and [[rotation]] data, consider using [[llCSV2List]] instead, since it can correctly parse them.&lt;br /&gt;
|func_desc&lt;br /&gt;
|return_text=that is {{LSLP|src}} broken into a list of strings, discarding {{LSLP|separators}}, keeping {{LSLP|spacers}}, discards any null (empty string) values generated.&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=&lt;br /&gt;
*All empty strings (that would arise from a spacer or separator being adjacent to each other or the ends) are removed;&lt;br /&gt;
**If you want them (to keep the order of a list, for example) use [[llParseStringKeepNulls]] instead;&lt;br /&gt;
*Only the first 8 separators and first 8 spacers supplied will be used. Any beyond that will be ignored. To work around this see [[#Useful Snippets]] section below. The only limit on the number of items in the output is available script memory.&lt;br /&gt;
* All separators and spacers must be strings. All other types will be ignored;&lt;br /&gt;
* Separators take precedent over spacers. The string is parsed from start to finish. Each position is compared against the separators then spacers before moving onto the next position;&lt;br /&gt;
* Duplicate separators and spacers have no ill effects;&lt;br /&gt;
* All elements in the list returned by llParseString2List are strings, and must be explicitly typecast if they are to be used as other types. Do not rely upon the implicit typecasting of the other llList2* functions (as they typically return a default value);&lt;br /&gt;
* Remember to capture the result of the operation with a variable, unless you are planning to act directly on the results.&lt;br /&gt;
|examples=&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        // This will say:&lt;br /&gt;
        // &amp;lt;A&amp;gt;&amp;lt;crazy&amp;gt;&amp;lt;fox&amp;gt;&amp;lt;.&amp;gt;&amp;lt;Saw&amp;gt;&amp;lt;the&amp;gt;&amp;lt;moon&amp;gt;&amp;lt;.&amp;gt;&amp;lt;.&amp;gt;&lt;br /&gt;
        string my_string = &amp;quot;A crazy fox.  Saw the moon..&amp;quot;;&lt;br /&gt;
        list my_list = llParseString2List(my_string,[&amp;quot; &amp;quot;],[&amp;quot;.&amp;quot;]);&lt;br /&gt;
        llOwnerSay(&amp;quot;&amp;lt;&amp;quot; + llDumpList2String(my_list,&amp;quot;&amp;gt;&amp;lt;&amp;quot;) + &amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
        // This will say:&lt;br /&gt;
        //  &amp;lt;A&amp;gt;&amp;lt;crazy&amp;gt;&amp;lt;fox&amp;gt;&amp;lt;.&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;Saw&amp;gt;&amp;lt;the&amp;gt;&amp;lt;moon&amp;gt;&amp;lt;.&amp;gt;&amp;lt;&amp;gt;&amp;lt;.&amp;gt;&amp;lt;&amp;gt;&lt;br /&gt;
        my_list = llParseStringKeepNulls(my_string,[&amp;quot; &amp;quot;],[&amp;quot;.&amp;quot;]);&lt;br /&gt;
        llOwnerSay(&amp;quot;&amp;lt;&amp;quot; + llDumpList2String(my_list,&amp;quot;&amp;gt;&amp;lt;&amp;quot;) + &amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While LSL does not support lists-in-lists, you can emulate lists-in-lists by making successive calls or you could use {{LSLGC|JSON}}.&lt;br /&gt;
&lt;br /&gt;
In this example some items have additional information supplied.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;   &lt;br /&gt;
string shoppinglist = &amp;quot;macaroni::pepperoni::bread#wheat::sausage#italian::coffee::syrup::apple::ice cream#strawberry#chocolate#vanilla&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        list items = llParseString2List(shoppinglist, [&amp;quot;::&amp;quot;], []);&lt;br /&gt;
        integer i = 0;&lt;br /&gt;
        integer j = llGetListLength(items);&lt;br /&gt;
        for(;i &amp;lt; j; ++i)&lt;br /&gt;
        {&lt;br /&gt;
            list desc = llParseString2List(llList2String(items, i), [&amp;quot;#&amp;quot;], []);&lt;br /&gt;
            if(llGetListLength(desc) &amp;gt; 1)&lt;br /&gt;
            {&lt;br /&gt;
                list types = llDeleteSubList(desc,0,0);&lt;br /&gt;
                llOwnerSay(&amp;quot;Item: &amp;quot;+ llList2String(desc, 0) + &amp;quot;  Type: &amp;quot; + llList2CSV(llDeleteSubList(types,-2,-1) + llDumpList2String(llList2List(types,-2,-1), &amp;quot; &amp;amp; &amp;quot;)));&lt;br /&gt;
            } else {&lt;br /&gt;
                llOwnerSay(&amp;quot;Item: &amp;quot;+ (string)desc);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers=&lt;br /&gt;
&#039;&#039;&#039;Examples of processing more than 8 spacers or separators:&#039;&#039;&#039;&lt;br /&gt;
{{{!}}&lt;br /&gt;
{{LSL DefineRow||[[ParseString2List]]|Functions exactly the same as [[llParseString2List]] and [[llParseStringKeepNulls]].}}&lt;br /&gt;
{{LSL DefineRow||[[Separate Words|separateWords]]|Functions exactly the same as [[llParseString2List]] unless you violate it&#039;s additional preconditions.&lt;br /&gt;
Appears to be correct at a glance.}}&lt;br /&gt;
{{!}}}&lt;br /&gt;
|also_functions={{LSL DefineRow||[[llParseStringKeepNulls]]}}&lt;br /&gt;
{{LSL DefineRow||[[llDumpList2String]]}}&lt;br /&gt;
{{LSL DefineRow||[[llCSV2List]]}}&lt;br /&gt;
{{LSL DefineRow||[[llList2CSV]]}}&lt;br /&gt;
|also_events&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_articles={{LSL DefineRow||[[Separate Words]]}}&lt;br /&gt;
{{LSL DefineRow||[[LSL-Editor/Bugs]]}}&lt;br /&gt;
|notes=Whenever you need to process the resulting list entries further (eg. typecasting), remember to [[llStringTrim]] any whitespace characters from the string. This can prevent issues such as unexpected values for vectors and rotations.&lt;br /&gt;
-----&lt;br /&gt;
If you indicate some items as separators, it will split the string where it finds the indicated separators, and strip out the separators.&lt;br /&gt;
&lt;br /&gt;
If instead you indicate some items as spacers, it will split the string where it finds the spacers, but leave the spacers there, including them as separate entries in the result list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;string myString = &amp;quot;What Are You Looking At?&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
llSay(0, llList2CSV( llParseString2List(myString,  [&amp;quot;W&amp;quot;, &amp;quot;A&amp;quot;, &amp;quot;Y&amp;quot;, &amp;quot;L&amp;quot;], [] ) ) );&lt;br /&gt;
//returns:  hat , re , ou , ooking , t?&lt;br /&gt;
    &lt;br /&gt;
llSay(0, llList2CSV( llParseString2List(myString, [], [&amp;quot;W&amp;quot;, &amp;quot;A&amp;quot;, &amp;quot;Y&amp;quot;, &amp;quot;L&amp;quot;] ) ) );&lt;br /&gt;
//returns: W, hat , A, re , Y, ou , L, ooking , A, t?&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using &amp;quot; &amp;quot; as a separator will parse a sentence into words.&lt;br /&gt;
&lt;br /&gt;
If there is no spacer you care about, just use &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; as the spacer.&lt;br /&gt;
&lt;br /&gt;
If an empty string is used as a separator or a spacer, it will have no effect.&lt;br /&gt;
|permission&lt;br /&gt;
|negative_index&lt;br /&gt;
|sort=ParseString2List&lt;br /&gt;
|cat1=List&lt;br /&gt;
|cat2=String&lt;br /&gt;
|cat3=Data Conversion&lt;br /&gt;
|cat4&lt;br /&gt;
|history={{LSL Added|0.6.0|remote=http://secondlife.wikia.com/wiki/Version_0.6.0}}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlResetTime&amp;diff=1218527</id>
		<title>LlResetTime</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlResetTime&amp;diff=1218527"/>
		<updated>2025-11-30T11:01:48Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Removed inaccurate caveat&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|func_id=83|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|func=llResetTime&lt;br /&gt;
|func_desc=Resets the script-time timer to zero.&lt;br /&gt;
|return_text&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=&lt;br /&gt;
*Script time resets when...&lt;br /&gt;
**Script reset (user or [[llResetScript]] or [[llResetOtherScript]])&lt;br /&gt;
**Call to either [[llResetTime]] or [[llGetAndResetTime]]&lt;br /&gt;
*Script time measures real world time, it is unaffected by time dilation. &lt;br /&gt;
|examples=&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default {&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        llResetTime();&lt;br /&gt;
    }&lt;br /&gt;
    touch_start(integer num_touch)&lt;br /&gt;
    {&lt;br /&gt;
        float time = llGetTime(); //Instead getting, and then resetting the time, we could use llGetAndReset() to accomplish the same thing.&lt;br /&gt;
        llResetTime();&lt;br /&gt;
        llSay(0,(string)time + &amp;quot; seconds have elapsed since the last touch.&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions={{LSL DefineRow||[[llGetTime]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetAndResetTime]]|}}&lt;br /&gt;
|also&lt;br /&gt;
|notes&lt;br /&gt;
|cat1=Time&lt;br /&gt;
|cat2=Script&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlGetAndResetTime&amp;diff=1218526</id>
		<title>LlGetAndResetTime</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlGetAndResetTime&amp;diff=1218526"/>
		<updated>2025-11-30T11:00:12Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Wording&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Issues/SVC-3293}}{{LSL_Function&lt;br /&gt;
|func_id=84&lt;br /&gt;
|func_sleep=0.0&lt;br /&gt;
|func_energy=10.0&lt;br /&gt;
|func=llGetAndResetTime&lt;br /&gt;
|sort=GetAndResetTime&lt;br /&gt;
|return_type=float&lt;br /&gt;
|return_text=that is script time in seconds and then resets the script time to zero.&lt;br /&gt;
|func_footnote&lt;br /&gt;
|spec=Script time is the amount of time the script has been running since it first started, was most recently reset, or one of the reset functions was called.  It is unaffected by time dilation.&lt;br /&gt;
|caveats=&lt;br /&gt;
*Script time is the amount of real-world time that the script has been in a running state. It is unaffected by time dilation, but it does not count time while the script is suspended, the user is offline (when in an attachment), the object is in inventory rather than rezzed, etc.&lt;br /&gt;
*Script time resets when...&lt;br /&gt;
**Script reset (user or [[llResetScript]] or [[llResetOtherScript]])&lt;br /&gt;
**Call to either [[llResetTime]] or [[llGetAndResetTime]]&lt;br /&gt;
* Due to (32 bit) floating point number limitations, the accuracy of this function is 1/32sec up to ~3 days, 1/16sec up to ~6 days, etc... doubling each time, e.g. it&#039;s only 1 second at ~194 days. Use [[llResetTime]] or [[llGetAndResetTime]] whenever practical to maintain the accuracy you require.&lt;br /&gt;
|examples=&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer num_touch)&lt;br /&gt;
    {&lt;br /&gt;
        // This is equivalent to calling llGetTime(), then llResetTime()&lt;br /&gt;
        float time = llGetAndResetTime();&lt;br /&gt;
&lt;br /&gt;
        llSay(0, (string)time + &amp;quot; seconds have elapsed since the last touch or script start.&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions={{LSL DefineRow||[[llResetTime]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetTime]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetRegionTimeDilation]]}}&lt;br /&gt;
|also&lt;br /&gt;
|cat1=Time&lt;br /&gt;
|cat2=Script&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Category:Experience_Tools&amp;diff=1218521</id>
		<title>Category:Experience Tools</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Category:Experience_Tools&amp;diff=1218521"/>
		<updated>2025-11-20T02:40:45Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: /* Experience Persistent Storage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Header|ml=*}}&lt;br /&gt;
{{RightToc}}Experience Tools provide a way to request persistent permissions across multiple logins and with multiple objects and scripts.  In order to use these functions, you must have an &#039;&#039;experience key&#039;&#039;.  For further information, see [http://community.secondlife.com/t5/English-Knowledge-Base/Experiences-in-Second-Life/ta-p/2744686 Experiences in Second Life] in the Second Life Knowledge Base.&lt;br /&gt;
&lt;br /&gt;
The Experience Tools API fall into three categories:&lt;br /&gt;
&lt;br /&gt;
== Experience Information ==&lt;br /&gt;
{|&lt;br /&gt;
{{LSL DefineRow|integer|[[llAgentInExperience]](key agent)}}&lt;br /&gt;
{{LSL DefineRow|list|[[llGetExperienceDetails]](key experience_id)}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Experience Permissions ==&lt;br /&gt;
{|&lt;br /&gt;
{{LSL DefineRow|event|[[experience_permissions]](key agent)}}&lt;br /&gt;
{{LSL DefineRow|event|[[experience_permissions_denied]](key agent, integer reason)}}&lt;br /&gt;
{{LSL DefineRow|void|[[llRequestExperiencePermissions]](key agent, string name)}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Experience Persistent Storage==&lt;br /&gt;
The persistent storage of experiences can still be used even where the experience is not set up as an allowed experience.&lt;br /&gt;
&lt;br /&gt;
Current limits for Persistent storage:&lt;br /&gt;
* Each key may be a maximum of 1011 bytes&lt;br /&gt;
* Each value may be a maximum of 4095 bytes&lt;br /&gt;
* Total stored data is restricted to 128 MiB&lt;br /&gt;
&lt;br /&gt;
These are all asynchronous functions, they all return a {{LSLGC|Key/handle|handle}} that corresponds to a [[dataserver]] event.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
{{LSL DefineRow|key|[[llReadKeyValue]](string k)}}&lt;br /&gt;
{{LSL DefineRow|key|[[llCreateKeyValue]](string k, string v)}}&lt;br /&gt;
{{LSL DefineRow|key|[[llUpdateKeyValue]](string k, string v, integer checked, string original_value)}}&lt;br /&gt;
{{LSL DefineRow|key|[[llDeleteKeyValue]](string k)}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Debugging===&lt;br /&gt;
{|&lt;br /&gt;
{{LSL DefineRow|key|[[llDataSizeKeyValue]]()|Gets the number of bytes used by the experience along with the experience&#039;s byte quote}}&lt;br /&gt;
{{LSL DefineRow|key|[[llKeysKeyValue]](integer start, integer count)|Gets the names of the keys}}&lt;br /&gt;
{{LSL DefineRow|key|[[llKeyCountKeyValue]]()|Gets the number of keys}}&lt;br /&gt;
{{LSL DefineRow|string|[[llGetExperienceErrorMessage]](integer value)|Converts an integer error code to a wordy string representation}}&lt;br /&gt;
|}&lt;br /&gt;
{{LSL Constants/Experience Tools Errors}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Template:LSL_Function/KeyValue&amp;diff=1218517</id>
		<title>Template:LSL Function/KeyValue</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Template:LSL_Function/KeyValue&amp;diff=1218517"/>
		<updated>2025-11-17T12:48:10Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added max memory limit.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;{{Multi-lang|category=LSL}}&amp;lt;/noinclude&amp;gt;{{LSL Injection Test}}{{#if:&lt;br /&gt;
&lt;br /&gt;
{{#vardefine:p_success_desc|A boolean specifying if the transaction succeeded (1) or not (0).}}&lt;br /&gt;
{{#vardefine:p_error_hover|An XP_ERROR_* flag that describes why the operation failed.}}&lt;br /&gt;
{{#vardefine:p_error_desc|An {{CAL|LSL Integer/experience_error{{#var:lang}}|XP_ERROR_*}} flag that describes why the operation failed.}}&lt;br /&gt;
{{#vardefine:p_cdl_desc|A string that is a comma-deliminated list}}&lt;br /&gt;
{{#vardefine:p_components_desc|varies depending upon success or failure of request}}&lt;br /&gt;
&lt;br /&gt;
{{#vardefine:p_{{{d2_name|}}}_hover|{{#var:p_{{{d2_name|}}}_hover}}{{{d2_hover|}}}}}&lt;br /&gt;
{{#vardefine:p_{{{d3_name|}}}_hover|{{#var:p_{{{d3_name|}}}_hover}}{{{d3_hover|}}}}}&lt;br /&gt;
{{#vardefine:p_{{{d4_name|}}}_hover|{{#var:p_{{{d4_name|}}}_hover}}{{{d4_hover|}}}}}&lt;br /&gt;
{{#vardefine:p_{{{dl_name|}}}_hover|{{#var:p_{{{dl_name|}}}_hover}}{{{dl_hover|}}}}}&lt;br /&gt;
&lt;br /&gt;
{{#if:{{{1|}}}&amp;lt;noinclude&amp;gt;*&amp;lt;/noinclude&amp;gt;|{{LSL Function/KeyValue/Key|{{{1}}}|dataserver={{{dataserver|}}}}}}}&lt;br /&gt;
{{#if:{{{key|}}}|{{LSL Function/KeyValue/Key|{{{key}}}|dataserver={{{dataserver|}}}}}}}&lt;br /&gt;
{{#if:{{{2|}}}&amp;lt;noinclude&amp;gt;*&amp;lt;/noinclude&amp;gt;|{{LSL Function/KeyValue/Value|{{{2}}}|dataserver={{{dataserver|}}}}}}}&lt;br /&gt;
{{#if:{{{value|}}}&amp;lt;noinclude&amp;gt;*&amp;lt;/noinclude&amp;gt;|{{LSL Function/KeyValue/Value|{{{value}}}|Note! This value may contain commas.|dataserver={{{dataserver|}}}}}}}&lt;br /&gt;
&lt;br /&gt;
{{#vardefine:p_{{{d2_name|}}}_desc|{{#var:p_{{{d2_name|}}}_desc}}{{{d2_desc|}}}}}&lt;br /&gt;
{{#vardefine:p_{{{d3_name|}}}_desc|{{#var:p_{{{d3_name|}}}_desc}}{{{d3_desc|}}}}}&lt;br /&gt;
{{#vardefine:p_{{{d4_name|}}}_desc|{{#var:p_{{{d4_name|}}}_desc}}{{{d4_desc|}}}}}&lt;br /&gt;
{{#vardefine:p_{{{dl_name|}}}_desc|{{#var:p_{{{dl_name|}}}_desc}}{{{dl_desc|}}}}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
{{#ifeq:{{#var:article-type}}|event||{{#if:{{#pos:{{#var:moded}}|r}}{{#pos:{{#var:moded}}|u}}||{{#vardefine:hidden-text|{{#var:hidden-text}}&lt;br /&gt;
{{LSLC|Dataserver{{#var:lang}}}}{{#if:{{{mode|}}}|{{LSLC|Dataserver/{{{mode}}}{{#var:lang}}}}}}&lt;br /&gt;
}}}}}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#vardefine:table_text|{{PBR}}&lt;br /&gt;
The string containing a comma-delimited list ({{LSLPT|cdl}}). &amp;lt;code&amp;gt;llDumpList2String([ [[integer]]&amp;amp;nbsp;{{LSLPT|success}}&amp;amp;nbsp;]&amp;amp;nbsp;+ {{LSLPT|components}});&amp;lt;/code&amp;gt;&lt;br /&gt;
* {{LSLPT|components}} vary depending upon success or failure of request.&lt;br /&gt;
* Failure: &amp;lt;code&amp;gt;{{LSLPT|cdl}} = [[llDumpList2String]]([ 0, [[integer]]&amp;amp;nbsp;{{LSLPT|error}}],&amp;quot;,&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Success: &amp;lt;code&amp;gt;{{LSLPT|cdl}} = [[llDumpList2String]]([ 1{{#if:{{{d2_type|}}}{{{d2_name|}}}|, [[{{{d2_type}}}{{#var:lang}}|{{{d2_type}}}]]&amp;amp;nbsp;{{LSLPT|{{{d2_name}}}}}{{#if:{{{d3_type|}}}{{{d3_name|}}}|, [[{{{d3_type}}}{{#var:lang}}|{{{d3_type}}}]]&amp;amp;nbsp;{{LSLPT|{{{d3_name}}}}}{{#if:{{{d4_type|}}}{{{d4_name|}}}|, [[{{{d4_type}}}{{#var:lang}}|{{{d4_type}}}]]&amp;amp;nbsp;{{LSLPT|{{{d4_name}}}}}}}}}}} ]{{#if:{{{dl_name|}}}|&amp;amp;nbsp;+ {{LSLPT|{{{dl_name|}}}}})}},&amp;quot;,&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
===== String Components =====&lt;br /&gt;
{{{!}} style=&amp;quot;margin-left:0em;&amp;quot;&lt;br /&gt;
{{LSL DefineRow|integer|2={{CAL|LSL Integer/boolean{{#var:lang}}|success}}|3={{#var:p_success_desc}}}}&lt;br /&gt;
{{LSL DefineRow|integer|2={{CAL|LSL Integer/experience_error{{#var:lang}}|error}}|3={{#var:p_error_desc}}}}&lt;br /&gt;
{{#if:{{{d2_name|}}}{{{d2_type|}}}|{{LSL DefineRow|1={{{d2_type|}}}|2={{#if:{{{d2_subtype|}}}|{{CAL|LSL {{ucfirst:{{{d2_type|}}}}}/{{{d2_subtype|}}}{{#var:lang}}|{{{d2_name|}}}}}|{{LSL_Parameter_Link|{{{d2_name|}}}}}}}|3={{#var:p_{{{d2_name|}}}_desc}}}}&lt;br /&gt;
{{#if:{{{d3_name|}}}{{{d3_type|}}}|{{LSL DefineRow|1={{{d3_type|}}}|2={{#if:{{{d3_subtype|}}}|{{CAL|LSL {{ucfirst:{{{d3_type|}}}}}/{{{d3_subtype|}}}{{#var:lang}}|{{{d3_name|}}}}}|{{LSL_Parameter_Link|{{{d3_name|}}}}}}}|3={{#var:p_{{{d3_name|}}}_desc}}}}&lt;br /&gt;
{{#if:{{{d4_name|}}}{{{d4_type|}}}|{{LSL DefineRow|1={{{d4_type|}}}|2={{#if:{{{d4_subtype|}}}|{{CAL|LSL {{ucfirst:{{{d4_type|}}}}}/{{{d4_subtype|}}}{{#var:lang}}|{{{d4_name|}}}}}|{{LSL_Parameter_Link|{{{d4_name|}}}}}}}|3={{#var:p_{{{d4_name|}}}_desc}}}}&lt;br /&gt;
}}}}}}&lt;br /&gt;
{{#if:{{{dl_name|}}}|{{LSL DefineRow|1=list|2={{#if:{{{dl_subtype|}}}|{{CAL|LSL List/{{{dl_subtype|}}}{{#var:lang}}|{{{dl_name|}}}}}|{{LSL_Parameter_Link|{{{dl_name|}}}}}}}|3={{#var:p_{{{dl_name|}}}_desc}}}}}}&lt;br /&gt;
{{!}}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{#if:{{{dataserver|}}}|&lt;br /&gt;
{{#vardefine:p_{{{d2_name|}}}_hover|}}&lt;br /&gt;
{{#vardefine:p_{{{d3_name|}}}_hover|}}&lt;br /&gt;
{{#vardefine:p_{{{d4_name|}}}_hover|}}&lt;br /&gt;
{{#vardefine:p_{{{dl_name|}}}_hover|}}&lt;br /&gt;
{{#vardefine:p_{{{d2_name|}}}_desc|}}&lt;br /&gt;
{{#vardefine:p_{{{d3_name|}}}_desc|}}&lt;br /&gt;
{{#vardefine:p_{{{d4_name|}}}_desc|}}&lt;br /&gt;
{{#vardefine:p_{{{dl_name|}}}_desc|}}&lt;br /&gt;
|&lt;br /&gt;
{{#vardefine:spec|{{#var:spec}}&lt;br /&gt;
* Maximum memory storage: 128 MiB&lt;br /&gt;
&amp;lt;h4&amp;gt;Dataserver&amp;lt;/h4&amp;gt;&lt;br /&gt;
The [[dataserver]] callback parameters are:&lt;br /&gt;
* A key containing the {{LSLGC|Key/handle|handle}} returned from [[{{#var:name}}]]&lt;br /&gt;
* A string containing a comma-delimited list ({{LSLPT|cdl}}). &amp;lt;code&amp;gt;llDumpList2String([ [[integer]]&amp;amp;nbsp;{{LSLPT|success}}&amp;amp;nbsp;]&amp;amp;nbsp;+ {{LSLPT|components}});&amp;lt;/code&amp;gt;&lt;br /&gt;
** {{LSLPT|components}} vary depending upon success or failure of request.&lt;br /&gt;
** Failure: &amp;lt;code&amp;gt;{{LSLPT|cdl}} = [[llDumpList2String]]([ 0, [[integer]]&amp;amp;nbsp;{{LSLPT|error}}],&amp;quot;,&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
** Success: &amp;lt;code&amp;gt;{{LSLPT|cdl}} = [[llDumpList2String]]([ 1{{#if:{{{d2_type|}}}{{{d2_name|}}}|, [[{{{d2_type}}}{{#var:lang}}|{{{d2_type}}}]]&amp;amp;nbsp;{{LSLPT|{{{d2_name}}}}}{{#if:{{{d3_type|}}}{{{d3_name|}}}|, [[{{{d3_type}}}{{#var:lang}}|{{{d3_type}}}]]&amp;amp;nbsp;{{LSLPT|{{{d3_name}}}}}{{#if:{{{d4_type|}}}{{{d4_name|}}}|, [[{{{d4_type}}}{{#var:lang}}|{{{d4_type}}}]]&amp;amp;nbsp;{{LSLPT|{{{d4_name}}}}}}}}}}} ]{{#if:{{{dl_name|}}}|&amp;amp;nbsp;+ {{LSLPT|{{{dl_name|}}}}})}},&amp;quot;,&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
===== String Components =====&lt;br /&gt;
{{{!}} style=&amp;quot;margin-left:0em;&amp;quot;&lt;br /&gt;
{{LSL DefineRow|integer|2={{CAL|LSL Integer/boolean{{#var:lang}}|success}}|3={{#var:p_success_desc}}}}&lt;br /&gt;
{{LSL DefineRow|integer|2={{CAL|LSL Integer/experience_error{{#var:lang}}|error}}|3={{#var:p_error_desc}}}}&lt;br /&gt;
{{#if:{{{d2_name|}}}{{{d2_type|}}}|{{LSL DefineRow|1={{{d2_type|}}}|2={{#if:{{{d2_subtype|}}}|{{CAL|LSL {{ucfirst:{{{d2_type|}}}}}/{{{d2_subtype|}}}{{#var:lang}}|{{{d2_name|}}}}}|{{LSL_Parameter_Link|{{{d2_name|}}}}}}}|3={{#var:p_{{{d2_name|}}}_desc}}}}&lt;br /&gt;
{{#if:{{{d3_name|}}}{{{d3_type|}}}|{{LSL DefineRow|1={{{d3_type|}}}|2={{#if:{{{d3_subtype|}}}|{{CAL|LSL {{ucfirst:{{{d3_type|}}}}}/{{{d3_subtype|}}}{{#var:lang}}|{{{d3_name|}}}}}|{{LSL_Parameter_Link|{{{d3_name|}}}}}}}|3={{#var:p_{{{d3_name|}}}_desc}}}}&lt;br /&gt;
{{#if:{{{d4_name|}}}{{{d4_type|}}}|{{LSL DefineRow|1={{{d4_type|}}}|2={{#if:{{{d4_subtype|}}}|{{CAL|LSL {{ucfirst:{{{d4_type|}}}}}/{{{d4_subtype|}}}{{#var:lang}}|{{{d4_name|}}}}}|{{LSL_Parameter_Link|{{{d4_name|}}}}}}}|3={{#var:p_{{{d4_name|}}}_desc}}}}&lt;br /&gt;
}}}}}}&lt;br /&gt;
{{#if:{{{dl_name|}}}|{{LSL DefineRow|1=list|2={{#if:{{{dl_subtype|}}}|{{CAL|LSL List/{{{dl_subtype|}}}{{#var:lang}}|{{{dl_name|}}}}}|{{LSL_Parameter_Link|{{{dl_name|}}}}}}}|3={{#var:p_{{{dl_name|}}}_desc}}}}}}&lt;br /&gt;
{{!}}}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
{{#if:{{{nocaveats|}}}||{{#if:{{{nogroup|}}}|&lt;br /&gt;
{{#vardefine:caveats|{{#var:caveats}}}}&lt;br /&gt;
*&lt;br /&gt;
}}}}&lt;br /&gt;
&lt;br /&gt;
{{#if:{{#var:key_stuff}}||&lt;br /&gt;
{{#vardefine:key_stuff|*}}&lt;br /&gt;
{{#vardefine:notes|{{#var:notes}}}}&lt;br /&gt;
}}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
}}&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{| {{Prettytable}}&lt;br /&gt;
|-{{Hl2}}&lt;br /&gt;
! #var&lt;br /&gt;
! value&lt;br /&gt;
|-&lt;br /&gt;
{{VarPair|p_{{{1}}}_desc}}&lt;br /&gt;
|-&lt;br /&gt;
{{VarPair|p_{{{2}}}_desc}}&lt;br /&gt;
|-&lt;br /&gt;
{{VarPair|p_{{{value}}}_desc}}&lt;br /&gt;
|-&lt;br /&gt;
{{VarPair|spec}}&lt;br /&gt;
|-&lt;br /&gt;
{{VarPair|caveats}}&lt;br /&gt;
|-&lt;br /&gt;
{{VarPair|table_text}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;{{#if:{{{dataserver|}}}|{{#var:table_text}}}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Category:LSL_Light&amp;diff=1218516</id>
		<title>Category:LSL Light</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Category:LSL_Light&amp;diff=1218516"/>
		<updated>2025-11-17T00:38:58Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Removed outdated technical explanation. There hasn&amp;#039;t been a limit of 8 lights for over a decade.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Header|ml=*}}{{LSLC|}}{{LSLC|Prim}}&lt;br /&gt;
== Properties of light ==&lt;br /&gt;
&lt;br /&gt;
The quality of a [[PRIM_POINT_LIGHT]] cast by an object is determined by five parameters found on the [[Building_Tools#Features_Tab|features tab]] of the [[Building_Tools#Edit_Tool|edit tool]], which can also be set by script commands. The controls appear in the dialog in the same order as used in the script commands (see example below).&lt;br /&gt;
&lt;br /&gt;
The first is a simple &#039;&#039;&#039;on/off switch&#039;&#039;&#039;: either the object is a [[PRIM_POINT_LIGHT]] source (checkbox activated or [[TRUE]]) or it isn&#039;t. The light&#039;s &#039;&#039;&#039;color&#039;&#039;&#039; is set by a color picker (dialog) or as an RGB vector (script) as usual. The &#039;&#039;&#039;intensity&#039;&#039;&#039; of light is a measurement of how strong in absolute terms the light is. This is a floating-point number ranging from 0.0 to 1.0, where 1.0 is fully on and 0.0 is equivalent to &amp;quot;off.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The two remaining settings are trickier, because their effects are quite subtle and they do interact with each other. &#039;&#039;&#039;Radius&#039;&#039;&#039; is the distance that light will travel from the centre of the object before it blends into and becomes indistinguishable from the scene&#039;s natural ambient lighting. This is a floating-point number measured in metres. &#039;&#039;&#039;Falloff&#039;&#039;&#039; is the extent to which light &amp;quot;decays&amp;quot; as it travels away from the object. This is a floating-point number ranging from 0.01 to 2.0, where 2.0 is an abrupt falling-off of intensity and 0.01 is relatively little change over distance. &lt;br /&gt;
&lt;br /&gt;
To illustrate how these settings work, imagine&amp;amp;mdash;or better still: build&amp;amp;mdash;a hollow grey cube 8 by 8 by 8 metres on the inside, with a light source hovering in the middle of one end-wall, and yourself standing below the light source looking at the opposite wall. (Hint: if you do actually build this, turn the sun off while you are testing; you may also need to close the curtains and extinguish all lights in the real-life room you&#039;re working in.)&lt;br /&gt;
&lt;br /&gt;
While the &#039;&#039;&#039;radius&#039;&#039;&#039; is less than 4 metres (i.e. under halfway to the opposite wall), it remains fairly dark. Turning the falloff up to 2.0 (maximum decay) makes the room somewhat darker.&lt;br /&gt;
&lt;br /&gt;
As the &#039;&#039;&#039;radius&#039;&#039;&#039; approaches 8 metres (the distance to the wall), the room is only slightly brighter; at maximum &#039;&#039;&#039;falloff&#039;&#039;&#039;, the room is about as dark as it was when the &#039;&#039;&#039;radius&#039;&#039;&#039; was 4 metres and &#039;&#039;&#039;falloff&#039;&#039;&#039; was 0.01.&lt;br /&gt;
&lt;br /&gt;
Only after the &#039;&#039;&#039;radius&#039;&#039;&#039; is greater than the distance to the opposite wall does it become significantly brighter. The effect of &#039;&#039;&#039;falloff&#039;&#039;&#039; is greater: when it is 0.01, direct illumination begins to make concentric circles on the wall, centred on the point where it intersects the light&#039;s axis; at the maximum of 2.0 the room is again as dark as it was when the &#039;&#039;&#039;radius&#039;&#039;&#039; was only 4 metres.&lt;br /&gt;
&lt;br /&gt;
== Controlling light sources ==&lt;br /&gt;
An example - How to generate, switch and change light from LSL:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
integer isLightTurnedOn;&lt;br /&gt;
 &lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer total_number)&lt;br /&gt;
    {&lt;br /&gt;
//      toggle isLightTurnedOn between TRUE and FALSE&lt;br /&gt;
        isLightTurnedOn = !isLightTurnedOn;&lt;br /&gt;
 &lt;br /&gt;
        if (isLightTurnedOn)&lt;br /&gt;
        {&lt;br /&gt;
            llSetPrimitiveParams([&lt;br /&gt;
                PRIM_FULLBRIGHT, ALL_SIDES, FALSE,&lt;br /&gt;
                PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 1.0, 10.0, 0.6]);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            vector COLOR_ORANGE  = &amp;lt;1.000, 0.522, 0.106&amp;gt;;&lt;br /&gt;
 &lt;br /&gt;
            llSetPrimitiveParams([&lt;br /&gt;
                PRIM_FULLBRIGHT, ALL_SIDES, TRUE,&lt;br /&gt;
                PRIM_POINT_LIGHT, TRUE, COLOR_ORANGE, 1.0, 10.0, 0.6]);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following example shows how to create a configurable light that turns itself on and off when touched.  The light&#039;s properties can be edited by changing the values defined at the top of the script.  This script illustrates the lighting attributes [[PRIM_POINT_LIGHT]], [[PRIM_FULLBRIGHT]] and [[PRIM_GLOW]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
// Touch the object to light it up.&lt;br /&gt;
// Lighting is configurable.&lt;br /&gt;
&lt;br /&gt;
integer light_s    = TRUE;&lt;br /&gt;
vector  lightcolor = &amp;lt;1.0, 0.75, 0.5&amp;gt;;&lt;br /&gt;
float   intensity  = 1.0;             // 0.0 &amp;lt;= intensity &amp;lt;= 1.0&lt;br /&gt;
float   radius     = 10.0;            // 0.1 &amp;lt;= radius &amp;lt;= 20.0&lt;br /&gt;
float   falloff    = 0.01;            // 0.01 &amp;lt;= falloff &amp;lt;= 2.0&lt;br /&gt;
float   glow       = 0.05;&lt;br /&gt;
&lt;br /&gt;
toggle()&lt;br /&gt;
{&lt;br /&gt;
    float thisglow = 0.0;&lt;br /&gt;
    light_s = !light_s;&lt;br /&gt;
&lt;br /&gt;
    if (light_s)&lt;br /&gt;
        thisglow = glow;&lt;br /&gt;
&lt;br /&gt;
    llSetPrimitiveParams([&lt;br /&gt;
        PRIM_POINT_LIGHT, light_s, lightcolor, intensity, radius, falloff,&lt;br /&gt;
        PRIM_FULLBRIGHT, ALL_SIDES, light_s,&lt;br /&gt;
        PRIM_GLOW,       ALL_SIDES, thisglow&lt;br /&gt;
    ]);&lt;br /&gt;
      llSetColor(lightcolor, ALL_SIDES);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        llSetText(&amp;quot;Touch for light&amp;quot;, &amp;lt;1.0, 1.0, 1.0&amp;gt;, 1.0);&lt;br /&gt;
        toggle();&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    touch_start(integer total_number)&lt;br /&gt;
    {&lt;br /&gt;
        toggle();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Limits&amp;diff=1218515</id>
		<title>Limits</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Limits&amp;diff=1218515"/>
		<updated>2025-11-16T19:06:15Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: /* Lighting */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;{{KBmaster}}&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
== Summary ==&lt;br /&gt;
{{RightToc}}&lt;br /&gt;
The Second Life Viewer and simulators have a division of labor, keeping track of the data that makes Second Life run. The Viewer&#039;s job is to:&lt;br /&gt;
&lt;br /&gt;
* Handle locations of objects.&lt;br /&gt;
* Get velocities and other physics information, and does simple physics to keep track of what is moving where.&lt;br /&gt;
&lt;br /&gt;
The simulator&#039;s job is to:&lt;br /&gt;
&lt;br /&gt;
* Run the physics engine.&lt;br /&gt;
* Detect collisions.&lt;br /&gt;
* Keep track of where everything is&lt;br /&gt;
* Send locations of content to the Viewer along with updates when certain changes occur.&lt;br /&gt;
&lt;br /&gt;
Limits are necessary for all of these components to work together. The list below outlines many of the &#039;&#039;&#039;Second Life numerical limits that affect your inworld experience&#039;&#039;&#039;. Some of these will likely change over time, so if you spot something incorrect/outdated, please take a moment to update it.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|1=The scope of this page is focused on Linden Lab&#039;s official Viewers, keeping in mind [[Third_Party_Viewer_Directory|3rd-party viewers may differ]]. Cite sources and provide substantiation for limits that aren&#039;t provided by Linden Lab. Also, unconfirmed speculation [[Talk:Limits|goes on this discussion page]]. Don&#039;t add data without a hard limit, and don&#039;t add obscure trivia that doesn&#039;t practically affect the general inworld experience.|width=80%}}&lt;br /&gt;
&lt;br /&gt;
== [[Avatar]] ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Max. # of attachments&#039;&#039;&#039; - 38 combined [[HUD]] and body attachments.&lt;br /&gt;
** They can be viewed by right-clicking your avatar and choosing &#039;&#039;&#039;Edit My Outfit&#039;&#039;&#039;.&lt;br /&gt;
** With the formal introduction of [[multiple attachments|multiple attachments to a single point]] in Viewer 2.4, you can attach up to 38 total objects, and they can all be attached to a single point. &lt;br /&gt;
** With the introduction of the Bento armature, there are 47 attachment points available.&lt;br /&gt;
** With the introduction of Animesh, there is a limit of 1 Animesh attachment for Basic &amp;amp; Plus members, 2 for Premium members, and 3 for Premium Plus members.&lt;br /&gt;
* &#039;&#039;&#039;Max. attachment position&#039;&#039;&#039; - 3.5m in a sphere around &amp;lt;0, 0, 0&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Max. # of clothing layers&#039;&#039;&#039; - 60 including alpha, tattoo, shoe base, physics, socks, gloves, undershirt, underpants, shirt, pants, jacket, skirt&lt;br /&gt;
** On viewers before Second Life Release 3.7.29.301305, the potential total was still 60, but limited to 5 items of each type.&lt;br /&gt;
** In any case, only one each of shape, skin, eyes and hair base may be worn. These 4 basic body parts are in addition to the 60 clothing layers.&lt;br /&gt;
* &#039;&#039;&#039;Max Clothing Texture&#039;&#039;&#039; - 1024x1024 px (except 128x128 px for eyes) - Server Side Appearance (and legacy baking on outside grids) will downsize larger textures*. (Support for 2K textures &amp;amp; Bakes on Mesh is in development but not yet supported)&lt;br /&gt;
* &#039;&#039;&#039;Max. unassisted fly height&#039;&#039;&#039; - Stable hover at current ground elevation &#039;&#039;plus&#039;&#039; 5020m.&lt;br /&gt;
** You can easily acquire a flight attachment to fly high. Also see [[flight limit]] and [[Limits#Navigation|the Navigation section]] below.&lt;br /&gt;
* &#039;&#039;&#039;Common shoe sizes&#039;&#039;&#039; - Traditionally, most women&#039;s shoes were designed for a size 0 (zero) foot. Men&#039;s generally scale up to 14. Newer items (mesh and some sculpted) replace the system feet or conform to the avatar&#039;s shape, so adjustments may be necessary.&lt;br /&gt;
** You can check this by going to Edit menu &amp;gt; Appearance and clicking on the Shape &amp;gt; Legs tab.&lt;br /&gt;
&amp;lt;!--Can someone verify the following?&lt;br /&gt;
* &#039;&#039;&#039;Min. length of avatar name&#039;&#039;&#039; - 2 for the first name, 2 for the last name, 5 in total (including the space).--&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Max. length of avatar name&#039;&#039;&#039; - 31 for first name, 31 for last name, 63 in total (including the space).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Max. length of {{LSLGC|Username}}&#039;&#039;&#039; - All lower case and a maximum of 63 including the &#039;.&#039;(eg; firstname.lastname)&lt;br /&gt;
**Older account user names are a combination of the First and last name in lower case separated by a period(.)(eg; lilmix.pixelmaid)&lt;br /&gt;
**Modern user names are a single name(no period)(eg; john1234) - &#039;&#039;&#039;Max.&#039;&#039;&#039; length is 31 characters&lt;br /&gt;
**The {{LSLGC|Legacy Name}}s for modern user names always shows Resident as the last name. So the Max is 40 characters in total for a new avatar&#039;s legacy name(including the space and last name &#039;Resident&#039;)&lt;br /&gt;
*&#039;&#039;&#039;Min. length of [[Display_Names]]&#039;&#039;&#039; - At least one script character&lt;br /&gt;
*&#039;&#039;&#039;Max. length of Display Names&#039;&#039;&#039; - 31 characters&lt;br /&gt;
**Display Names can include most Unicode script characters, spaces, and some punctuation.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Max. avatar speed&#039;&#039;&#039; - 250m/s (with only attachments to assist)&lt;br /&gt;
* &#039;&#039;&#039;Max. teleport speed&#039;&#039;&#039; - 6 teleports per minute&lt;br /&gt;
&lt;br /&gt;
== [[Animation]] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Length of animation&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|60.0 seconds}}&lt;br /&gt;
| Limited by length or Size, whichever is reached first&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Size of Animation&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|{{HoverText|250,000 bytes|250 KB (decimal), 244 KB (binary)}}}}&lt;br /&gt;
| Limited by length or Size, whichever is reached first. Size is after conversion to [[Internal Animation Format|.anim]]. {{KBtip|custom=Tip for Blender users:|1=You can use a built-in function called &amp;quot;&#039;&#039;Decimate&#039;&#039;&amp;quot; to simplify your animation. See [https://www.youtube.com/watch?v=lScwEYJZy1M this tutorial]}}&lt;br /&gt;
| [[Internal Animation Format]]&lt;br /&gt;
|-&lt;br /&gt;
||Distance an Animation Can Travel&lt;br /&gt;
| {{no|-5 meters (per axis)}}&lt;br /&gt;
| {{no|+5 meters (per axis)}}&lt;br /&gt;
| 10 meters per axis cumulatively (travelling from -5 to +5).&lt;br /&gt;
| n/a&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||animation priority&lt;br /&gt;
| {{no|0}}&lt;br /&gt;
| {{no|.bvh: 4, [[Internal Animation Format|.anim]]: 6}}&lt;br /&gt;
| [[Internal Animation Format|.anim]] uses per-joint priorities, meaning it&#039;s possible to have a mix of joint priorities in the same animation.&lt;br /&gt;
| [[Animation_Priority|Animation Priority]]&lt;br /&gt;
|-&lt;br /&gt;
||Number of unique joints per animation&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|216}}&lt;br /&gt;
| [[Internal Animation Format|.anim]], unlike .bvh, uses a flat structure, meaning that only the bones animated are stored, and bones which would otherwise be kept by .bvh (to keep hierarchy structure) are deleted.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of simultaneous animations playing&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|30}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [http://community.secondlife.com/t5/English-Knowledge-Base/Build-Tools/ta-p/700039 Building] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||[[Prim]] dimensions&lt;br /&gt;
| {{no|&amp;lt;0.01,&amp;amp;nbsp;0.01,&amp;amp;nbsp;0.01&amp;gt;}}&lt;br /&gt;
| {{no|&amp;lt;64.0, 64.0, 64.0&amp;gt;}}&lt;br /&gt;
| Used to be &amp;lt;10.0, 10.0, 10.0&amp;gt; prior to Second Life Server version 11.08.17.238770&lt;br /&gt;
| [[llSetScale]], [[PRIM_SIZE]] and [[Megaprim]]&lt;br /&gt;
|-&lt;br /&gt;
||Number of prims in a linkset&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|256 prims}}&lt;br /&gt;
| Used to be 255 prior to Second Life Server version 1.26&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Linking distance&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|64 meters}}&lt;br /&gt;
| distance has changed on [https://community.secondlife.com/blogs/entry/13626-coming-soon-server-release-202306/ 2023.06 update]. The old max distance was 54 meters.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Local offset (avatars)&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1000 meters per axis}}&lt;br /&gt;
| Unlike regular [[Linkability_Rules|Linkability Rules]] for linksets, an avatar sitting on an object may be moved significantly further by scripts.&lt;br /&gt;
| [[llSetLinkPrimitiveParams]] with [[PRIM_POS_LOCAL]]&lt;br /&gt;
|-&lt;br /&gt;
||[[Mesh/Mesh_physics#Physics_Resource_Cost|Physics cost]] for a physical object&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|32.0}}&lt;br /&gt;
||Additional notes:&lt;br /&gt;
* If you try to link more, it&#039;ll either say &amp;quot;Can&#039;t enable physics for objects with more than 32 primitives&amp;quot; or &amp;quot;Object has too many primitives -- its dynamics have been disabled.&amp;quot;.&lt;br /&gt;
* On server versions 1.38 and below, each sitting avatar counted as 1 prim. From 1.40.2 the limit is only on actual prims.&lt;br /&gt;
| [[OBJECT_PHYSICS_COST]]&lt;br /&gt;
|-&lt;br /&gt;
||Build or rez height&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|4096.0 meters}}&lt;br /&gt;
| 768.0 meters for Viewers prior to version 1.20&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Prim name length&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|63 bytes UTF-8 [[String|string]]}}&lt;br /&gt;
| same limit for {{LSLGC|Avatar/Name|avatar names}}, although the character set for avatar names is significantly limited.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Prim description length&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|127 bytes UTF-8 [[String|string]]}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Temporary prims&#039; lifetime&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|up to around 60 seconds}}&lt;br /&gt;
| depends upon when the next garbage collector cycle is&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of temporary prims&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|RegionPrimLimit - NumberOfPrimsInRegion + Minimum(0.5*RegionPrimLimit, 1000)}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Length of hovertext&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|254 bytes UTF-8 [[String|string]]}}&lt;br /&gt;
| n/a&lt;br /&gt;
| [[llSetText]] and [[PRIM_TEXT]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [https://community.secondlife.com/knowledgebase/english/lighting-and-shadows-r331/ Lighting] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Number of active light sources&lt;br /&gt;
| n/a&lt;br /&gt;
| {{yes|no limit}}&lt;br /&gt;
| Advanced Lighting Model (ALM) was developed around 2011-2013 to remove the hard limit on light sources.&lt;br /&gt;
|-&lt;br /&gt;
||Number of projectors rendered at full detail&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|2}}&lt;br /&gt;
| Only the 2 projectors closest to an object will cause it to cast shadows. Additional projectors still affect color.&lt;br /&gt;
| [https://community.secondlife.com/knowledgebase/english/lighting-and-shadows-r331/#Projectors Knowledge Base &amp;gt; Lighting &amp;gt; Projectors]&lt;br /&gt;
|-&lt;br /&gt;
||Number of lights rendered on Alpha Blended surfaces&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|8}}&lt;br /&gt;
| 8 including the Sun and Moon. The viewer will attempt to select the &amp;quot;most significant&amp;quot; lights to render, however these blended surfaces will look different as a result of these lighting limitations.&lt;br /&gt;
| [[Alpha Modes Do&#039;s and Don&#039;ts#Alpha_blending|Alpha Blending]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [[Communication]] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Whisper distance&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|10 meters}}&lt;br /&gt;
| &amp;lt;code&amp;gt;/whisper&amp;lt;/code&amp;gt; as the first few characters in a message can be used in viewer-2-code based viewers in local chat.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Chat distance&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|20 meters}}&lt;br /&gt;
| text spoken as a &amp;quot;chat&amp;quot; step &#039;&#039;&#039;within gestures&#039;&#039;&#039; cannot be shouted or whispered. /whisper and /shout &#039;&#039;can&#039;&#039; be used in the &amp;quot;Replace with&amp;quot; field.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Shout distance&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|100 meters}}&lt;br /&gt;
| &amp;lt;code&amp;gt;/shout&amp;lt;/code&amp;gt; as the first few characters in a message can be used in viewer-2-code based viewers in local chat.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Length of a chat message and whether or not it is truncated&lt;br /&gt;
| {{no|1 byte or single-byte character}}&lt;br /&gt;
| {{no|1024 bytes or single-byte characters}}&lt;br /&gt;
| Limit does not apply to instant messages via [[llInstantMessage]] and communication between IM and email, the limit there is 1023 bytes or single-byte characters.&lt;br /&gt;
The viewer-to-server communication on &#039;&#039;negative&#039;&#039; channels is truncated to 254 bytes, and on &#039;&#039;positive&#039;&#039; channels, to 1023 bytes. These limits do not affect communication between scripts.&lt;br /&gt;
| [[llInstantMessage]]&lt;br /&gt;
|-&lt;br /&gt;
||Maximum events&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|64 events}}&lt;br /&gt;
| Events are stored in a 64 bit bitmap. Events happening when the bitmap is full, will get discarded.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of offline messages&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|Capped at 30 messages for Basic members, 40 for Plus members, 120 for Premium members, and 240 for Premium Plus members.}}&lt;br /&gt;
| Number of offline messages (involving [[IM|IMs]], inventory offers, group notices, group invitations) received before messages get capped. Note: If autoAcceptNewInventory (debug setting) is set to TRUE (the default is FALSE), then all inventory offers, even above the cap allowed by membership, go directly to inventory and do not count against the cap on offline messages. &lt;br /&gt;
| [[IM]] - [https://community.secondlife.com/blogs/entry/2559-group-limits-update-no-changes-for-basic-members/ Blog Post]&lt;br /&gt;
|-&lt;br /&gt;
||Length of a properly delivered email reply to an [[IM]]&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1023 bytes or single-byte characters}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Time after which a reply to an [[IM]] can be sent via email.&lt;br /&gt;
| {{no|when receiving the IM via email.}}&lt;br /&gt;
| {{no|5 days after receiving the offline IM via email.}}&lt;br /&gt;
| n/a&lt;br /&gt;
| [https://blogs.secondlife.com/community/features/blog/2007/02/06/im-to-e-mail-return-addresses-changing Second Life Blogs]&lt;br /&gt;
|-&lt;br /&gt;
||Time period for which [[IM|IMs]] are stored within Second Life.&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|31 days after receiving the [[IM]]}}&lt;br /&gt;
| [[User:Torley_Linden|Torley Linden]] confirmed this with [[User:Kelly_Linden|Kelly Linden]]&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of [[IM|IMs]] sent by an object within an hour&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|5000 per hour}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [[Gesture]]s ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Shortcut key mappings&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|33 unique combinations, since {{K|F2}}&amp;amp;ndash;{{K|F12}} can be used with the {{K|Ctrl}} or {{K|Shift}} modifiers.}}&lt;br /&gt;
| n/a&lt;br /&gt;
| [[All_keyboard_shortcut_keys]] and [http://community.secondlife.com/t5/English-Knowledge-Base/Keyboard-shortcuts/ta-p/1086557 the knowledge base]&lt;br /&gt;
|-&lt;br /&gt;
||Chat step length&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|127 bytes or single-byte characters}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Wait time&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|3600 seconds (which is one hour)}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
| Maximum steps data&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1000 bytes}} of packed data; saving with more data returns the message &amp;quot;Gesture save failed. This gesture has too many steps. Try removing some steps, then save again.&amp;quot; Shorter text, for example, will allow more steps.&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Length of included assets&lt;br /&gt;
| n/a&lt;br /&gt;
| see sections for [[#Animation|animation]], [[#Sound|sound]]&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [[Group]]s ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of groups you can belong to:&#039;&#039;&#039; 50 for Basic members, 55 for Plus members, 80 for Premium members, 150 for Premium Plus members.&lt;br /&gt;
** After downgrading from a higher limit membership to a lower one, you may not join any new groups until you are a member of fewer than your current subscriptions&#039;s group limit. For example, if you downgrade from Premium (70 groups allowed) to Basic (42 groups allowed), you can&#039;t join any new groups until you&#039;re a member of fewer than 42 groups.&lt;br /&gt;
** Roles within groups are sort of like sub-groups. In many cases, you can use them instead of creating new groups.&lt;br /&gt;
* &#039;&#039;&#039;Minimum number of members in a group:&#039;&#039;&#039; 2&lt;br /&gt;
** A group with only 1 person for 48 hours will be disbanded (cancelled). Unless the group owns land.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of roles allowed in a group:&#039;&#039;&#039; 10 (including &amp;quot;Owners&amp;quot; and &amp;quot;Everyone&amp;quot;, which cannot be deleted)&lt;br /&gt;
* &#039;&#039;&#039;Maximum Group Name:&#039;&#039;&#039; 35 single-byte characters.&lt;br /&gt;
* &#039;&#039;&#039;Maximum Group Title:&#039;&#039;&#039; 20 single-byte characters.&lt;br /&gt;
* &#039;&#039;&#039;Maximum Length of a Group Notice:&#039;&#039;&#039; 512 single-byte characters.&lt;br /&gt;
* &#039;&#039;&#039;Maximum Age of a Group Notice:&#039;&#039;&#039; 14 days.&lt;br /&gt;
&lt;br /&gt;
== [[Inventory]] ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of inventory items that can be sent in a folder:&#039;&#039;&#039; 42&lt;br /&gt;
** Folders count as items too. This has more to do with packet size limits than cheeky Douglas Adams references.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of accounts that an inventory item can be shared with at once:&#039;&#039;&#039; The limit is unknown, but tests show that it is more than 35. No documentation has been found. One third-party viewer developer says that the code doesn&#039;t seem to look for a limit. It just builds the list and sends it to each avatar individually.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of inventory items that can be contained in a prim:&#039;&#039;&#039; 10,000&lt;br /&gt;
* &#039;&#039;&#039;Maximum characters in an inventory item name:&#039;&#039;&#039; 63&lt;br /&gt;
* &#039;&#039;&#039;Maximum notecard line:&#039;&#039;&#039; None, but scripts can only read the first 1024 bytes (was 255 bytes before server version [https://releasenotes.secondlife.com/simulator/2021-10-25.565008.html 2021-10-25.565008]).&lt;br /&gt;
* &#039;&#039;&#039;Maximum notecard size:&#039;&#039;&#039; 65,536 bytes&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
* &#039;&#039;&#039;Number of items in the Library:&#039;&#039;&#039; 1,248 as of 2010-06-17&lt;br /&gt;
* &#039;&#039;&#039;Number of items in the Library:&#039;&#039;&#039; 2,210 as of 2012-11-04 --&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Number of items in the Library:&#039;&#039;&#039; 3,500 as of 2019-08-10&lt;br /&gt;
** Verified by [[How_does_the_Library_work|hiding the Library]] and counting the difference.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of inventory items displayed in a single inventory folder:&#039;&#039;&#039; Several thousand. &amp;lt;!-- not a specific item count, because the data is variable size, but still useful to document that there is a limit. --&amp;gt;&lt;br /&gt;
** This was discussed during [http://wiki.secondlife.com/wiki/Simulator_User_Group/Transcripts/2011.03.08] &amp;quot;the folder will load up to the limit number of items, and remaining ones won&#039;t show up in the viewer. you still own them, they aren&#039;t lost, but they will be hidden until the folder size is reduced.&amp;quot;&lt;br /&gt;
** There is no specified limit for total avatar inventory, very large folders can be split.&lt;br /&gt;
** There was discussion in the Third Party Developers&#039; UG (4/10/2015) meeting about new inventory and login problems from having large numbers of items in a single folder. AISv3 &#039;&#039;removed&#039;&#039; server-side limits on the number of items in a folder. Flat inventories are bad. No one is certain at what number of items in a folder cause a login problem. The fix is to clear the inventory cache (not the viewer cache) and log into a deserted, empty region then move inventory items into folders and sub-folders. - The problem appears at different a number of items depending on your computer and connection speed. Hopefully being on an empty region will give one enough edge to get logged in and do some corrective work before being dropped. If not, you will need to contact support for help. They will divide large folders into smaller groups so you can log in.&lt;br /&gt;
&lt;br /&gt;
== [[Land]] ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Maximum [[Land#Parcel|parcel]] size:&#039;&#039;&#039; 65,536 meters²&lt;br /&gt;
** Covering a whole region, or square on the World Map.&lt;br /&gt;
* &#039;&#039;&#039;Minimum parcel size:&#039;&#039;&#039; 16 meters²&lt;br /&gt;
* &#039;&#039;&#039;Maximum parcel name length:&#039;&#039;&#039; 63 single-byte characters&lt;br /&gt;
* &#039;&#039;&#039;Maximum parcel description length:&#039;&#039;&#039; 255 characters&lt;br /&gt;
* &#039;&#039;&#039;Region name length:&#039;&#039;&#039; Linden Concierge policy states a minimum of 3 characters, and a maximum of 25 characters (including spaces). See [[Linden Lab Official:Guidelines for Private Region Naming|Guidelines for Private Region Naming]]. However, the technical maximum limit is 35 characters, and exceptions to the concierge policy do exist (e.g [http://maps.secondlife.com/secondlife/Sandbox%20-%20Weapons%20testing%20%28no%20damag/128/128/2 Sandbox - Weapons testing (no damag] and [http://slurl.com/secondlife/X/128/128/24 X])&lt;br /&gt;
* &#039;&#039;&#039;Ban line height for &amp;quot;no entry&amp;quot; or &amp;quot;pay to access&amp;quot;&#039;&#039;&#039; (in other words, &amp;quot;allow public access&amp;quot; is turned off, or &amp;quot;allow group access&amp;quot; is turned on, in the parcel options) is the parcel&#039;s ground elevation &#039;&#039;plus&#039;&#039; 50 meters (except, if the region has been set to &amp;quot;Block Parcel Fly Over&amp;quot; in which case the access controls extend to at least 4096 meters). If a user is &#039;&#039;&#039;explicitly banned by name&#039;&#039;&#039;, the height is the parcel&#039;s ground elevation &#039;&#039;plus&#039;&#039; 5000 meters. On current viewers this is visible to the full extent.&lt;br /&gt;
* &#039;&#039;&#039;Maximum prims in a [[Land#Region|region]]:&#039;&#039;&#039;&lt;br /&gt;
** &amp;quot;Mainland / Full Region&amp;quot; and &amp;quot;Linden Homes / Full Region&amp;quot;: 22,500&lt;br /&gt;
** Full Regions in private estates: 20,000 with an [https://community.secondlife.com/t5/Featured-News/Upgrade-Your-Full-Private-Region-to-30-000-Land-Impact-Today/ba-p/3086162 option for 30,000]&lt;br /&gt;
** Homestead: 5,000&lt;br /&gt;
** Openspace : 1,000&lt;br /&gt;
:: To check a region&#039;s capacity, use &amp;lt;tt&amp;gt;[[llGetEnv]](&amp;quot;region_max_prims&amp;quot;)&amp;lt;/tt&amp;gt;.&lt;br /&gt;
::&#039;&#039;(Region types are as found in the General tab of World &amp;gt; About Land, or returned by &#039;&#039;&amp;lt;tt&amp;gt;llGetEnv(&amp;quot;region_product_name&amp;quot;)&amp;lt;/tt&amp;gt;&#039;&#039;)&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Maximum auto return value&#039;&#039;&#039;: Besides {{code|0}} (which means &#039;&#039;never&#039;&#039;), {{HoverText|999,999|Almost 23 months}} minutes is the highest auto return value.&lt;br /&gt;
* &#039;&#039;&#039;Terraforming limits:&#039;&#039;&#039;&lt;br /&gt;
** Most mainland can be raised/lowered by 4 meters (+/-).&lt;br /&gt;
** Some mainland regions cannot be terraformed, including: Bay City regions, Boardman, Brown, De Haro, Horizons, Kama City regions, Nautilus City regions, Nova Albion regions, and Shermerville regions.&lt;br /&gt;
** [http://community.secondlife.com/t5/English-Knowledge-Base/Linden-Homes/ta-p/700103 Linden Homes] do not allow terraforming.&lt;br /&gt;
** A few, very old mainland Regions like Da Boom have a terraform range of 40 meters (+/-).&lt;br /&gt;
** Estate (private island) terraformability is settable to a maximum of 100 meters (+/-) by the estate owner or managers.&lt;br /&gt;
* &#039;&#039;&#039;Maximum water height:&#039;&#039;&#039; 100 meters using inworld controls, 255 meters by using a {{code|*.raw}} file upload.&lt;br /&gt;
** Region water height is usually 20 meters, and adjacent regions should have the same water height, or else they will look discontinuous.&lt;br /&gt;
* &#039;&#039;&#039;Maximum terrain height:&#039;&#039;&#039; 255 meters using inworld controls (Region/Estate setting &amp;amp; Edit Terrain tool), 510 meters by using a {{code|*.raw}} file upload.&lt;br /&gt;
* &#039;&#039;&#039;Miscellaneous estate limits:&#039;&#039;&#039; You can have a maximum of 20 estate managers, 500 allowed Residents, 63 allowed groups, and 750 banned Residents.&lt;br /&gt;
** See World menu &amp;gt; Region/Estate &amp;gt; Estate tab.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of agents&#039;&#039;&#039;&lt;br /&gt;
** [https://second.life/eventregions Event Regions]: 175 agents&lt;br /&gt;
** Full region: 100 basic account users plus 10 additional available to premium accounts. (Typically set to 40 on mainland, 55 on Linden Homes regions, but this does vary. Some meeting areas have this set to 60 and higher.)&lt;br /&gt;
*** Recent server performance improvements make regions with 60 agents in them perform quite well.&lt;br /&gt;
** [[Land#Homestead|Homestead]]: 20 (plus 5 additional available to premium accounts)&lt;br /&gt;
** [[Land#Openspace|Openspace]]: 10 (plus 2 additional available to premium accounts)&lt;br /&gt;
* &#039;&#039;&#039;Freeze Time:&#039;&#039;&#039; Land owners can [[freeze]] other Residents for up to 30 seconds.  Members of land-owning groups can also be granted this ability.&lt;br /&gt;
* &#039;&#039;&#039;Minimum parcel that can be listed in Places or All search:&#039;&#039;&#039; 144 meters² &amp;lt;!-- 128 and under are disabled --&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Minimum parcel that can be listed in Events:&#039;&#039;&#039; 512 meters²&lt;br /&gt;
* &#039;&#039;&#039;Maximum altitude for event listings:&#039;&#039;&#039; 768 meters ({{JIRA|WEB-814}})&lt;br /&gt;
* &#039;&#039;&#039;Maximum heights that objects can be seen on the [[World Map]]:&#039;&#039;&#039; 400.005m&lt;br /&gt;
* &#039;&#039;&#039;Maximum height that avatars will be hidden if &#039;Avatars on other parcels can see and chat with avatars on this parcel&#039; is checked:&#039;&#039;&#039; 50m above the ground (see &#039;Ban line height&#039; above)&lt;br /&gt;
&lt;br /&gt;
== [[Mesh]] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Number of vertices&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|65536 vertices per LOD}}&lt;br /&gt;
| per [[mesh]] - See [[Talk:Limits#Mesh_Limits|discussion page]]&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||COLLADA file size&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|8 MB}}&lt;br /&gt;
| Maximum mesh asset size after compression, roughly equivalent to a 256 MB Raw COLLADA file.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Bone influence weights per vertex&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|4}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Bone influence weights per mesh&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|110}}&lt;br /&gt;
| some folk have reported needing to go as low as about 99 in rare cases&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of materials per mesh&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|8}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of materials per face&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Physics weight for a vehicle&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|32.0}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Maximum number of triangles for Animesh&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|100,000}}&lt;br /&gt;
| Server uses estimated triangles (streaming cost calculated), not visual triangles.&lt;br /&gt;
| [[Animesh User Guide|Animesh_User_Guide]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===User Found Temporary Mesh Limits:===&lt;br /&gt;
&#039;&#039;Because of an apparent bug in the importer stay within these limits.&#039;&#039;&lt;br /&gt;
* 174,752 triangles, beyond which additional triangles will appear as holes.&lt;br /&gt;
* The 64k vertices per material is pre-empted by a limit of 21,844 triangles per material, which is presently reported as {{Jira|BUG-1001}}. (4/2014 - See [[Talk:Limits#Mesh_Limits|discussion page]])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:: The import process will continue making new materials beyond 8 x 21,844 (=174,752) triangles, but the extra triangles then get dropped by the limitation to 8 materials, causing holes in the resulting object. Over the 21,844-triangle limit, the vertex count will start to climb steeply, even with smooth shading, because the materials get highly interspersed. So the same vertices have to appear in multiple material lists. So, the moral of the story is to stay below 21,844 triangles per material, for now, if you want to avoid some unexpected effects.&lt;br /&gt;
&lt;br /&gt;
::Since viewer release 3.8.4, processing of meshes in the Collada file that have faces assigned to more than 8 materials has changed. Instead of simply dropping the extra material faces, the uploader now creates a new object to accommodate them. The result is that the single mesh is divided into multiple objects (prims) in a linkset. Thus, the limitation to 8 materials is removed as far as input is concerned, but still applies to each of the resulting linked objects actually uploaded. As a consequence, it is now possible to upload a mesh with more than 174,752 triangles, although it will be divided into multiple objects.&lt;br /&gt;
&lt;br /&gt;
== Misc. ==&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;[https://secondlife.com/currency/describe-limits.php Billing and Trading Limits]&#039;&#039;&#039; - Includes [https://secondlife.com/whatis/economy-market.php LindeX] currency exchange limits.&lt;br /&gt;
&lt;br /&gt;
== Navigation ==&lt;br /&gt;
&lt;br /&gt;
[[File:Max Alt.jpg|thumb|right|435px|&#039;&#039;&#039;Height counter error above 2147483647 meters&#039;&#039;&#039;]]&lt;br /&gt;
* &#039;&#039;&#039;Absolute height limit:&#039;&#039;&#039; [http://en.wikipedia.org/wiki/2147483647#2147483647_in_computing 2147483647]&amp;amp;nbsp;=&amp;amp;nbsp;2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt;&amp;amp;nbsp;−&amp;amp;nbsp;1 meters, which causes the altitude counter to roll over. Altitudes well below this cause graphics errors probably due to limited floating point number precision.&lt;br /&gt;
* &#039;&#039;&#039;Highest z-value of an [[SLurl]], that will still teleport you to a positive altitude:&#039;&#039;&#039; 4096&lt;br /&gt;
** This used to be much higher, but is clamped in late model viewers.&lt;br /&gt;
&lt;br /&gt;
== [[Profile]] ==&lt;br /&gt;
&lt;br /&gt;
Each 7-bit ASCII character is encoded in one byte. International characters might need more bytes. When pasting text instead of typing, you can get in one byte more into each of the below.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;2nd Life tab&#039;s About field&#039;&#039;&#039; - 510 bytes&lt;br /&gt;
* &#039;&#039;&#039;Picks tab&#039;&#039;&#039; - 20 picks with 1022 bytes each&lt;br /&gt;
* &#039;&#039;&#039;1st Life tab&#039;s Info field&#039;&#039;&#039; - 253 bytes&lt;br /&gt;
* &#039;&#039;&#039;Classified tab&#039;&#039;&#039; - 100 listings with x bytes each&lt;br /&gt;
* &#039;&#039;&#039;My Notes&#039;&#039;&#039; - 1022 bytes&lt;br /&gt;
&lt;br /&gt;
== [[Lag|Performance]] ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Healthy Viewer FPS&#039;&#039;&#039; - Generally, FPS equal to or above 24 is good. The higher it gets, the smoother&#039;&#039;&#039;*&#039;&#039;&#039;. You can check via [[Advanced]] menu &amp;gt; Performance Tools &amp;gt; Lag Meter, or for more advanced usage, see Advanced &amp;gt; Performance Tools &amp;gt; Statistics Bar.&lt;br /&gt;
* &#039;&#039;&#039;Avatar Rendering Cost scores&#039;&#039;&#039; - [[Avatar Rendering Cost|Learn all about it!]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;*&#039;&#039;&#039; Only until your framerate reaches your monitor&#039;s refresh rate, after this point there will be very little benefit.&lt;br /&gt;
&lt;br /&gt;
== [[Scripting]] == &amp;lt;!-- Important scripting limits should be placed here, less important ones should be in the articles Caveats or Specification sections --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Height at which scripts reactivate on a no-script parcel&#039;&#039;&#039; - 50 m above terrain mesh. Scripted objects that take controls will keep running when you fly down or enter a no-script parcel. (If scripts are disabled in the region debug panel, they will not run at any height.)&lt;br /&gt;
* &#039;&#039;&#039;Maximum height where scripts can run&#039;&#039;&#039; - none, as long as the object remains rezzed or attached.&lt;br /&gt;
* &#039;&#039;&#039;Maximum script source code size&#039;&#039;&#039; - 65536 single byte characters (that&#039;s a viewer limit and can be changed in the config file &#039;panel_script_ed.xml&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Maximum script memory size (LSO)&#039;&#039;&#039; - 16384 bytes.&lt;br /&gt;
* &#039;&#039;&#039;Maximum script memory size ([[Mono]])&#039;&#039;&#039; - 65536 bytes (the maximum memory available to Mono scripts can be constrained via [[llSetMemoryLimit]]).&lt;br /&gt;
* &#039;&#039;&#039;Maximum memory for [[:Category:LSL LinksetData|Linkset data]]&#039;&#039;&#039; key/value pairs - 128 KiB &lt;br /&gt;
* &#039;&#039;&#039;Maximum memory for [[:Category:Experience Tools|Experience]]&#039;&#039;&#039; key/value pairs- 128 MiB &lt;br /&gt;
* &#039;&#039;&#039;Maximum active [[llListen|listeners]] per script&#039;&#039;&#039; - 65.&lt;br /&gt;
** &#039;&#039;&#039;Usable channel for each listener&#039;&#039;&#039; - &#039;&#039;min.&#039;&#039; -2147483648, &#039;&#039;max.&#039;&#039; 2147483647, including the following special channels: [[PUBLIC_CHANNEL]] &#039;&#039;and&#039;&#039; [[DEBUG_CHANNEL]].&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of scripts that can be rezzed at once&#039;&#039;&#039; – 1,000. (Refer to [https://community.secondlife.com/knowledgebase/english/building-tips-r13/ Building Tips, by Jeremy Linden].)&lt;br /&gt;
* For specific scripting limits, look up calls in the &#039;&#039;&#039;[[LSL Portal]]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== [[Sound]] ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: add link to modemworld.me summary of CCUG from 15/12/22 regarding sound max length --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{KBtip|Second Life will accept sounds that are encoded following &amp;quot;[https://en.wikipedia.org/wiki/Compact_Disc_Digital_Audio CD Quality]&amp;quot; encoding presets present in many {{Abbr|DAW|Digital Audio Workstation}}s.}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Sound length&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|less than 30 seconds or precisely 1,323,000 samples}}&lt;br /&gt;
| The advice is typically to upload sounds less than 30.0 and that 29.99 seconds is fine. However if you do wish to be able upload sounds that are 30 seconds precisely then the .WAV file must have exactly 1,323,000 samples (44,100Hz * 30 seconds). Specifically anything above 1,323,000 samples fails to upload.&lt;br /&gt;
| &#039;&#039;&#039;[https://community.secondlife.com/knowledgebase/english/uploading-assets-r75/ Uploading Assets]&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Bit depth&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|16 bit}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Sample rate&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|44.100 kHz}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Audio channels&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1 channel (mono)}}&lt;br /&gt;
| Channels will be &#039;&#039;&#039;merged&#039;&#039;&#039; at upload time for dual-channel (stereo) audio.&lt;br /&gt;
| n/a&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [[Textures]] ==&lt;br /&gt;
* &#039;&#039;&#039;[http://en.wikipedia.org/wiki/Aspect_ratio Aspect ratios]&#039;&#039;&#039; of profile, place, etc. pictures — all of these were measured at UI size (Edit menu &amp;gt; Preferences &amp;gt; General tab &amp;gt; UI Size) = 1.000:&lt;br /&gt;
&lt;br /&gt;
=== Second Life Viewer 3.6 ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Classifieds thumbnail&#039;&#039;&#039; - ~3:2 (101&amp;amp;times;69 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Classifieds expanded&#039;&#039;&#039; - ~4:3 (159&amp;amp;times;120 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Classifieds expanded &amp;gt; More Info&#039;&#039;&#039; - native aspect ratio&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Destination Guide thumbnail&#039;&#039;&#039; - ~3:2 (101&amp;amp;times;69 pixels) &lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Destination Guide expanded&#039;&#039;&#039; - ~4:3 (159&amp;amp;times;120 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; People&#039;&#039;&#039; - 1:1 (100&amp;amp;times;100 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Places expanded&#039;&#039;&#039; ~4:3 (159&amp;amp;times;120 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Place Profile&#039;&#039;&#039; - ~3:2 (290&amp;amp;times;197 pixels)&lt;br /&gt;
* &#039;&#039;&#039;About Land &amp;gt; Options tab&#039;&#039;&#039; - ~4:3 (195&amp;amp;times;150 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Picture&#039;&#039;&#039; - native aspect ratio; thumbnail cropped to 72&amp;amp;times;72 pixels; zoomed uncropped up to 300&amp;amp;times;300 pixels&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Real world picture&#039;&#039;&#039; - native aspect ratio; thumbnail cropped to 45&amp;amp;times;45 pixels; zoomed uncropped up to 300&amp;amp;times;300 pixels&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Picks thumbnail&#039;&#039;&#039; - ~16:9 (~516&amp;amp;times;~270 pixels as measured on screen; the ratio seems closer to 16:8.5)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Pick expanded&#039;&#039;&#039; - Unknown as of 2022-11-13 (viewer 6.6.7.576223); the Pick image does not expand.&lt;br /&gt;
&lt;br /&gt;
=== 1.x Series Viewers ===&lt;br /&gt;
(official Viewer up to 1.23.5, still used by some [[Alternate viewers|Third Party Viewers]])&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; All for &amp;quot;Classifieds&amp;quot;, &amp;quot;People&amp;quot;, and &amp;quot;Places&amp;quot;&#039;&#039;&#039; - 4:3 (256&amp;amp;times;192 pixels)&amp;lt;ref&amp;gt;As of {{#time: r|2024-02-26T18:00:00}}, this seems to be one of the very few sizes supported by the [[Picture Service]].&amp;lt;/ref&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Places and Classified tabs&#039;&#039;&#039; - ~7:5 (398&amp;amp;times;282 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Land tab&#039;&#039;&#039; - ~7:5 (358&amp;amp;times;252 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; 2nd Life tab&#039;&#039;&#039; - ~4:3 (178&amp;amp;times;133 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Picks tab&#039;&#039;&#039; - 16:9 (288&amp;amp;times;162 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; 1st Life tab&#039;&#039;&#039; - 1:1 (133&amp;amp;times;133 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Classifieds tab&#039;&#039;&#039; - ~3:2 (206&amp;amp;times;137 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Web tab&#039;&#039;&#039; - 1:1 (400&amp;amp;times;400 pixels)&lt;br /&gt;
** A scrollbar uses 15 pixels on the right-hand side.&lt;br /&gt;
* &#039;&#039;&#039;About Land &amp;gt; Options tab&#039;&#039;&#039; - ~3:2 (178&amp;amp;times;117 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Group Information &amp;gt; General tab&#039;s &amp;quot;Group Insignia&amp;quot;&#039;&#039;&#039; - 1:1 (126&amp;amp;times;126 pixels)&lt;br /&gt;
** Some of these textures are shared (for example, Search &amp;gt; All place pages, Search &amp;gt; Places, and About Land &amp;gt; Options use the same image), so you should pick a well-balanced ratio and size.&lt;br /&gt;
** [[Texture_aspect_ratios|Learn how to get correct texture aspect ratios when editing images.]]&lt;br /&gt;
&lt;br /&gt;
=== All Viewers ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Maximum texture size&#039;&#039;&#039; - 2048&amp;amp;times;2048 pixels&lt;br /&gt;
** All Second Life textures are constrained to [http://en.wikipedia.org/wiki/Power_of_2 powers of 2] (e.g., 128, 256, 512).&lt;br /&gt;
** We strongly recommend you use as small textures as possible because larger ones consume more memory and take substantially longer to load.&lt;br /&gt;
** Where large textures are being forced by import to only 512&amp;amp;times;512, lower your ...&amp;gt; Preferences &amp;gt;...&amp;gt; UI Size under 1.0, to increase import size to the max 2048&amp;amp;times;2048.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Minimum texture size&#039;&#039;&#039; - 4&amp;amp;times;4 pixels&lt;br /&gt;
** This means that there are ten possible image dimensions: 4, 8, 16, 32, 64, 128, 256, 512, 1024 &amp;amp; 2048 pixels, either horizontal or vertical.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Texture resizing&#039;&#039;&#039;&lt;br /&gt;
** Uploaded textures will be resized to the dimensions above. &lt;br /&gt;
** Each dimension is resized independently (for instance, a 1024 x 999 image will be resized to 1024 x 1024).&lt;br /&gt;
** A dimension that is already a power of two will not be resized.&lt;br /&gt;
** The uploader “prefers” to round down rather than up. The threshold for rounding down/up is .75 of the distance between powers of two. &lt;br /&gt;
*** For example, given a dimension of x, if x / 512 is larger than 1.75, x will be resized to 1024, else 512.&lt;br /&gt;
*** Another example:  If x / 256 is larger than 1.75, x will be resized to 512. &lt;br /&gt;
&lt;br /&gt;
[[Category:Creation]] [[Category:Tutorials]] [[Category:Lists]]&lt;br /&gt;
&lt;br /&gt;
=== Notes ===&lt;br /&gt;
In general, 1 byte is enough to contain one character.&amp;lt;ref&amp;gt;Except in .NET-based applications, and those related to .NET development — which is the case of the SL viewer — which essentially use UTF-16 everywhere, i.e., every character is encoded in &#039;&#039;two&#039;&#039; bytes, not just one.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Text using Unicode characters (including, but not limited to, emojis), where supported, will require 1–4 bytes (depending on the encoding).&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
{{References}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Limits&amp;diff=1218514</id>
		<title>Limits</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Limits&amp;diff=1218514"/>
		<updated>2025-11-16T19:03:36Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: /* Lighting */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;{{KBmaster}}&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
== Summary ==&lt;br /&gt;
{{RightToc}}&lt;br /&gt;
The Second Life Viewer and simulators have a division of labor, keeping track of the data that makes Second Life run. The Viewer&#039;s job is to:&lt;br /&gt;
&lt;br /&gt;
* Handle locations of objects.&lt;br /&gt;
* Get velocities and other physics information, and does simple physics to keep track of what is moving where.&lt;br /&gt;
&lt;br /&gt;
The simulator&#039;s job is to:&lt;br /&gt;
&lt;br /&gt;
* Run the physics engine.&lt;br /&gt;
* Detect collisions.&lt;br /&gt;
* Keep track of where everything is&lt;br /&gt;
* Send locations of content to the Viewer along with updates when certain changes occur.&lt;br /&gt;
&lt;br /&gt;
Limits are necessary for all of these components to work together. The list below outlines many of the &#039;&#039;&#039;Second Life numerical limits that affect your inworld experience&#039;&#039;&#039;. Some of these will likely change over time, so if you spot something incorrect/outdated, please take a moment to update it.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|1=The scope of this page is focused on Linden Lab&#039;s official Viewers, keeping in mind [[Third_Party_Viewer_Directory|3rd-party viewers may differ]]. Cite sources and provide substantiation for limits that aren&#039;t provided by Linden Lab. Also, unconfirmed speculation [[Talk:Limits|goes on this discussion page]]. Don&#039;t add data without a hard limit, and don&#039;t add obscure trivia that doesn&#039;t practically affect the general inworld experience.|width=80%}}&lt;br /&gt;
&lt;br /&gt;
== [[Avatar]] ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Max. # of attachments&#039;&#039;&#039; - 38 combined [[HUD]] and body attachments.&lt;br /&gt;
** They can be viewed by right-clicking your avatar and choosing &#039;&#039;&#039;Edit My Outfit&#039;&#039;&#039;.&lt;br /&gt;
** With the formal introduction of [[multiple attachments|multiple attachments to a single point]] in Viewer 2.4, you can attach up to 38 total objects, and they can all be attached to a single point. &lt;br /&gt;
** With the introduction of the Bento armature, there are 47 attachment points available.&lt;br /&gt;
** With the introduction of Animesh, there is a limit of 1 Animesh attachment for Basic &amp;amp; Plus members, 2 for Premium members, and 3 for Premium Plus members.&lt;br /&gt;
* &#039;&#039;&#039;Max. attachment position&#039;&#039;&#039; - 3.5m in a sphere around &amp;lt;0, 0, 0&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Max. # of clothing layers&#039;&#039;&#039; - 60 including alpha, tattoo, shoe base, physics, socks, gloves, undershirt, underpants, shirt, pants, jacket, skirt&lt;br /&gt;
** On viewers before Second Life Release 3.7.29.301305, the potential total was still 60, but limited to 5 items of each type.&lt;br /&gt;
** In any case, only one each of shape, skin, eyes and hair base may be worn. These 4 basic body parts are in addition to the 60 clothing layers.&lt;br /&gt;
* &#039;&#039;&#039;Max Clothing Texture&#039;&#039;&#039; - 1024x1024 px (except 128x128 px for eyes) - Server Side Appearance (and legacy baking on outside grids) will downsize larger textures*. (Support for 2K textures &amp;amp; Bakes on Mesh is in development but not yet supported)&lt;br /&gt;
* &#039;&#039;&#039;Max. unassisted fly height&#039;&#039;&#039; - Stable hover at current ground elevation &#039;&#039;plus&#039;&#039; 5020m.&lt;br /&gt;
** You can easily acquire a flight attachment to fly high. Also see [[flight limit]] and [[Limits#Navigation|the Navigation section]] below.&lt;br /&gt;
* &#039;&#039;&#039;Common shoe sizes&#039;&#039;&#039; - Traditionally, most women&#039;s shoes were designed for a size 0 (zero) foot. Men&#039;s generally scale up to 14. Newer items (mesh and some sculpted) replace the system feet or conform to the avatar&#039;s shape, so adjustments may be necessary.&lt;br /&gt;
** You can check this by going to Edit menu &amp;gt; Appearance and clicking on the Shape &amp;gt; Legs tab.&lt;br /&gt;
&amp;lt;!--Can someone verify the following?&lt;br /&gt;
* &#039;&#039;&#039;Min. length of avatar name&#039;&#039;&#039; - 2 for the first name, 2 for the last name, 5 in total (including the space).--&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Max. length of avatar name&#039;&#039;&#039; - 31 for first name, 31 for last name, 63 in total (including the space).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Max. length of {{LSLGC|Username}}&#039;&#039;&#039; - All lower case and a maximum of 63 including the &#039;.&#039;(eg; firstname.lastname)&lt;br /&gt;
**Older account user names are a combination of the First and last name in lower case separated by a period(.)(eg; lilmix.pixelmaid)&lt;br /&gt;
**Modern user names are a single name(no period)(eg; john1234) - &#039;&#039;&#039;Max.&#039;&#039;&#039; length is 31 characters&lt;br /&gt;
**The {{LSLGC|Legacy Name}}s for modern user names always shows Resident as the last name. So the Max is 40 characters in total for a new avatar&#039;s legacy name(including the space and last name &#039;Resident&#039;)&lt;br /&gt;
*&#039;&#039;&#039;Min. length of [[Display_Names]]&#039;&#039;&#039; - At least one script character&lt;br /&gt;
*&#039;&#039;&#039;Max. length of Display Names&#039;&#039;&#039; - 31 characters&lt;br /&gt;
**Display Names can include most Unicode script characters, spaces, and some punctuation.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Max. avatar speed&#039;&#039;&#039; - 250m/s (with only attachments to assist)&lt;br /&gt;
* &#039;&#039;&#039;Max. teleport speed&#039;&#039;&#039; - 6 teleports per minute&lt;br /&gt;
&lt;br /&gt;
== [[Animation]] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Length of animation&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|60.0 seconds}}&lt;br /&gt;
| Limited by length or Size, whichever is reached first&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Size of Animation&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|{{HoverText|250,000 bytes|250 KB (decimal), 244 KB (binary)}}}}&lt;br /&gt;
| Limited by length or Size, whichever is reached first. Size is after conversion to [[Internal Animation Format|.anim]]. {{KBtip|custom=Tip for Blender users:|1=You can use a built-in function called &amp;quot;&#039;&#039;Decimate&#039;&#039;&amp;quot; to simplify your animation. See [https://www.youtube.com/watch?v=lScwEYJZy1M this tutorial]}}&lt;br /&gt;
| [[Internal Animation Format]]&lt;br /&gt;
|-&lt;br /&gt;
||Distance an Animation Can Travel&lt;br /&gt;
| {{no|-5 meters (per axis)}}&lt;br /&gt;
| {{no|+5 meters (per axis)}}&lt;br /&gt;
| 10 meters per axis cumulatively (travelling from -5 to +5).&lt;br /&gt;
| n/a&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||animation priority&lt;br /&gt;
| {{no|0}}&lt;br /&gt;
| {{no|.bvh: 4, [[Internal Animation Format|.anim]]: 6}}&lt;br /&gt;
| [[Internal Animation Format|.anim]] uses per-joint priorities, meaning it&#039;s possible to have a mix of joint priorities in the same animation.&lt;br /&gt;
| [[Animation_Priority|Animation Priority]]&lt;br /&gt;
|-&lt;br /&gt;
||Number of unique joints per animation&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|216}}&lt;br /&gt;
| [[Internal Animation Format|.anim]], unlike .bvh, uses a flat structure, meaning that only the bones animated are stored, and bones which would otherwise be kept by .bvh (to keep hierarchy structure) are deleted.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of simultaneous animations playing&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|30}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [http://community.secondlife.com/t5/English-Knowledge-Base/Build-Tools/ta-p/700039 Building] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||[[Prim]] dimensions&lt;br /&gt;
| {{no|&amp;lt;0.01,&amp;amp;nbsp;0.01,&amp;amp;nbsp;0.01&amp;gt;}}&lt;br /&gt;
| {{no|&amp;lt;64.0, 64.0, 64.0&amp;gt;}}&lt;br /&gt;
| Used to be &amp;lt;10.0, 10.0, 10.0&amp;gt; prior to Second Life Server version 11.08.17.238770&lt;br /&gt;
| [[llSetScale]], [[PRIM_SIZE]] and [[Megaprim]]&lt;br /&gt;
|-&lt;br /&gt;
||Number of prims in a linkset&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|256 prims}}&lt;br /&gt;
| Used to be 255 prior to Second Life Server version 1.26&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Linking distance&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|64 meters}}&lt;br /&gt;
| distance has changed on [https://community.secondlife.com/blogs/entry/13626-coming-soon-server-release-202306/ 2023.06 update]. The old max distance was 54 meters.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Local offset (avatars)&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1000 meters per axis}}&lt;br /&gt;
| Unlike regular [[Linkability_Rules|Linkability Rules]] for linksets, an avatar sitting on an object may be moved significantly further by scripts.&lt;br /&gt;
| [[llSetLinkPrimitiveParams]] with [[PRIM_POS_LOCAL]]&lt;br /&gt;
|-&lt;br /&gt;
||[[Mesh/Mesh_physics#Physics_Resource_Cost|Physics cost]] for a physical object&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|32.0}}&lt;br /&gt;
||Additional notes:&lt;br /&gt;
* If you try to link more, it&#039;ll either say &amp;quot;Can&#039;t enable physics for objects with more than 32 primitives&amp;quot; or &amp;quot;Object has too many primitives -- its dynamics have been disabled.&amp;quot;.&lt;br /&gt;
* On server versions 1.38 and below, each sitting avatar counted as 1 prim. From 1.40.2 the limit is only on actual prims.&lt;br /&gt;
| [[OBJECT_PHYSICS_COST]]&lt;br /&gt;
|-&lt;br /&gt;
||Build or rez height&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|4096.0 meters}}&lt;br /&gt;
| 768.0 meters for Viewers prior to version 1.20&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Prim name length&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|63 bytes UTF-8 [[String|string]]}}&lt;br /&gt;
| same limit for {{LSLGC|Avatar/Name|avatar names}}, although the character set for avatar names is significantly limited.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Prim description length&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|127 bytes UTF-8 [[String|string]]}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Temporary prims&#039; lifetime&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|up to around 60 seconds}}&lt;br /&gt;
| depends upon when the next garbage collector cycle is&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of temporary prims&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|RegionPrimLimit - NumberOfPrimsInRegion + Minimum(0.5*RegionPrimLimit, 1000)}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Length of hovertext&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|254 bytes UTF-8 [[String|string]]}}&lt;br /&gt;
| n/a&lt;br /&gt;
| [[llSetText]] and [[PRIM_TEXT]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [https://community.secondlife.com/knowledgebase/english/lighting-and-shadows-r331/ Lighting] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Number of active light sources&lt;br /&gt;
| n/a&lt;br /&gt;
| no limit&lt;br /&gt;
| Advanced Lighting Model (ALM) was developed around 2011-2013 to remove the hard limit on light sources.&lt;br /&gt;
|-&lt;br /&gt;
||Number of projectors rendered at full detail&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|2}}&lt;br /&gt;
| Only the 2 projectors closest to an object will cause it to cast shadows. Additional projectors still affect color.&lt;br /&gt;
| [https://community.secondlife.com/knowledgebase/english/lighting-and-shadows-r331/#Projectors Knowledge Base &amp;gt; Lighting &amp;gt; Projectors]&lt;br /&gt;
|-&lt;br /&gt;
||Number of lights rendered on Alpha Blended surfaces&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|8}}&lt;br /&gt;
| 8 including the Sun and Moon. The viewer will attempt to select the &amp;quot;most significant&amp;quot; lights to render, however these blended surfaces will look different as a result of these lighting limitations.&lt;br /&gt;
| [[Alpha Modes Do&#039;s and Don&#039;ts#Alpha_blending|Alpha Blending]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [[Communication]] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Whisper distance&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|10 meters}}&lt;br /&gt;
| &amp;lt;code&amp;gt;/whisper&amp;lt;/code&amp;gt; as the first few characters in a message can be used in viewer-2-code based viewers in local chat.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Chat distance&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|20 meters}}&lt;br /&gt;
| text spoken as a &amp;quot;chat&amp;quot; step &#039;&#039;&#039;within gestures&#039;&#039;&#039; cannot be shouted or whispered. /whisper and /shout &#039;&#039;can&#039;&#039; be used in the &amp;quot;Replace with&amp;quot; field.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Shout distance&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|100 meters}}&lt;br /&gt;
| &amp;lt;code&amp;gt;/shout&amp;lt;/code&amp;gt; as the first few characters in a message can be used in viewer-2-code based viewers in local chat.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Length of a chat message and whether or not it is truncated&lt;br /&gt;
| {{no|1 byte or single-byte character}}&lt;br /&gt;
| {{no|1024 bytes or single-byte characters}}&lt;br /&gt;
| Limit does not apply to instant messages via [[llInstantMessage]] and communication between IM and email, the limit there is 1023 bytes or single-byte characters.&lt;br /&gt;
The viewer-to-server communication on &#039;&#039;negative&#039;&#039; channels is truncated to 254 bytes, and on &#039;&#039;positive&#039;&#039; channels, to 1023 bytes. These limits do not affect communication between scripts.&lt;br /&gt;
| [[llInstantMessage]]&lt;br /&gt;
|-&lt;br /&gt;
||Maximum events&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|64 events}}&lt;br /&gt;
| Events are stored in a 64 bit bitmap. Events happening when the bitmap is full, will get discarded.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of offline messages&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|Capped at 30 messages for Basic members, 40 for Plus members, 120 for Premium members, and 240 for Premium Plus members.}}&lt;br /&gt;
| Number of offline messages (involving [[IM|IMs]], inventory offers, group notices, group invitations) received before messages get capped. Note: If autoAcceptNewInventory (debug setting) is set to TRUE (the default is FALSE), then all inventory offers, even above the cap allowed by membership, go directly to inventory and do not count against the cap on offline messages. &lt;br /&gt;
| [[IM]] - [https://community.secondlife.com/blogs/entry/2559-group-limits-update-no-changes-for-basic-members/ Blog Post]&lt;br /&gt;
|-&lt;br /&gt;
||Length of a properly delivered email reply to an [[IM]]&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1023 bytes or single-byte characters}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Time after which a reply to an [[IM]] can be sent via email.&lt;br /&gt;
| {{no|when receiving the IM via email.}}&lt;br /&gt;
| {{no|5 days after receiving the offline IM via email.}}&lt;br /&gt;
| n/a&lt;br /&gt;
| [https://blogs.secondlife.com/community/features/blog/2007/02/06/im-to-e-mail-return-addresses-changing Second Life Blogs]&lt;br /&gt;
|-&lt;br /&gt;
||Time period for which [[IM|IMs]] are stored within Second Life.&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|31 days after receiving the [[IM]]}}&lt;br /&gt;
| [[User:Torley_Linden|Torley Linden]] confirmed this with [[User:Kelly_Linden|Kelly Linden]]&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of [[IM|IMs]] sent by an object within an hour&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|5000 per hour}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [[Gesture]]s ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Shortcut key mappings&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|33 unique combinations, since {{K|F2}}&amp;amp;ndash;{{K|F12}} can be used with the {{K|Ctrl}} or {{K|Shift}} modifiers.}}&lt;br /&gt;
| n/a&lt;br /&gt;
| [[All_keyboard_shortcut_keys]] and [http://community.secondlife.com/t5/English-Knowledge-Base/Keyboard-shortcuts/ta-p/1086557 the knowledge base]&lt;br /&gt;
|-&lt;br /&gt;
||Chat step length&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|127 bytes or single-byte characters}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Wait time&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|3600 seconds (which is one hour)}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
| Maximum steps data&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1000 bytes}} of packed data; saving with more data returns the message &amp;quot;Gesture save failed. This gesture has too many steps. Try removing some steps, then save again.&amp;quot; Shorter text, for example, will allow more steps.&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Length of included assets&lt;br /&gt;
| n/a&lt;br /&gt;
| see sections for [[#Animation|animation]], [[#Sound|sound]]&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [[Group]]s ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of groups you can belong to:&#039;&#039;&#039; 50 for Basic members, 55 for Plus members, 80 for Premium members, 150 for Premium Plus members.&lt;br /&gt;
** After downgrading from a higher limit membership to a lower one, you may not join any new groups until you are a member of fewer than your current subscriptions&#039;s group limit. For example, if you downgrade from Premium (70 groups allowed) to Basic (42 groups allowed), you can&#039;t join any new groups until you&#039;re a member of fewer than 42 groups.&lt;br /&gt;
** Roles within groups are sort of like sub-groups. In many cases, you can use them instead of creating new groups.&lt;br /&gt;
* &#039;&#039;&#039;Minimum number of members in a group:&#039;&#039;&#039; 2&lt;br /&gt;
** A group with only 1 person for 48 hours will be disbanded (cancelled). Unless the group owns land.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of roles allowed in a group:&#039;&#039;&#039; 10 (including &amp;quot;Owners&amp;quot; and &amp;quot;Everyone&amp;quot;, which cannot be deleted)&lt;br /&gt;
* &#039;&#039;&#039;Maximum Group Name:&#039;&#039;&#039; 35 single-byte characters.&lt;br /&gt;
* &#039;&#039;&#039;Maximum Group Title:&#039;&#039;&#039; 20 single-byte characters.&lt;br /&gt;
* &#039;&#039;&#039;Maximum Length of a Group Notice:&#039;&#039;&#039; 512 single-byte characters.&lt;br /&gt;
* &#039;&#039;&#039;Maximum Age of a Group Notice:&#039;&#039;&#039; 14 days.&lt;br /&gt;
&lt;br /&gt;
== [[Inventory]] ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of inventory items that can be sent in a folder:&#039;&#039;&#039; 42&lt;br /&gt;
** Folders count as items too. This has more to do with packet size limits than cheeky Douglas Adams references.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of accounts that an inventory item can be shared with at once:&#039;&#039;&#039; The limit is unknown, but tests show that it is more than 35. No documentation has been found. One third-party viewer developer says that the code doesn&#039;t seem to look for a limit. It just builds the list and sends it to each avatar individually.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of inventory items that can be contained in a prim:&#039;&#039;&#039; 10,000&lt;br /&gt;
* &#039;&#039;&#039;Maximum characters in an inventory item name:&#039;&#039;&#039; 63&lt;br /&gt;
* &#039;&#039;&#039;Maximum notecard line:&#039;&#039;&#039; None, but scripts can only read the first 1024 bytes (was 255 bytes before server version [https://releasenotes.secondlife.com/simulator/2021-10-25.565008.html 2021-10-25.565008]).&lt;br /&gt;
* &#039;&#039;&#039;Maximum notecard size:&#039;&#039;&#039; 65,536 bytes&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
* &#039;&#039;&#039;Number of items in the Library:&#039;&#039;&#039; 1,248 as of 2010-06-17&lt;br /&gt;
* &#039;&#039;&#039;Number of items in the Library:&#039;&#039;&#039; 2,210 as of 2012-11-04 --&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Number of items in the Library:&#039;&#039;&#039; 3,500 as of 2019-08-10&lt;br /&gt;
** Verified by [[How_does_the_Library_work|hiding the Library]] and counting the difference.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of inventory items displayed in a single inventory folder:&#039;&#039;&#039; Several thousand. &amp;lt;!-- not a specific item count, because the data is variable size, but still useful to document that there is a limit. --&amp;gt;&lt;br /&gt;
** This was discussed during [http://wiki.secondlife.com/wiki/Simulator_User_Group/Transcripts/2011.03.08] &amp;quot;the folder will load up to the limit number of items, and remaining ones won&#039;t show up in the viewer. you still own them, they aren&#039;t lost, but they will be hidden until the folder size is reduced.&amp;quot;&lt;br /&gt;
** There is no specified limit for total avatar inventory, very large folders can be split.&lt;br /&gt;
** There was discussion in the Third Party Developers&#039; UG (4/10/2015) meeting about new inventory and login problems from having large numbers of items in a single folder. AISv3 &#039;&#039;removed&#039;&#039; server-side limits on the number of items in a folder. Flat inventories are bad. No one is certain at what number of items in a folder cause a login problem. The fix is to clear the inventory cache (not the viewer cache) and log into a deserted, empty region then move inventory items into folders and sub-folders. - The problem appears at different a number of items depending on your computer and connection speed. Hopefully being on an empty region will give one enough edge to get logged in and do some corrective work before being dropped. If not, you will need to contact support for help. They will divide large folders into smaller groups so you can log in.&lt;br /&gt;
&lt;br /&gt;
== [[Land]] ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Maximum [[Land#Parcel|parcel]] size:&#039;&#039;&#039; 65,536 meters²&lt;br /&gt;
** Covering a whole region, or square on the World Map.&lt;br /&gt;
* &#039;&#039;&#039;Minimum parcel size:&#039;&#039;&#039; 16 meters²&lt;br /&gt;
* &#039;&#039;&#039;Maximum parcel name length:&#039;&#039;&#039; 63 single-byte characters&lt;br /&gt;
* &#039;&#039;&#039;Maximum parcel description length:&#039;&#039;&#039; 255 characters&lt;br /&gt;
* &#039;&#039;&#039;Region name length:&#039;&#039;&#039; Linden Concierge policy states a minimum of 3 characters, and a maximum of 25 characters (including spaces). See [[Linden Lab Official:Guidelines for Private Region Naming|Guidelines for Private Region Naming]]. However, the technical maximum limit is 35 characters, and exceptions to the concierge policy do exist (e.g [http://maps.secondlife.com/secondlife/Sandbox%20-%20Weapons%20testing%20%28no%20damag/128/128/2 Sandbox - Weapons testing (no damag] and [http://slurl.com/secondlife/X/128/128/24 X])&lt;br /&gt;
* &#039;&#039;&#039;Ban line height for &amp;quot;no entry&amp;quot; or &amp;quot;pay to access&amp;quot;&#039;&#039;&#039; (in other words, &amp;quot;allow public access&amp;quot; is turned off, or &amp;quot;allow group access&amp;quot; is turned on, in the parcel options) is the parcel&#039;s ground elevation &#039;&#039;plus&#039;&#039; 50 meters (except, if the region has been set to &amp;quot;Block Parcel Fly Over&amp;quot; in which case the access controls extend to at least 4096 meters). If a user is &#039;&#039;&#039;explicitly banned by name&#039;&#039;&#039;, the height is the parcel&#039;s ground elevation &#039;&#039;plus&#039;&#039; 5000 meters. On current viewers this is visible to the full extent.&lt;br /&gt;
* &#039;&#039;&#039;Maximum prims in a [[Land#Region|region]]:&#039;&#039;&#039;&lt;br /&gt;
** &amp;quot;Mainland / Full Region&amp;quot; and &amp;quot;Linden Homes / Full Region&amp;quot;: 22,500&lt;br /&gt;
** Full Regions in private estates: 20,000 with an [https://community.secondlife.com/t5/Featured-News/Upgrade-Your-Full-Private-Region-to-30-000-Land-Impact-Today/ba-p/3086162 option for 30,000]&lt;br /&gt;
** Homestead: 5,000&lt;br /&gt;
** Openspace : 1,000&lt;br /&gt;
:: To check a region&#039;s capacity, use &amp;lt;tt&amp;gt;[[llGetEnv]](&amp;quot;region_max_prims&amp;quot;)&amp;lt;/tt&amp;gt;.&lt;br /&gt;
::&#039;&#039;(Region types are as found in the General tab of World &amp;gt; About Land, or returned by &#039;&#039;&amp;lt;tt&amp;gt;llGetEnv(&amp;quot;region_product_name&amp;quot;)&amp;lt;/tt&amp;gt;&#039;&#039;)&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Maximum auto return value&#039;&#039;&#039;: Besides {{code|0}} (which means &#039;&#039;never&#039;&#039;), {{HoverText|999,999|Almost 23 months}} minutes is the highest auto return value.&lt;br /&gt;
* &#039;&#039;&#039;Terraforming limits:&#039;&#039;&#039;&lt;br /&gt;
** Most mainland can be raised/lowered by 4 meters (+/-).&lt;br /&gt;
** Some mainland regions cannot be terraformed, including: Bay City regions, Boardman, Brown, De Haro, Horizons, Kama City regions, Nautilus City regions, Nova Albion regions, and Shermerville regions.&lt;br /&gt;
** [http://community.secondlife.com/t5/English-Knowledge-Base/Linden-Homes/ta-p/700103 Linden Homes] do not allow terraforming.&lt;br /&gt;
** A few, very old mainland Regions like Da Boom have a terraform range of 40 meters (+/-).&lt;br /&gt;
** Estate (private island) terraformability is settable to a maximum of 100 meters (+/-) by the estate owner or managers.&lt;br /&gt;
* &#039;&#039;&#039;Maximum water height:&#039;&#039;&#039; 100 meters using inworld controls, 255 meters by using a {{code|*.raw}} file upload.&lt;br /&gt;
** Region water height is usually 20 meters, and adjacent regions should have the same water height, or else they will look discontinuous.&lt;br /&gt;
* &#039;&#039;&#039;Maximum terrain height:&#039;&#039;&#039; 255 meters using inworld controls (Region/Estate setting &amp;amp; Edit Terrain tool), 510 meters by using a {{code|*.raw}} file upload.&lt;br /&gt;
* &#039;&#039;&#039;Miscellaneous estate limits:&#039;&#039;&#039; You can have a maximum of 20 estate managers, 500 allowed Residents, 63 allowed groups, and 750 banned Residents.&lt;br /&gt;
** See World menu &amp;gt; Region/Estate &amp;gt; Estate tab.&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of agents&#039;&#039;&#039;&lt;br /&gt;
** [https://second.life/eventregions Event Regions]: 175 agents&lt;br /&gt;
** Full region: 100 basic account users plus 10 additional available to premium accounts. (Typically set to 40 on mainland, 55 on Linden Homes regions, but this does vary. Some meeting areas have this set to 60 and higher.)&lt;br /&gt;
*** Recent server performance improvements make regions with 60 agents in them perform quite well.&lt;br /&gt;
** [[Land#Homestead|Homestead]]: 20 (plus 5 additional available to premium accounts)&lt;br /&gt;
** [[Land#Openspace|Openspace]]: 10 (plus 2 additional available to premium accounts)&lt;br /&gt;
* &#039;&#039;&#039;Freeze Time:&#039;&#039;&#039; Land owners can [[freeze]] other Residents for up to 30 seconds.  Members of land-owning groups can also be granted this ability.&lt;br /&gt;
* &#039;&#039;&#039;Minimum parcel that can be listed in Places or All search:&#039;&#039;&#039; 144 meters² &amp;lt;!-- 128 and under are disabled --&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Minimum parcel that can be listed in Events:&#039;&#039;&#039; 512 meters²&lt;br /&gt;
* &#039;&#039;&#039;Maximum altitude for event listings:&#039;&#039;&#039; 768 meters ({{JIRA|WEB-814}})&lt;br /&gt;
* &#039;&#039;&#039;Maximum heights that objects can be seen on the [[World Map]]:&#039;&#039;&#039; 400.005m&lt;br /&gt;
* &#039;&#039;&#039;Maximum height that avatars will be hidden if &#039;Avatars on other parcels can see and chat with avatars on this parcel&#039; is checked:&#039;&#039;&#039; 50m above the ground (see &#039;Ban line height&#039; above)&lt;br /&gt;
&lt;br /&gt;
== [[Mesh]] ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Number of vertices&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|65536 vertices per LOD}}&lt;br /&gt;
| per [[mesh]] - See [[Talk:Limits#Mesh_Limits|discussion page]]&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||COLLADA file size&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|8 MB}}&lt;br /&gt;
| Maximum mesh asset size after compression, roughly equivalent to a 256 MB Raw COLLADA file.&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Bone influence weights per vertex&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|4}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Bone influence weights per mesh&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|110}}&lt;br /&gt;
| some folk have reported needing to go as low as about 99 in rare cases&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of materials per mesh&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|8}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Number of materials per face&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Physics weight for a vehicle&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|32.0}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Maximum number of triangles for Animesh&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|100,000}}&lt;br /&gt;
| Server uses estimated triangles (streaming cost calculated), not visual triangles.&lt;br /&gt;
| [[Animesh User Guide|Animesh_User_Guide]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===User Found Temporary Mesh Limits:===&lt;br /&gt;
&#039;&#039;Because of an apparent bug in the importer stay within these limits.&#039;&#039;&lt;br /&gt;
* 174,752 triangles, beyond which additional triangles will appear as holes.&lt;br /&gt;
* The 64k vertices per material is pre-empted by a limit of 21,844 triangles per material, which is presently reported as {{Jira|BUG-1001}}. (4/2014 - See [[Talk:Limits#Mesh_Limits|discussion page]])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:: The import process will continue making new materials beyond 8 x 21,844 (=174,752) triangles, but the extra triangles then get dropped by the limitation to 8 materials, causing holes in the resulting object. Over the 21,844-triangle limit, the vertex count will start to climb steeply, even with smooth shading, because the materials get highly interspersed. So the same vertices have to appear in multiple material lists. So, the moral of the story is to stay below 21,844 triangles per material, for now, if you want to avoid some unexpected effects.&lt;br /&gt;
&lt;br /&gt;
::Since viewer release 3.8.4, processing of meshes in the Collada file that have faces assigned to more than 8 materials has changed. Instead of simply dropping the extra material faces, the uploader now creates a new object to accommodate them. The result is that the single mesh is divided into multiple objects (prims) in a linkset. Thus, the limitation to 8 materials is removed as far as input is concerned, but still applies to each of the resulting linked objects actually uploaded. As a consequence, it is now possible to upload a mesh with more than 174,752 triangles, although it will be divided into multiple objects.&lt;br /&gt;
&lt;br /&gt;
== Misc. ==&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;[https://secondlife.com/currency/describe-limits.php Billing and Trading Limits]&#039;&#039;&#039; - Includes [https://secondlife.com/whatis/economy-market.php LindeX] currency exchange limits.&lt;br /&gt;
&lt;br /&gt;
== Navigation ==&lt;br /&gt;
&lt;br /&gt;
[[File:Max Alt.jpg|thumb|right|435px|&#039;&#039;&#039;Height counter error above 2147483647 meters&#039;&#039;&#039;]]&lt;br /&gt;
* &#039;&#039;&#039;Absolute height limit:&#039;&#039;&#039; [http://en.wikipedia.org/wiki/2147483647#2147483647_in_computing 2147483647]&amp;amp;nbsp;=&amp;amp;nbsp;2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt;&amp;amp;nbsp;−&amp;amp;nbsp;1 meters, which causes the altitude counter to roll over. Altitudes well below this cause graphics errors probably due to limited floating point number precision.&lt;br /&gt;
* &#039;&#039;&#039;Highest z-value of an [[SLurl]], that will still teleport you to a positive altitude:&#039;&#039;&#039; 4096&lt;br /&gt;
** This used to be much higher, but is clamped in late model viewers.&lt;br /&gt;
&lt;br /&gt;
== [[Profile]] ==&lt;br /&gt;
&lt;br /&gt;
Each 7-bit ASCII character is encoded in one byte. International characters might need more bytes. When pasting text instead of typing, you can get in one byte more into each of the below.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;2nd Life tab&#039;s About field&#039;&#039;&#039; - 510 bytes&lt;br /&gt;
* &#039;&#039;&#039;Picks tab&#039;&#039;&#039; - 20 picks with 1022 bytes each&lt;br /&gt;
* &#039;&#039;&#039;1st Life tab&#039;s Info field&#039;&#039;&#039; - 253 bytes&lt;br /&gt;
* &#039;&#039;&#039;Classified tab&#039;&#039;&#039; - 100 listings with x bytes each&lt;br /&gt;
* &#039;&#039;&#039;My Notes&#039;&#039;&#039; - 1022 bytes&lt;br /&gt;
&lt;br /&gt;
== [[Lag|Performance]] ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Healthy Viewer FPS&#039;&#039;&#039; - Generally, FPS equal to or above 24 is good. The higher it gets, the smoother&#039;&#039;&#039;*&#039;&#039;&#039;. You can check via [[Advanced]] menu &amp;gt; Performance Tools &amp;gt; Lag Meter, or for more advanced usage, see Advanced &amp;gt; Performance Tools &amp;gt; Statistics Bar.&lt;br /&gt;
* &#039;&#039;&#039;Avatar Rendering Cost scores&#039;&#039;&#039; - [[Avatar Rendering Cost|Learn all about it!]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;*&#039;&#039;&#039; Only until your framerate reaches your monitor&#039;s refresh rate, after this point there will be very little benefit.&lt;br /&gt;
&lt;br /&gt;
== [[Scripting]] == &amp;lt;!-- Important scripting limits should be placed here, less important ones should be in the articles Caveats or Specification sections --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Height at which scripts reactivate on a no-script parcel&#039;&#039;&#039; - 50 m above terrain mesh. Scripted objects that take controls will keep running when you fly down or enter a no-script parcel. (If scripts are disabled in the region debug panel, they will not run at any height.)&lt;br /&gt;
* &#039;&#039;&#039;Maximum height where scripts can run&#039;&#039;&#039; - none, as long as the object remains rezzed or attached.&lt;br /&gt;
* &#039;&#039;&#039;Maximum script source code size&#039;&#039;&#039; - 65536 single byte characters (that&#039;s a viewer limit and can be changed in the config file &#039;panel_script_ed.xml&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Maximum script memory size (LSO)&#039;&#039;&#039; - 16384 bytes.&lt;br /&gt;
* &#039;&#039;&#039;Maximum script memory size ([[Mono]])&#039;&#039;&#039; - 65536 bytes (the maximum memory available to Mono scripts can be constrained via [[llSetMemoryLimit]]).&lt;br /&gt;
* &#039;&#039;&#039;Maximum memory for [[:Category:LSL LinksetData|Linkset data]]&#039;&#039;&#039; key/value pairs - 128 KiB &lt;br /&gt;
* &#039;&#039;&#039;Maximum memory for [[:Category:Experience Tools|Experience]]&#039;&#039;&#039; key/value pairs- 128 MiB &lt;br /&gt;
* &#039;&#039;&#039;Maximum active [[llListen|listeners]] per script&#039;&#039;&#039; - 65.&lt;br /&gt;
** &#039;&#039;&#039;Usable channel for each listener&#039;&#039;&#039; - &#039;&#039;min.&#039;&#039; -2147483648, &#039;&#039;max.&#039;&#039; 2147483647, including the following special channels: [[PUBLIC_CHANNEL]] &#039;&#039;and&#039;&#039; [[DEBUG_CHANNEL]].&lt;br /&gt;
* &#039;&#039;&#039;Maximum number of scripts that can be rezzed at once&#039;&#039;&#039; – 1,000. (Refer to [https://community.secondlife.com/knowledgebase/english/building-tips-r13/ Building Tips, by Jeremy Linden].)&lt;br /&gt;
* For specific scripting limits, look up calls in the &#039;&#039;&#039;[[LSL Portal]]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== [[Sound]] ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: add link to modemworld.me summary of CCUG from 15/12/22 regarding sound max length --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{KBtip|Second Life will accept sounds that are encoded following &amp;quot;[https://en.wikipedia.org/wiki/Compact_Disc_Digital_Audio CD Quality]&amp;quot; encoding presets present in many {{Abbr|DAW|Digital Audio Workstation}}s.}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; width=&amp;quot;100%&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Limit affects&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Lower limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Upper limit&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;More coverage&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Sound length&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|less than 30 seconds or precisely 1,323,000 samples}}&lt;br /&gt;
| The advice is typically to upload sounds less than 30.0 and that 29.99 seconds is fine. However if you do wish to be able upload sounds that are 30 seconds precisely then the .WAV file must have exactly 1,323,000 samples (44,100Hz * 30 seconds). Specifically anything above 1,323,000 samples fails to upload.&lt;br /&gt;
| &#039;&#039;&#039;[https://community.secondlife.com/knowledgebase/english/uploading-assets-r75/ Uploading Assets]&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Bit depth&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|16 bit}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Sample rate&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|44.100 kHz}}&lt;br /&gt;
| n/a&lt;br /&gt;
| n/a&lt;br /&gt;
|-&lt;br /&gt;
||Audio channels&lt;br /&gt;
| n/a&lt;br /&gt;
| {{no|1 channel (mono)}}&lt;br /&gt;
| Channels will be &#039;&#039;&#039;merged&#039;&#039;&#039; at upload time for dual-channel (stereo) audio.&lt;br /&gt;
| n/a&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== [[Textures]] ==&lt;br /&gt;
* &#039;&#039;&#039;[http://en.wikipedia.org/wiki/Aspect_ratio Aspect ratios]&#039;&#039;&#039; of profile, place, etc. pictures — all of these were measured at UI size (Edit menu &amp;gt; Preferences &amp;gt; General tab &amp;gt; UI Size) = 1.000:&lt;br /&gt;
&lt;br /&gt;
=== Second Life Viewer 3.6 ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Classifieds thumbnail&#039;&#039;&#039; - ~3:2 (101&amp;amp;times;69 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Classifieds expanded&#039;&#039;&#039; - ~4:3 (159&amp;amp;times;120 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Classifieds expanded &amp;gt; More Info&#039;&#039;&#039; - native aspect ratio&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Destination Guide thumbnail&#039;&#039;&#039; - ~3:2 (101&amp;amp;times;69 pixels) &lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Destination Guide expanded&#039;&#039;&#039; - ~4:3 (159&amp;amp;times;120 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; People&#039;&#039;&#039; - 1:1 (100&amp;amp;times;100 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Places expanded&#039;&#039;&#039; ~4:3 (159&amp;amp;times;120 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Place Profile&#039;&#039;&#039; - ~3:2 (290&amp;amp;times;197 pixels)&lt;br /&gt;
* &#039;&#039;&#039;About Land &amp;gt; Options tab&#039;&#039;&#039; - ~4:3 (195&amp;amp;times;150 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Picture&#039;&#039;&#039; - native aspect ratio; thumbnail cropped to 72&amp;amp;times;72 pixels; zoomed uncropped up to 300&amp;amp;times;300 pixels&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Real world picture&#039;&#039;&#039; - native aspect ratio; thumbnail cropped to 45&amp;amp;times;45 pixels; zoomed uncropped up to 300&amp;amp;times;300 pixels&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Picks thumbnail&#039;&#039;&#039; - ~16:9 (~516&amp;amp;times;~270 pixels as measured on screen; the ratio seems closer to 16:8.5)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Pick expanded&#039;&#039;&#039; - Unknown as of 2022-11-13 (viewer 6.6.7.576223); the Pick image does not expand.&lt;br /&gt;
&lt;br /&gt;
=== 1.x Series Viewers ===&lt;br /&gt;
(official Viewer up to 1.23.5, still used by some [[Alternate viewers|Third Party Viewers]])&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; All for &amp;quot;Classifieds&amp;quot;, &amp;quot;People&amp;quot;, and &amp;quot;Places&amp;quot;&#039;&#039;&#039; - 4:3 (256&amp;amp;times;192 pixels)&amp;lt;ref&amp;gt;As of {{#time: r|2024-02-26T18:00:00}}, this seems to be one of the very few sizes supported by the [[Picture Service]].&amp;lt;/ref&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Places and Classified tabs&#039;&#039;&#039; - ~7:5 (398&amp;amp;times;282 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Search &amp;gt; Land tab&#039;&#039;&#039; - ~7:5 (358&amp;amp;times;252 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; 2nd Life tab&#039;&#039;&#039; - ~4:3 (178&amp;amp;times;133 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Picks tab&#039;&#039;&#039; - 16:9 (288&amp;amp;times;162 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; 1st Life tab&#039;&#039;&#039; - 1:1 (133&amp;amp;times;133 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Classifieds tab&#039;&#039;&#039; - ~3:2 (206&amp;amp;times;137 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Profile &amp;gt; Web tab&#039;&#039;&#039; - 1:1 (400&amp;amp;times;400 pixels)&lt;br /&gt;
** A scrollbar uses 15 pixels on the right-hand side.&lt;br /&gt;
* &#039;&#039;&#039;About Land &amp;gt; Options tab&#039;&#039;&#039; - ~3:2 (178&amp;amp;times;117 pixels)&lt;br /&gt;
* &#039;&#039;&#039;Group Information &amp;gt; General tab&#039;s &amp;quot;Group Insignia&amp;quot;&#039;&#039;&#039; - 1:1 (126&amp;amp;times;126 pixels)&lt;br /&gt;
** Some of these textures are shared (for example, Search &amp;gt; All place pages, Search &amp;gt; Places, and About Land &amp;gt; Options use the same image), so you should pick a well-balanced ratio and size.&lt;br /&gt;
** [[Texture_aspect_ratios|Learn how to get correct texture aspect ratios when editing images.]]&lt;br /&gt;
&lt;br /&gt;
=== All Viewers ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Maximum texture size&#039;&#039;&#039; - 2048&amp;amp;times;2048 pixels&lt;br /&gt;
** All Second Life textures are constrained to [http://en.wikipedia.org/wiki/Power_of_2 powers of 2] (e.g., 128, 256, 512).&lt;br /&gt;
** We strongly recommend you use as small textures as possible because larger ones consume more memory and take substantially longer to load.&lt;br /&gt;
** Where large textures are being forced by import to only 512&amp;amp;times;512, lower your ...&amp;gt; Preferences &amp;gt;...&amp;gt; UI Size under 1.0, to increase import size to the max 2048&amp;amp;times;2048.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Minimum texture size&#039;&#039;&#039; - 4&amp;amp;times;4 pixels&lt;br /&gt;
** This means that there are ten possible image dimensions: 4, 8, 16, 32, 64, 128, 256, 512, 1024 &amp;amp; 2048 pixels, either horizontal or vertical.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Texture resizing&#039;&#039;&#039;&lt;br /&gt;
** Uploaded textures will be resized to the dimensions above. &lt;br /&gt;
** Each dimension is resized independently (for instance, a 1024 x 999 image will be resized to 1024 x 1024).&lt;br /&gt;
** A dimension that is already a power of two will not be resized.&lt;br /&gt;
** The uploader “prefers” to round down rather than up. The threshold for rounding down/up is .75 of the distance between powers of two. &lt;br /&gt;
*** For example, given a dimension of x, if x / 512 is larger than 1.75, x will be resized to 1024, else 512.&lt;br /&gt;
*** Another example:  If x / 256 is larger than 1.75, x will be resized to 512. &lt;br /&gt;
&lt;br /&gt;
[[Category:Creation]] [[Category:Tutorials]] [[Category:Lists]]&lt;br /&gt;
&lt;br /&gt;
=== Notes ===&lt;br /&gt;
In general, 1 byte is enough to contain one character.&amp;lt;ref&amp;gt;Except in .NET-based applications, and those related to .NET development — which is the case of the SL viewer — which essentially use UTF-16 everywhere, i.e., every character is encoded in &#039;&#039;two&#039;&#039; bytes, not just one.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Text using Unicode characters (including, but not limited to, emojis), where supported, will require 1–4 bytes (depending on the encoding).&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
{{References}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Viewer_URI_Name_Space&amp;diff=1218509</id>
		<title>Viewer URI Name Space</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Viewer_URI_Name_Space&amp;diff=1218509"/>
		<updated>2025-10-27T20:41:34Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added /mention&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{ProtocolNav}}&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
The SL client supports {{Wikipedia|URI|links}} of the format {{mono|&amp;lt;nowiki&amp;gt;secondlife://Region/123/45/67/&amp;lt;region name&amp;gt;&amp;lt;/nowiki&amp;gt;}}, which have traditionally specified a region name and {{mono|x/y/z}} location.  This means &amp;quot;run Second Life viewer and show Region on the world map&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The new {{Wikipedia|URI|URL scheme}} has 3 slashes&amp;lt;ref name=&amp;quot;RFC3986&amp;quot;&amp;gt;Two slashes for the beginning of the authority section — for HTTP requests, this is the name of the server (optionally with the port) — here it&#039;s empty; followed by a third slash, terminating the authority section. See https://www.rfc-editor.org/rfc/rfc3986#section-3.2 . Because Second Life is omitting the &#039;authority&#039; part, that section is empty, thus the three slashes; note that Second Life is &#039;&#039;not&#039;&#039; the only example of an &#039;authorityless&#039; communications protocol; there are plenty of other examples.&amp;lt;/ref&amp;gt;, and allows direct control of the user interface and login.  For example, {{mono|&amp;lt;nowiki&amp;gt;secondlife:///app/login&amp;lt;/nowiki&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Most {{mono|&amp;lt;nowiki&amp;gt;secondlife:///app&amp;lt;/nowiki&amp;gt;}} URLs only work from browser instances running inside Second Life because of a denial-of-service attack using {{mono|&amp;lt;nowiki&amp;gt;secondlife:///app/teleport&amp;lt;/nowiki&amp;gt;}} links to force repeated teleports.  [[SLURL]]s that work with external browsers include:&lt;br /&gt;
* {{mono|&amp;lt;nowiki&amp;gt;secondlife://&amp;lt;region name&amp;gt;&amp;lt;/nowiki&amp;gt;}}&lt;br /&gt;
* {{mono|&amp;lt;nowiki&amp;gt;secondlife:///app/login&amp;lt;/nowiki&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
In chat, dialogs and other user interface elements, these URLs will be highlighted as clickable links, often with custom formatting including icons. The full functionality is available in Viewer 2.0 and up. Clickable links without formatting were available only in older versions&#039; chat and [[Instant Message|IM]] history.&lt;br /&gt;
&lt;br /&gt;
These links also may be known as &#039;&#039;Viewer URL Namespace&#039;&#039;, &#039;&#039;SLAPP URLs&#039;&#039; or &#039;&#039;Application [[SLURL]]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;nowiki&amp;gt;secondlife://&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
: &#039;&#039;&#039;&amp;lt;region_name&amp;gt;&#039;&#039;&#039; log in to this region or, if logged in, show information and offer teleport&lt;br /&gt;
:: &#039;&#039;&#039;/&amp;lt;local_x&amp;gt;&#039;&#039;&#039; optional X position, defaults to 128&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;local_y&amp;gt;&#039;&#039;&#039; optional Y position, defaults to 128&lt;br /&gt;
:::: &#039;&#039;&#039;/&amp;lt;local_z&amp;gt;&#039;&#039;&#039; optional Z position, defaults to 0&lt;br /&gt;
&lt;br /&gt;
: &#039;&#039;&#039;/app&#039;&#039;&#039;&lt;br /&gt;
:: &#039;&#039;&#039;/agent&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;agent_id&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/about&#039;&#039;&#039; open agent&#039;s profile, with 2nd Life tab selected&lt;br /&gt;
:::: &#039;&#039;&#039;/inspect&#039;&#039;&#039; display info dialog for agent (2.0)&lt;br /&gt;
:::: &#039;&#039;&#039;/im&#039;&#039;&#039; start an IM session with the agent (2.0)&lt;br /&gt;
:::: &#039;&#039;&#039;/mention&#039;&#039;&#039; creates a link for the agent, the agent&#039;s viewer may notify them with sound and highlight if they hear the message.&lt;br /&gt;
:::: &#039;&#039;&#039;/offerteleport&#039;&#039;&#039; display teleport offer dialog (2.0)&lt;br /&gt;
:::: &#039;&#039;&#039;/pay&#039;&#039;&#039; display pay resident dialog (2.0)&lt;br /&gt;
:::: &#039;&#039;&#039;/requestfriend&#039;&#039;&#039; display friendship offer dialog (2.0)&lt;br /&gt;
:::: &#039;&#039;&#039;/mute&#039;&#039;&#039; add to block list (2.0)&lt;br /&gt;
:::: &#039;&#039;&#039;/unmute&#039;&#039;&#039; remove from block list (2.0)&lt;br /&gt;
:::: &#039;&#039;&#039;/completename&#039;&#039;&#039; replace the URL with the avatar&#039;s display and user names,&lt;br /&gt;
:::::e.g., &amp;quot;hmm &amp;lt;nowiki&amp;gt;secondlife:///app/agent/eea40b5a-553d-4a07-b1ca-9f6e2f867814/complete&amp;lt;/nowiki&amp;gt; wow&amp;quot; in chat becomes &amp;quot;hmm Cerise (cerise.sorbet) wow&amp;quot; (2.4); see {{LSLGC|Avatar/Name}} for more details.&lt;br /&gt;
:::: &#039;&#039;&#039;/displayname&#039;&#039;&#039; replace the URL with the avatar&#039;s display name (2.4)&lt;br /&gt;
:::: &#039;&#039;&#039;/username&#039;&#039;&#039; replace the URL with the avatar&#039;s username e.g. &amp;quot;user.name&amp;quot; (2.4)&lt;br /&gt;
:: &#039;&#039;&#039;/appearance&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/show&#039;&#039;&#039; display the sidebar appearance tab (2.0)&lt;br /&gt;
:: &#039;&#039;&#039;/balance&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/request&#039;&#039;&#039; request a [[L$]] balance update from the server (2.0)&lt;br /&gt;
:: &#039;&#039;&#039;/chat&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;channel&amp;gt;&#039;&#039;&#039; any channel number greater than 0&amp;lt;ref&amp;gt;The public (chat) channel, also written as [[PUBLIC_CHANNEL]]&amp;lt;/ref&amp;gt; except [[DEBUG_CHANNEL]].&lt;br /&gt;
:::: &#039;&#039;&#039;/&amp;amp;lt;text&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:: &#039;&#039;&#039;/classified&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;classified_id&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/about&#039;&#039;&#039; open floater describing classified&lt;br /&gt;
:: &#039;&#039;&#039;/event&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;event_id&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/about&#039;&#039;&#039; open floater describing event&lt;br /&gt;
:: &#039;&#039;&#039;/experience&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;experience_id&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/profile&#039;&#039;&#039; open floater describing experience&lt;br /&gt;
:: &#039;&#039;&#039;/group&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;group_id&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/about&#039;&#039;&#039; open floater describing group&lt;br /&gt;
:::: &#039;&#039;&#039;/inspect&#039;&#039;&#039; display info dialog for group (2.0)&lt;br /&gt;
::: &#039;&#039;&#039;/create&#039;&#039;&#039; open the create group dialog (1.20)&lt;br /&gt;
::: &#039;&#039;&#039;/list&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/show&#039;&#039;&#039; open the list of groups to which user belongs (1.20)&lt;br /&gt;
:: &#039;&#039;&#039;/help&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;help_query&amp;gt;&#039;&#039;&#039; optional help topic (2.0)&lt;br /&gt;
:: &#039;&#039;&#039;/inventory&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;inventory_id&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/select&#039;&#039;&#039; inventory offer (2.0)&lt;br /&gt;
::: &#039;&#039;&#039;/show&#039;&#039;&#039; open the inventory sidebar tab (2.0)&lt;br /&gt;
:: &#039;&#039;&#039;/keybinding&#039;&#039;&#039;&lt;br /&gt;
::: Movement controls&lt;br /&gt;
::: &#039;&#039;&#039;/walk_to&#039;&#039;&#039; Walk to location mouse cursor points to&lt;br /&gt;
::: &#039;&#039;&#039;/teleport_to&#039;&#039;&#039; Teleport to location mouse cursor points to, but not all locations allow direct teleportation so you might be teleported closer to destination instead&lt;br /&gt;
::: &#039;&#039;&#039;/push_forward&#039;&#039;&#039; Move Forward&lt;br /&gt;
::: &#039;&#039;&#039;/push_backward&#039;&#039;&#039; Move Backward &lt;br /&gt;
::: &#039;&#039;&#039;/turn_left&#039;&#039;&#039; Left&lt;br /&gt;
::: &#039;&#039;&#039;/turn_right&#039;&#039;&#039; Right&lt;br /&gt;
::: &#039;&#039;&#039;/slide_left&#039;&#039;&#039; Strafe left&lt;br /&gt;
::: &#039;&#039;&#039;/slide_right&#039;&#039;&#039; Strafe right&lt;br /&gt;
::: &#039;&#039;&#039;/jump&#039;&#039;&#039; Jump/Up&lt;br /&gt;
::: &#039;&#039;&#039;/push_down&#039;&#039;&#039; Down&lt;br /&gt;
::: &#039;&#039;&#039;/run_forward&#039;&#039;&#039; Run Forward&lt;br /&gt;
::: &#039;&#039;&#039;/run_backward&#039;&#039;&#039; Run Backward&lt;br /&gt;
::: &#039;&#039;&#039;/run_left&#039;&#039;&#039; Run Left&lt;br /&gt;
::: &#039;&#039;&#039;/run_right&#039;&#039;&#039; Run Right&lt;br /&gt;
::: &#039;&#039;&#039;/toggle_run&#039;&#039;&#039; Toggle Run&lt;br /&gt;
::: &#039;&#039;&#039;/toggle_fly&#039;&#039;&#039; Fly/Stop flying&lt;br /&gt;
::: &#039;&#039;&#039;/toggle_sit&#039;&#039;&#039; Sit/Stand&lt;br /&gt;
::: &#039;&#039;&#039;/stop_moving&#039;&#039;&#039; Stop Moving&lt;br /&gt;
::: Camera controls&lt;br /&gt;
::: &#039;&#039;&#039;/look_up Look Up&lt;br /&gt;
::: &#039;&#039;&#039;/look_down&#039;&#039;&#039; Look Down&lt;br /&gt;
::: &#039;&#039;&#039;/move_forward&#039;&#039;&#039; Camera Forward&lt;br /&gt;
::: &#039;&#039;&#039;/move_backward&#039;&#039;&#039; Camera Backward&lt;br /&gt;
::: &#039;&#039;&#039;/move_forward_fast&#039;&#039;&#039; Camera Forward Fast&lt;br /&gt;
::: &#039;&#039;&#039;/move_backward_fast&#039;&#039;&#039; Camera Backward Fast&lt;br /&gt;
::: &#039;&#039;&#039;/spin_over&#039;&#039;&#039; Camera Spin Over&lt;br /&gt;
::: &#039;&#039;&#039;/spin_under&#039;&#039;&#039; Camera Spin Under&lt;br /&gt;
::: &#039;&#039;&#039;/pan_up&#039;&#039;&#039; Camera Pan Up&lt;br /&gt;
::: &#039;&#039;&#039;/pan_down&#039;&#039;&#039; Camera Pan Down&lt;br /&gt;
::: &#039;&#039;&#039;/pan_left&#039;&#039;&#039; Camera Pan Left&lt;br /&gt;
::: &#039;&#039;&#039;/pan_right&#039;&#039;&#039; Camera Pan Right&lt;br /&gt;
::: &#039;&#039;&#039;/pan_in&#039;&#039;&#039; Camera Pan In&lt;br /&gt;
::: &#039;&#039;&#039;/pan_out&#039;&#039;&#039; Camera Pan Out&lt;br /&gt;
::: &#039;&#039;&#039;/spin_around_ccw&#039;&#039;&#039; Camera spin around counterclockwise&lt;br /&gt;
::: &#039;&#039;&#039;/spin_around_cw&#039;&#039;&#039; Camera spin around clockwise&lt;br /&gt;
::: &#039;&#039;&#039;/move_forward_sitting&#039;&#039;&#039; Camera Forward Sitting&lt;br /&gt;
::: &#039;&#039;&#039;/move_backward_sitting&#039;&#039;&#039; Camera Backward Sitting&lt;br /&gt;
::: &#039;&#039;&#039;/spin_over_sitting&#039;&#039;&#039; Camera Spin Over Sitting&lt;br /&gt;
::: &#039;&#039;&#039;/spin_under_sitting&#039;&#039;&#039; Camera Spin Under Sitting&lt;br /&gt;
::: &#039;&#039;&#039;/spin_around_ccw_sitting&#039;&#039;&#039; Camera spin around counterclockwise sitting&lt;br /&gt;
::: &#039;&#039;&#039;/spin_around_cw_sitting&#039;&#039;&#039; Camera spin around clockwise sitting&lt;br /&gt;
::: Editing controls&lt;br /&gt;
::: &#039;&#039;&#039;/edit_avatar_spin_ccw&#039;&#039;&#039; Camera spin around avatar counterclockwise&lt;br /&gt;
::: &#039;&#039;&#039;/edit_avatar_spin_cw&#039;&#039;&#039; Camera spin around avatar clockwise&lt;br /&gt;
::: &#039;&#039;&#039;/edit_avatar_spin_over&#039;&#039;&#039; Camera spin over avatar&lt;br /&gt;
::: &#039;&#039;&#039;/edit_avatar_spin_under&#039;&#039;&#039; Camera spin under avatar&lt;br /&gt;
::: &#039;&#039;&#039;/edit_avatar_move_forward&#039;&#039;&#039; Camera Forward&lt;br /&gt;
::: &#039;&#039;&#039;/edit_avatar_move_backward&#039;&#039;&#039; Camera Backward&lt;br /&gt;
::: Sound and Media controls&lt;br /&gt;
::: &#039;&#039;&#039;/toggle_pause_media&#039;&#039;&#039; Play/Pause Media&lt;br /&gt;
::: &#039;&#039;&#039;/toggle_enable_media&#039;&#039;&#039; Play/Stop All Media&lt;br /&gt;
::: &#039;&#039;&#039;/voice_follow_key&#039;&#039;&#039; Voice&lt;br /&gt;
::: &#039;&#039;&#039;/toggle_voice&#039;&#039;&#039; Toggle Voice&lt;br /&gt;
::: &#039;&#039;&#039;/start_chat&#039;&#039;&#039; Start Chat&lt;br /&gt;
::: &#039;&#039;&#039;/start_gesture&#039;&#039;&#039; Start Gesture&lt;br /&gt;
::: &#039;&#039;&#039;/script_trigger_lbutton&#039;&#039;&#039; Interact (Script LMB)&lt;br /&gt;
:::: Control modes &lt;br /&gt;
:::: &#039;&#039;&#039;?mode=&#039;&#039;&#039; &amp;quot;first_person&amp;quot; or 0, &amp;quot;third_person&amp;quot; or 1, &amp;quot;edit_avatar&amp;quot; or 2, &amp;quot;sitting&amp;quot; or 3. e.g. secondlife:///app/keybinding/push_forward?mode=sitting&lt;br /&gt;
:: &#039;&#039;&#039;/login&#039;&#039;&#039; log in on launch. External and internal browsers.&lt;br /&gt;
::: see below for query parameters, of course, values are URL escaped&lt;br /&gt;
:: &#039;&#039;&#039;/maptrackavatar&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;friend_id&amp;gt;&#039;&#039;&#039; find a friend on the world map, requires permission (2.4)&lt;br /&gt;
:: &#039;&#039;&#039;/objectim&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;object_id&amp;gt;&#039;&#039;&#039; display an info dialog for the object sending this message (2.0)&lt;br /&gt;
:::: &#039;&#039;&#039;?name=&amp;lt;object_name&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;&amp;amp;owner=&amp;lt;owner_id&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;&amp;amp;groupowned=true&#039;&#039;&#039; (add if the object is deeded to a group)&lt;br /&gt;
:::: &#039;&#039;&#039;&amp;amp;slurl=&amp;lt;region&amp;gt;/&amp;lt;x&amp;gt;/&amp;lt;y&amp;gt;/&amp;lt;z&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:: &#039;&#039;&#039;/openfloater&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;floater_name&amp;gt;&#039;&#039;&#039; e.g. &amp;quot;preferences&amp;quot;, &amp;quot;people&amp;quot;, &amp;quot;places&amp;quot;, &amp;quot;picks&amp;quot;, &amp;quot;destinations&amp;quot;, &amp;quot;profile&amp;quot;&lt;br /&gt;
:: &#039;&#039;&#039;/parcel&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;parcel_id&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/about&#039;&#039;&#039; open floater describing place&lt;br /&gt;
:: &#039;&#039;&#039;&amp;lt;strike&amp;gt;/region&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;region_id&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/about&#039;&#039;&#039; information from database about that region?  list of parcels?  covenant?&amp;lt;/strike&amp;gt;&lt;br /&gt;
:: &#039;&#039;&#039;/search&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;category&amp;gt;&#039;&#039;&#039; &amp;quot;all&amp;quot;, &amp;quot;people&amp;quot;, &amp;quot;places&amp;quot;, &amp;quot;events&amp;quot;, &amp;quot;groups&amp;quot;, &amp;quot;wiki&amp;quot;, &amp;quot;destinations&amp;quot;, &amp;quot;classifieds&amp;quot;&lt;br /&gt;
:::: &#039;&#039;&#039;/&amp;lt;search_term&amp;gt;&#039;&#039;&#039; open a search floater with matching results (2.0)&lt;br /&gt;
:: &#039;&#039;&#039;/sharewithavatar&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;agent_id&amp;gt;&#039;&#039;&#039; open an inventory share/IM window for agent (2.4)&lt;br /&gt;
:: &#039;&#039;&#039;/teleport&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;region_name&amp;gt;&#039;&#039;&#039; teleport instantly to this region, no dialog&lt;br /&gt;
:::: &#039;&#039;&#039;/&amp;lt;local_x&amp;gt;&#039;&#039;&#039; optional X position, defaults to 128&lt;br /&gt;
::::: &#039;&#039;&#039;/&amp;lt;local_y&amp;gt;&#039;&#039;&#039; optional Y position, defaults to 128&lt;br /&gt;
:::::: &#039;&#039;&#039;/&amp;lt;local_z&amp;gt;&#039;&#039;&#039; optional Z position, defaults to 0&lt;br /&gt;
:: &#039;&#039;&#039;/voicecallavatar&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;agent_id&amp;gt;&#039;&#039;&#039; start a private voice session (2.4)&lt;br /&gt;
:: &#039;&#039;&#039;/wear_folder&#039;&#039;&#039;&lt;br /&gt;
::: &#039;&#039;&#039;/?folder_id=&amp;lt;inventory_folder_uuid&amp;gt;&#039;&#039;&#039; replace outfit with contents of specified folder (2.6)&lt;br /&gt;
::: &#039;&#039;&#039;/?folder_name=&amp;lt;library_folder_name&amp;gt;&#039;&#039;&#039; replace outfit with contents of named Library folder&lt;br /&gt;
:: &#039;&#039;&#039;/worldmap&#039;&#039;&#039; open the map with this destination selected (2.0)&lt;br /&gt;
::: &#039;&#039;&#039;/&amp;lt;region_name&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
:::: &#039;&#039;&#039;/&amp;lt;local_x&amp;gt;&#039;&#039;&#039; optional X position, defaults to 128&lt;br /&gt;
::::: &#039;&#039;&#039;/&amp;lt;local_y&amp;gt;&#039;&#039;&#039; optional Y position, defaults to 128&lt;br /&gt;
:::::: &#039;&#039;&#039;/&amp;lt;local_z&amp;gt;&#039;&#039;&#039; optional Z position, defaults to 0&lt;br /&gt;
&lt;br /&gt;
This could be extended to things like:&lt;br /&gt;
: &#039;&#039;&#039;/app/event/&amp;lt;event_id&amp;gt;/subscribe&#039;&#039;&#039; to register for notifications&lt;br /&gt;
: &#039;&#039;&#039;/app/parcel/&amp;lt;parcel_id&amp;gt;/teleport&#039;&#039;&#039; to teleport to a specific location&lt;br /&gt;
&lt;br /&gt;
== Login Query Parameters ==&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| first || the account first name&lt;br /&gt;
|-&lt;br /&gt;
| last || the account last name&lt;br /&gt;
|-&lt;br /&gt;
| session || the secure session id&lt;br /&gt;
|-&lt;br /&gt;
| location || login location, format TBD, optional&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Second Life Wiki formatting ==&lt;br /&gt;
&lt;br /&gt;
When adding {{mono|&amp;lt;nowiki&amp;gt;secondlife://&amp;lt;/nowiki&amp;gt;}} links using the above syntax to a Second Life Wiki page, you can enclose your links in squared brackets (thus turning them into a standard Wiki external link), which also gives you the chance of changing the displayed text. Example:  &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;kbd&amp;gt;&amp;lt;nowiki&amp;gt;[secondlife:///app/objectim/190f571d-fdf1-8f6c-4e9d-973e9b5e0566?name=ObjectName&amp;amp;owner=c93c3129-2250-4c79-a5f7-8c755ca2707e&amp;amp;groupowned=true&amp;amp;slurl=Location/128/128/27 ObjectName]&amp;lt;/nowiki&amp;gt;&amp;lt;/kbd&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will display as &lt;br /&gt;
&lt;br /&gt;
 [secondlife:///app/objectim/190f571d-fdf1-8f6c-4e9d-973e9b5e0566?name=ObjectName&amp;amp;owner=c93c3129-2250-4c79-a5f7-8c755ca2707e&amp;amp;groupowned=true&amp;amp;slurl=Location/128/128/27 ObjectName]&lt;br /&gt;
&lt;br /&gt;
In this case you will also need to ensure the object name has been escaped.&lt;br /&gt;
&lt;br /&gt;
Sometimes, having the link clickable (which will attempt to launch the Second Life Viewer to open it), is not desirable. Instead, to display the {{Wikipedia|URI}} itself without making it clickable, surround it with {{mono|&amp;lt;nowiki&amp;gt;&amp;lt;nowiki&amp;gt;...&amp;lt;/nowiki&amp;gt;&amp;lt;/nowiki&amp;gt;}}. For example:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;kbd&amp;gt;&#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&amp;lt;nowiki&amp;gt;secondlife:///app/group/8db35111-979b-14a7-6a74-b47be86ce04f/about&amp;lt;/nowiki&amp;gt;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/kbd&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will display as&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;secondlife:///app/group/8db35111-979b-14a7-6a74-b47be86ce04f/about&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
whereas&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;kbd&amp;gt;&#039;&#039;&#039;&amp;lt;nowiki&amp;gt;secondlife:///app/group/8db35111-979b-14a7-6a74-b47be86ce04f/about&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/kbd&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will display the whole link and make it clickable, e.g.:&lt;br /&gt;
&lt;br /&gt;
 secondlife:///app/group/8db35111-979b-14a7-6a74-b47be86ce04f/about&lt;br /&gt;
&lt;br /&gt;
== LSL Examples ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
string Who(key id)&lt;br /&gt;
{&lt;br /&gt;
    return &amp;quot;secondlife:///app/agent/&amp;quot; + (string)id + &amp;quot;/inspect&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer num)&lt;br /&gt;
    {&lt;br /&gt;
        llSay(0, &amp;quot;Touched by &amp;quot; + Who(llDetectedKey(0)) + &amp;quot;.&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Name Spaces]]&lt;br /&gt;
[[Category:Search]]&lt;br /&gt;
[[Category:LSL Chat]]&lt;br /&gt;
[[Category:LSL_Dialog]]&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlWind&amp;diff=1218505</id>
		<title>LlWind</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlWind&amp;diff=1218505"/>
		<updated>2025-10-23T16:01:22Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: More testing and observations.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2={{LSL Function/offset|offset|noZ=*|sim=*}}&lt;br /&gt;
|func=llWind&lt;br /&gt;
|sort=Wind&lt;br /&gt;
|func_id=44&lt;br /&gt;
|func_sleep=0.0&lt;br /&gt;
|func_energy=10.0&lt;br /&gt;
|return_type=vector&lt;br /&gt;
|p1_type=vector&lt;br /&gt;
|p1_name=offset&lt;br /&gt;
|func_footnote&lt;br /&gt;
|return_text=that is the wind velocity at the prim&#039;s [[llGetPos|position]] + {{LSLPT|offset}}&lt;br /&gt;
|spec&lt;br /&gt;
|caveats&lt;br /&gt;
|notes=&lt;br /&gt;
* Each region simulates its own wind with a random seed value, and chaos is scaled by the region&#039;s sun position.&lt;br /&gt;
** While the simulation is performed on a 16x16 grid, the wind has perfect resolution anywhere between those cells. (interpolated)&lt;br /&gt;
** This low resolution tends to result in smooth gradients of wind, however sudden and strong changes in wind speed and direction are possible.&lt;br /&gt;
** Vortexes and are also possible, sometimes multiple in the same region.&lt;br /&gt;
* Neighboring regions influence each other at the connecting border, which may lead to cascading changes for either region.&lt;br /&gt;
* Regions without neighbors exhibit very uniform wind across the whole region.&lt;br /&gt;
** Wind will occasionally change direction over time, but eventually stabilizes toward the new direction.&lt;br /&gt;
* Maximum wind speed can reach at least 20m/s.&lt;br /&gt;
|examples=&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer num)&lt;br /&gt;
    {&lt;br /&gt;
        llSay(0, &amp;quot;Wind velocity: &amp;quot; + (string)llWind(ZERO_VECTOR));&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
// wind interpretation as angle and speed&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        vector windVector = llWind( ZERO_VECTOR);&lt;br /&gt;
        float windSpeed = llVecMag( windVector);&lt;br /&gt;
        float windDirection = llAtan2( windVector.y, windVector.x);&lt;br /&gt;
        integer compassWind = ( 450 - (integer)( RAD_TO_DEG*windDirection))%360;&lt;br /&gt;
        llOwnerSay( &amp;quot;\nWind direction: &amp;quot;+(string)compassWind+&amp;quot;°\nWind speed: &amp;quot;+(string)windSpeed+&amp;quot; m/S&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions={{LSL DefineRow||[[llCloud]]}}&lt;br /&gt;
|also_articles={{LSL DefineRow||{{LSLGC|Weather}}|SL Weather information}}&lt;br /&gt;
{{LSL DefineRow||[[User:Dora_Gustafson/moderated_world_wind|Moderated in-world wind]]}}&lt;br /&gt;
|notes&lt;br /&gt;
|issues={{Issue/V1|SVC-3131|llWind() and viewer wind effects do not match}}&lt;br /&gt;
|haiku={{Haiku|Softly blowing breeze|Calmly rustling through the tree|A gentle whisper}}&lt;br /&gt;
|cat1=Region&lt;br /&gt;
|cat2=Weather&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlWind&amp;diff=1218504</id>
		<title>LlWind</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlWind&amp;diff=1218504"/>
		<updated>2025-10-23T13:50:42Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added notes from testing.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2={{LSL Function/offset|offset|noZ=*|sim=*}}&lt;br /&gt;
|func=llWind&lt;br /&gt;
|sort=Wind&lt;br /&gt;
|func_id=44&lt;br /&gt;
|func_sleep=0.0&lt;br /&gt;
|func_energy=10.0&lt;br /&gt;
|return_type=vector&lt;br /&gt;
|p1_type=vector&lt;br /&gt;
|p1_name=offset&lt;br /&gt;
|func_footnote&lt;br /&gt;
|return_text=that is the wind velocity at the prim&#039;s [[llGetPos|position]] + {{LSLPT|offset}}&lt;br /&gt;
|spec&lt;br /&gt;
|caveats&lt;br /&gt;
|notes=&lt;br /&gt;
* Each region simulates its own wind with a random seed value, and chaos is scaled by the region&#039;s sun position.&lt;br /&gt;
** While the simulation is performed on a 16x16 grid, the wind has perfect resolution anywhere between those cells. (interpolated)&lt;br /&gt;
* Neighboring regions influence each other at the connecting border, which may lead to cascading changes for either region.&lt;br /&gt;
* Regions without neighbors exhibit very uniform wind across the whole region.&lt;br /&gt;
** Wind will occasionally change direction over time, but eventually stabilizes toward the new direction.&lt;br /&gt;
|examples=&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer num)&lt;br /&gt;
    {&lt;br /&gt;
        llSay(0, &amp;quot;Wind velocity: &amp;quot; + (string)llWind(ZERO_VECTOR));&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
// wind interpretation as angle and speed&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        vector windVector = llWind( ZERO_VECTOR);&lt;br /&gt;
        float windSpeed = llVecMag( windVector);&lt;br /&gt;
        float windDirection = llAtan2( windVector.y, windVector.x);&lt;br /&gt;
        integer compassWind = ( 450 - (integer)( RAD_TO_DEG*windDirection))%360;&lt;br /&gt;
        llOwnerSay( &amp;quot;\nWind direction: &amp;quot;+(string)compassWind+&amp;quot;°\nWind speed: &amp;quot;+(string)windSpeed+&amp;quot; m/S&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions={{LSL DefineRow||[[llCloud]]}}&lt;br /&gt;
|also_articles={{LSL DefineRow||{{LSLGC|Weather}}|SL Weather information}}&lt;br /&gt;
{{LSL DefineRow||[[User:Dora_Gustafson/moderated_world_wind|Moderated in-world wind]]}}&lt;br /&gt;
|notes&lt;br /&gt;
|issues={{Issue/V1|SVC-3131|llWind() and viewer wind effects do not match}}&lt;br /&gt;
|haiku={{Haiku|Softly blowing breeze|Calmly rustling through the tree|A gentle whisper}}&lt;br /&gt;
|cat1=Region&lt;br /&gt;
|cat2=Weather&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=NULL_KEY&amp;diff=1218492</id>
		<title>NULL KEY</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=NULL_KEY&amp;diff=1218492"/>
		<updated>2025-10-18T17:10:38Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Issues/SVC-5772}}{{LSL_Constant/string}}{{LSL Constant&lt;br /&gt;
|name=NULL_KEY&lt;br /&gt;
|type=string&lt;br /&gt;
|value=&amp;quot;00000000-0000-0000-0000-000000000000&amp;quot;&lt;br /&gt;
|desc=&amp;lt;span style=&amp;quot;color:red;&amp;quot;&amp;gt;&#039;&#039;&#039;NULL_KEY is a [[string]].&#039;&#039;&#039;&amp;lt;/span&amp;gt; However it is only really useful as a [[key]].&lt;br /&gt;
&lt;br /&gt;
For {{LSLGC|Conditional}} tests, NULL_KEY evaluates to &#039;&#039;&#039;TRUE&#039;&#039;&#039; like other strings.&lt;br /&gt;
&lt;br /&gt;
For list search (like [[llListFindList]]), NULL_KEY won&#039;t match null keys whose type is &#039;&#039;&#039;key&#039;&#039;&#039; without typecasting.&lt;br /&gt;
|examples=&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
// Returns 2 for valid keys, 1 for NULL_KEY, 0 for invalid keys.&lt;br /&gt;
integer isKey(key input)&lt;br /&gt;
{&lt;br /&gt;
    if (input) return 2;&lt;br /&gt;
    return (input == NULL_KEY);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        // NULL_KEY itself is evaluated as FALSE only when typecast to key.&lt;br /&gt;
        if (NULL_KEY) { llOwnerSay(&amp;quot;NULL_KEY is TRUE&amp;quot;);  } // Correct.&lt;br /&gt;
        else          { llOwnerSay(&amp;quot;NULL_KEY is FALSE&amp;quot;); } // Never.&lt;br /&gt;
&lt;br /&gt;
        if ((key) NULL_KEY) { llOwnerSay(&amp;quot;Casted NULL_KEY is TRUE&amp;quot;);  } // Never.&lt;br /&gt;
        else                { llOwnerSay(&amp;quot;Casted NULL_KEY is FALSE&amp;quot;); } // Correct.&lt;br /&gt;
&lt;br /&gt;
        // NULL_KEY won&#039;t match keys in lists without typecasting.&lt;br /&gt;
        // The following list contains a null key because avatars don&#039;t have creators:&lt;br /&gt;
        list details = llGetObjectDetails(llGetOwner(), [OBJECT_CREATOR]);&lt;br /&gt;
        integer index = llListFindList(details, [NULL_KEY]);&lt;br /&gt;
        if (index &amp;gt;= 0) { llOwnerSay(&amp;quot;NULL_KEY was found in list&amp;quot;); } // Never.&lt;br /&gt;
        else             { llOwnerSay(&amp;quot;NULL_KEY was NOT found in list&amp;quot;); } // Correct.&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|notes=In most situations NULL_KEY isn&#039;t needed; an empty string (&amp;quot;&amp;quot;) will suffice. To take advantage of this certain practices have to be avoided. In many applications keys are checked against NULL_KEY to determine if they are valid; this is bad practice.&lt;br /&gt;
&lt;br /&gt;
LSL makes it easy to check if a key is valid. Simply use the key as the parameter for a conditional.&lt;br /&gt;
&lt;br /&gt;
That is, instead of &amp;lt;code&amp;gt;if(uuid != NULL_KEY)&amp;lt;/code&amp;gt;, use &amp;lt;code&amp;gt;if(uuid)&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;if(uuid)&amp;lt;/code&amp;gt; will only return [[TRUE]] if it is a valid key that is also not a null key.&lt;br /&gt;
|functions=&lt;br /&gt;
{{LSL DefineRow||[[llAvatarOnSitTarget]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llDetectedKey]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetNotecardLine]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetLandOwnerAt]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetPermissionsKey]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetTexture]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llListen]]|}}&lt;br /&gt;
|events=&lt;br /&gt;
{{LSL DefineRow||[[attach]]|}}&lt;br /&gt;
|haiku={{Haiku|My hopes for nothing|but for thirty two zeros,|have been dashed, four times.}}&lt;br /&gt;
|cat1=Key&lt;br /&gt;
|cat2&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=NULL_KEY&amp;diff=1218491</id>
		<title>NULL KEY</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=NULL_KEY&amp;diff=1218491"/>
		<updated>2025-10-18T17:09:13Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Corrected information about evaluation and typing.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Issues/SVC-5772}}{{LSL_Constant/string}}{{LSL Constant&lt;br /&gt;
|name=NULL_KEY&lt;br /&gt;
|type=string&lt;br /&gt;
|value=&amp;quot;00000000-0000-0000-0000-000000000000&amp;quot;&lt;br /&gt;
|desc=&amp;lt;span style=&amp;quot;color:red;&amp;quot;&amp;gt;&#039;&#039;&#039;NULL_KEY is a [[string]].&#039;&#039;&#039;&amp;lt;/span&amp;gt; However it is only really useful as a [[key]].&lt;br /&gt;
&lt;br /&gt;
For {{LSLGC|Conditional}} tests, NULL_KEY evaluates to &#039;&#039;&#039;TRUE&#039;&#039;&#039; like other strings.&lt;br /&gt;
For list search (like {{llListFindList}}), NULL_KEY won&#039;t match null keys whose type is &#039;&#039;&#039;key&#039;&#039;&#039;.&lt;br /&gt;
|examples=&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
// Returns 2 for valid keys, 1 for NULL_KEY, 0 for invalid keys.&lt;br /&gt;
integer isKey(key input)&lt;br /&gt;
{&lt;br /&gt;
    if (input) return 2;&lt;br /&gt;
    return (input == NULL_KEY);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        // NULL_KEY itself is evaluated as FALSE only when typecast to key.&lt;br /&gt;
        if (NULL_KEY) { llOwnerSay(&amp;quot;NULL_KEY is TRUE&amp;quot;);  } // Correct.&lt;br /&gt;
        else          { llOwnerSay(&amp;quot;NULL_KEY is FALSE&amp;quot;); } // Never.&lt;br /&gt;
&lt;br /&gt;
        if ((key) NULL_KEY) { llOwnerSay(&amp;quot;Casted NULL_KEY is TRUE&amp;quot;);  } // Never.&lt;br /&gt;
        else                { llOwnerSay(&amp;quot;Casted NULL_KEY is FALSE&amp;quot;); } // Correct.&lt;br /&gt;
&lt;br /&gt;
        // NULL_KEY won&#039;t match keys in lists without typecasting.&lt;br /&gt;
        // The following list contains a null key because avatars don&#039;t have creators:&lt;br /&gt;
        list details = llGetObjectDetails(llGetOwner(), [OBJECT_CREATOR]);&lt;br /&gt;
        integer index = llListFindList(details, [NULL_KEY]);&lt;br /&gt;
        if (index &amp;gt;= 0) { llOwnerSay(&amp;quot;NULL_KEY was found in list&amp;quot;); } // Never.&lt;br /&gt;
        else             { llOwnerSay(&amp;quot;NULL_KEY was NOT found in list&amp;quot;); } // Correct.&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|notes=In most situations NULL_KEY isn&#039;t needed; an empty string (&amp;quot;&amp;quot;) will suffice. To take advantage of this certain practices have to be avoided. In many applications keys are checked against NULL_KEY to determine if they are valid; this is bad practice.&lt;br /&gt;
&lt;br /&gt;
LSL makes it easy to check if a key is valid. Simply use the key as the parameter for a conditional.&lt;br /&gt;
&lt;br /&gt;
That is, instead of &amp;lt;code&amp;gt;if(uuid != NULL_KEY)&amp;lt;/code&amp;gt;, use &amp;lt;code&amp;gt;if(uuid)&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;if(uuid)&amp;lt;/code&amp;gt; will only return [[TRUE]] if it is a valid key that is also not a null key.&lt;br /&gt;
|functions=&lt;br /&gt;
{{LSL DefineRow||[[llAvatarOnSitTarget]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llDetectedKey]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetNotecardLine]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetLandOwnerAt]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetPermissionsKey]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetTexture]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llListen]]|}}&lt;br /&gt;
|events=&lt;br /&gt;
{{LSL DefineRow||[[attach]]|}}&lt;br /&gt;
|haiku={{Haiku|My hopes for nothing|but for thirty two zeros,|have been dashed, four times.}}&lt;br /&gt;
|cat1=Key&lt;br /&gt;
|cat2&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlIsFriend&amp;diff=1218389</id>
		<title>LlIsFriend</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlIsFriend&amp;diff=1218389"/>
		<updated>2025-08-02T15:21:15Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Removed bug caveat, fixed over a year ago.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Function&lt;br /&gt;
|inject-2={{LSL Function/uuid|uuid|sim=*}}&lt;br /&gt;
|func=llIsFriend&lt;br /&gt;
|sort=IsFriend&lt;br /&gt;
|func_id=0|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|return_type=integer&lt;br /&gt;
|return_subtype=boolean&lt;br /&gt;
|p1_type=key|p1_name=agent_id&lt;br /&gt;
|func_desc=&lt;br /&gt;
|return_text=that is [[TRUE]] if {{LSLP|agent_id}} and the owner of the [[prim]] the [[script]] is in are friends, otherwise [[FALSE]].&lt;br /&gt;
|func_footnote&lt;br /&gt;
|spec=&lt;br /&gt;
&lt;br /&gt;
* If the prim is owned by a group this function behaves identically to [[llSameGroup]]&lt;br /&gt;
* This function will return [[FALSE]] under the following edge cases:&lt;br /&gt;
** If neither the owner, nor the target agent are in the region.&lt;br /&gt;
** If the agent_id does not specify an agent.&lt;br /&gt;
&lt;br /&gt;
|caveats=&lt;br /&gt;
* You are not your own friend.&lt;br /&gt;
&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        llSensorRepeat(&amp;quot;&amp;quot;, NULL_KEY, AGENT, 95, PI, 10);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sensor(integer count)&lt;br /&gt;
    {&lt;br /&gt;
        integer index;&lt;br /&gt;
        for (index = 0; index &amp;lt; count; ++index)&lt;br /&gt;
        {&lt;br /&gt;
            string is_is_not = &amp;quot; is NOT &amp;quot;;&lt;br /&gt;
            if (llIsFriend(llDetectedKey(index)))&lt;br /&gt;
            {&lt;br /&gt;
                is_is_not = &amp;quot; is &amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            llSay(0, llDetectedName(index) + is_is_not + &amp;quot;a friend.&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llDetectedGroup]]}}&lt;br /&gt;
{{LSL DefineRow||[[llSameGroup]]}}&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_events&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes&lt;br /&gt;
|cat1=Avatar&lt;br /&gt;
|cat2=Group&lt;br /&gt;
|haiku&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlAddToLandBanList&amp;diff=1218372</id>
		<title>LlAddToLandBanList</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlAddToLandBanList&amp;diff=1218372"/>
		<updated>2025-07-25T18:16:23Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Updated description based on Github lsl_definitions.yaml&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2={{Issues/SVC-1911}}{{LSL_Function/avatar|avatar}}&lt;br /&gt;
|func_id=310|func_sleep=0.1|func_energy=10.0&lt;br /&gt;
|func=llAddToLandBanList|sort=AddToLandBanList&lt;br /&gt;
|p1_type=key|p1_name=avatar|p2_type=float|p2_name=hours&lt;br /&gt;
|func_footnote&lt;br /&gt;
|func_desc=Add {{LSLP|avatar}} to the parcel ban list for the specified number of {{LSLP|hours}}.&lt;br /&gt;
&lt;br /&gt;
A value of 0 for {{LSLP|hours}} will add the agent indefinitely. The smallest value that {{LSLP|hours}} will accept is 0.01; anything smaller will be seen as 0.&lt;br /&gt;
&lt;br /&gt;
Residents teleporting to a parcel where they are banned will be redirected to a neighboring parcel.&lt;br /&gt;
|return_text&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=&lt;br /&gt;
* Must be owned by the land owner.&lt;br /&gt;
* When values below 1 are used, it seems the function bans in approximately 30 second increments. (Probably 36 second increments, as 0.01 of an hour is 36 seconds)&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;// This is not a complete solution, requires full avatar names to work - even for unbanning someone!&lt;br /&gt;
// This is meant only as an example of the land ban and pass management functions.&lt;br /&gt;
// free to copy, use, modify, distribute - just don&#039;t ask me to debug your modified code. ;-)&lt;br /&gt;
// &lt;br /&gt;
// Commands are:&lt;br /&gt;
//   /5 ban:full_avatar_name&lt;br /&gt;
//   /5 tempban:full_avatar_name&lt;br /&gt;
//   /5 unban:full_avatar_name&lt;br /&gt;
//   /5 pass:full_avatar_name&lt;br /&gt;
//   /5 unpass:full_avatar_name&lt;br /&gt;
//   /5 clearban&lt;br /&gt;
//   /5 clearpass&lt;br /&gt;
&lt;br /&gt;
string command;&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        llListen(5, &amp;quot;&amp;quot;, llGetOwner(), &amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    on_rez(integer param)&lt;br /&gt;
    {&lt;br /&gt;
        llResetScript();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    listen(integer chan, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (command != &amp;quot;&amp;quot;)&lt;br /&gt;
        {&lt;br /&gt;
            llOwnerSay(&amp;quot;Sorry, still processing last command, try again in a second.&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        list args = llParseString2List(message,[&amp;quot;:&amp;quot;],[]);&lt;br /&gt;
        command = llToLower(llList2String(args,0));&lt;br /&gt;
        &lt;br /&gt;
        if (command == &amp;quot;clearbans&amp;quot;)&lt;br /&gt;
        {&lt;br /&gt;
            llResetLandBanList();&lt;br /&gt;
        }&lt;br /&gt;
        if (command == &amp;quot;clearpass&amp;quot;)&lt;br /&gt;
        {&lt;br /&gt;
            llResetLandPassList();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            llSensor(llList2String(args,1),NULL_KEY,AGENT,96,PI);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    no_sensor()&lt;br /&gt;
    {&lt;br /&gt;
        command = &amp;quot;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    sensor(integer num)&lt;br /&gt;
    {&lt;br /&gt;
        integer i = 0;&lt;br /&gt;
        for (; i &amp;lt; num; ++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (command == &amp;quot;ban&amp;quot;)&lt;br /&gt;
            {&lt;br /&gt;
                // Ban indefinetely &lt;br /&gt;
                llAddToLandBanList(llDetectedKey(i),0.0);&lt;br /&gt;
            }&lt;br /&gt;
            if (command == &amp;quot;tempban&amp;quot;)&lt;br /&gt;
            {&lt;br /&gt;
                // Ban for 1 hour.&lt;br /&gt;
                llAddToLandBanList(llDetectedKey(i),1.0);&lt;br /&gt;
            }&lt;br /&gt;
            if (command == &amp;quot;unban&amp;quot;)&lt;br /&gt;
            {&lt;br /&gt;
                llRemoveFromLandBanList(llDetectedKey(i));&lt;br /&gt;
            }&lt;br /&gt;
            if (command == &amp;quot;pass&amp;quot;)&lt;br /&gt;
            {&lt;br /&gt;
                // Add to land pass list for 1 hour&lt;br /&gt;
                llAddToLandPassList(llDetectedKey(i),1.0);&lt;br /&gt;
            }&lt;br /&gt;
            if (command == &amp;quot;unpass&amp;quot;)&lt;br /&gt;
            {&lt;br /&gt;
                llRemoveFromLandPassList(llDetectedKey(i));&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        command = &amp;quot;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llAddToLandPassList]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llRemoveFromLandBanList]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llRemoveFromLandPassList]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llResetLandBanList]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llResetLandPassList]]|}}&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_events&lt;br /&gt;
|also_articles&lt;br /&gt;
|issues&lt;br /&gt;
|haiku=&lt;br /&gt;
 {{Haiku|All are welcome here|except for the following|who have annoyed me}}&lt;br /&gt;
|cat1=Security&lt;br /&gt;
|cat2=Parcel&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlSetGroundTexture&amp;diff=1218371</id>
		<title>LlSetGroundTexture</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlSetGroundTexture&amp;diff=1218371"/>
		<updated>2025-07-25T14:25:55Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added return type&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2&lt;br /&gt;
|func_id=|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|sort=SetGroundTexture&lt;br /&gt;
|func=llSetGroundTexture|sort=SetGroundTexture&lt;br /&gt;
|return_type=integer&lt;br /&gt;
|p1_type=list|p1_name=changes|p1_desc=List of changes to apply to ground textures on the region.&lt;br /&gt;
|func_desc=&lt;br /&gt;
Changes the textures used to paint the terrain of a region. The owner of the script must be able to manage the estate.&lt;br /&gt;
|return_text&lt;br /&gt;
|spec&lt;br /&gt;
|caveats=&lt;br /&gt;
* This function must be executed from a script in an object owned by the region owner or an estate manager.&lt;br /&gt;
* This function is throttled at 4 calls every 2 minutes.&lt;br /&gt;
* PBR terrain is all or nothing. Mixing normal terrain textures with PBR textures will cause the PBR textures to appear gray.&lt;br /&gt;
|constants=&lt;br /&gt;
{{LSL Constants/GroundTexture}}&lt;br /&gt;
|examples&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llModifyLand]]|Modifies terrain contours.}}&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_events&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes&lt;br /&gt;
|cat1=Ground&lt;br /&gt;
|cat2=Region&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlSetAgentEnvironment&amp;diff=1218370</id>
		<title>LlSetAgentEnvironment</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlSetAgentEnvironment&amp;diff=1218370"/>
		<updated>2025-07-25T14:23:23Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Added return type&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|func=llSetAgentEnvironment&lt;br /&gt;
|return_type=integer&lt;br /&gt;
|func_desc=This function sets environment values for an individual agent in an experience.  The changes to the environment persist until the agent moves to a new region or llSetAgentEnvironment is called for an agent with an empty list. Passing an empty list in params will strip all environmental settings applied to this agent as part of the experience&lt;br /&gt;
|p1_type= key|p1_name= agent_id|p1_desc= The key for an agent in the region.  The agent must be in the region and must be participating in the experience. &lt;br /&gt;
|p2_type= float|p2_name= transition|p2_desc= The number of seconds over which to transition to the new settings.&lt;br /&gt;
|p3_type= list|p3_name= params|p3_desc= A list of parameters to retrieve from the current environment. See table below for details.&lt;br /&gt;
|constants=&lt;br /&gt;
{{{!}} class=&amp;quot;sortable&amp;quot; {{Prettytable|style=margin-top:0;}}&lt;br /&gt;
{{!}}+ &#039;&#039;&#039;Return Values&#039;&#039;&#039;&lt;br /&gt;
{{!}}-{{Hl2}}&lt;br /&gt;
!Value&lt;br /&gt;
!Constant&lt;br /&gt;
!Description&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} {{!!}} 1 {{!!}} The agent has been instructed to change their environment.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} ENV_NOT_EXPERIENCE {{!!}} -1 {{!!}}The script is not running as part of an experience with a valid experience key.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} ENV_NO_EXPERIENCE_PERMISSION {{!!}} -2 {{!!}} The agent has not granted permission.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} ENV_NO_ENVIRONMENT {{!!}} -3 {{!!}} The environment inventory object could not be found.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} ENV_INVALID_AGENT {{!!}} -4 {{!!}} Unable to find specified agent.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} ENV_INVALID_RULE {{!!}} -5 {{!!}} There was an issue with one of the rules.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} ENV_VALIDATION_FAIL {{!!}} -6 {{!!}} Unable to validate values passed.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} ENV_NO_EXPERIENCE_LAND {{!!}} -7 {{!!}} The experience has not been enabled or can not run on the land.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} ENV_THROTTLE {{!!}} -8 {{!!}} The scripts have exceeded the throttle.  Wait and retry the request.&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
{{:LSL Constants/llSetEnvironment}}&lt;br /&gt;
&lt;br /&gt;
|caveats= &lt;br /&gt;
* The list of valid parameters differs from those available for [[llGetEnvironment]].&lt;br /&gt;
* The agent&#039;s viewer may choose to ignore this command.&lt;br /&gt;
* An environment set locally on the viewer will override any environment set from this function.&lt;br /&gt;
|examples=&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
float gTransitionTime = 3.0;&lt;br /&gt;
list gListEnvironmentParams = [&lt;br /&gt;
    SKY_CLOUD_TEXTURE, TEXTURE_PLYWOOD,&lt;br /&gt;
    SKY_GAMMA, 10.0,&lt;br /&gt;
    WATER_NORMAL_SCALE, &amp;lt;5.0, 5.0, 5.0&amp;gt;&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer total_number)&lt;br /&gt;
    {&lt;br /&gt;
        key person = llDetectedKey(0);&lt;br /&gt;
        if (llGetAgentSize(person) != ZERO_VECTOR)&lt;br /&gt;
        {&lt;br /&gt;
            llRequestExperiencePermissions(person, &amp;quot;&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            llInstantMessage(person, &amp;quot;You need to be in the same region to change environment&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    experience_permissions(key agent_id)&lt;br /&gt;
    {&lt;br /&gt;
        integer envTest = llSetAgentEnvironment(agent_id, gTransitionTime, gListEnvironmentParams);&lt;br /&gt;
        if (envTest == 1)&lt;br /&gt;
        {&lt;br /&gt;
            llRegionSayTo(agent_id, 0, &amp;quot;Applying environment for &amp;quot; + (string)agent_id);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            llRegionSayTo(agent_id, 0, &amp;quot;Cannot apply environment for &amp;quot; + (string)agent_id + &amp;quot; due to reason id: &amp;quot; + (string)envTest);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    experience_permissions_denied(key agent_id, integer reason)&lt;br /&gt;
    {&lt;br /&gt;
        llRegionSayTo(agent_id, 0, &amp;quot;Denied experience permissions for &amp;quot; + (string)agent_id + &amp;quot; due to reason id: &amp;quot; + (string)reason);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions={{LSL DefineRow||[[llReplaceAgentEnvironment]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetEnvironment]]|}}&lt;br /&gt;
|notes&lt;br /&gt;
|cat1=Experience&lt;br /&gt;
|cat2=Permissions/Experience&lt;br /&gt;
}}&lt;br /&gt;
[[Category:Experience Tools]]&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=OPT_CHARACTER&amp;diff=1218355</id>
		<title>OPT CHARACTER</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=OPT_CHARACTER&amp;diff=1218355"/>
		<updated>2025-07-07T12:49:39Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Fixed name.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Constant&lt;br /&gt;
|name=OPT_CHARACTER&lt;br /&gt;
|type=integer&lt;br /&gt;
|value=2&lt;br /&gt;
|hvalue=&lt;br /&gt;
|desc&lt;br /&gt;
|examples&lt;br /&gt;
|caveats=&lt;br /&gt;
|functions={{LSL DefineRow||[[llGetObjectDetails]]|}}&lt;br /&gt;
|events=&lt;br /&gt;
|cat1&lt;br /&gt;
|cat2&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=CLICK_ACTION_DISABLED&amp;diff=1218354</id>
		<title>CLICK ACTION DISABLED</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=CLICK_ACTION_DISABLED&amp;diff=1218354"/>
		<updated>2025-07-07T12:27:26Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Fixed name.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Constant&lt;br /&gt;
|name=CLICK_ACTION_DISABLED&lt;br /&gt;
|type=integer&lt;br /&gt;
|value=8&lt;br /&gt;
|desc&lt;br /&gt;
|examples&lt;br /&gt;
|functions=&lt;br /&gt;
{{LSL DefineRow||[[llSetClickAction]]|}}&lt;br /&gt;
{{LSL DefineRow||[[llGetObjectDetails]]|}}&lt;br /&gt;
|events&lt;br /&gt;
|haiku={{Haiku|Immense horizons|A disabled click touches|into the action}}&lt;br /&gt;
|cat1=Click Action&lt;br /&gt;
|cat2&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
|self&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Http_request&amp;diff=1218316</id>
		<title>Http request</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Http_request&amp;diff=1218316"/>
		<updated>2025-05-23T23:36:35Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Event|event_id&lt;br /&gt;
|event=http_request|event_id=33|event_delay&lt;br /&gt;
|p1_type=key|p1_subtype=handle|p1_name=request_id|p1_desc=HTTP request id for response use, and function response identification.&lt;br /&gt;
|p2_type=string|p2_name=method|p2_desc=&amp;lt;code&amp;gt;{{String|GET}}&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;{{String|POST}}&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;{{String|PUT}}&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;{{String|DELETE}}&amp;lt;/code&amp;gt;, [[URL_REQUEST_GRANTED]], [[URL_REQUEST_DENIED]]|p2_hover={{String|GET}}, {{String|POST}}, {{String|PUT}}, {{String|DELETE}}, URL_REQUEST_GRANTED, URL_REQUEST_DENIED&lt;br /&gt;
|p3_type=string|p3_name=body|p3_desc=Contents of the request.&lt;br /&gt;
|event_desc=Triggered when task receives an HTTP request.&lt;br /&gt;
|constants=&lt;br /&gt;
|spec=See [[LSL_http_server]] for full specification.&lt;br /&gt;
|caveats=&lt;br /&gt;
* {{LSLP|body}} is [[limit]]ed to 2048 bytes; anything longer will be truncated to 2048 bytes.&lt;br /&gt;
** As per the above, this function is unaffected by any values passed to [[llHTTPRequest]] via [[HTTP_BODY_MAXLENGTH]], as they reside on the [[:Category:LSL HTTP#Understanding_LSL_HTTP_Communications | Outgoing]] pipeline, and this function is on the [[:Category:LSL HTTP#Understanding_LSL_HTTP_Communications | Incoming]] pipeline.&lt;br /&gt;
* headers (accessed with [[llGetHTTPHeader]]) are limited to 255 bytes.&lt;br /&gt;
* There is a limit of 64 pending http_request&lt;br /&gt;
** Be mindful of the event queue (64) in general as well. http_request events may be discarded if your script&#039;s queue is filled by other events, such as incoming [[listen]] messages.&lt;br /&gt;
* {{LSLP|body}} is not sent with the request unless the method is set to &amp;lt;code&amp;gt;{{String|POST}}&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;{{String|PUT}}&amp;lt;/code&amp;gt;, or [[URL_REQUEST_GRANTED]].&lt;br /&gt;
* Requests may fail with a [https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_Error 503 status code] for at least two possible reasons:&lt;br /&gt;
** if the returned body is &amp;lt;code&amp;gt;ERROR: The requested URL could not be retrieved&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;(111) Connection refused&amp;lt;/code&amp;gt;, the problem will normally last only a few minutes - this is th nightly maint &amp;amp; log rotation.  It does reliably impact object to object HTTP at that time, and quite probably may impact object to/from web around the same time.  The interruption in service is fairly brief, and the precise timing may vary as LL adjust their nightly maint processes, or due to server load.&lt;br /&gt;
** this response can also happen if too many requests are received by scripts in the region owned by the same owner. In this case, a Retry-After header is returned with a number of seconds to wait before retrying. Note that if you have many requestors, some of them may have to wait longer. A good general strategy is to wait the suggested number of seconds (possibly with a little randomness added in) before retrying, and if that fails increase how long you wait by multiplying by a small number before retrying again. If all your requestors follow some method like this, eventually they&#039;ll get through.&lt;br /&gt;
|examples=&lt;br /&gt;
See [[LSL_http_server/examples]] for some examples from the feature design phase.&lt;br /&gt;
{{LSL Tip|Never ever forget to release a URL again which you have requested! URLs are region resources just like prims. If you take them all you can get into big trouble with the sim owner and/or estate managers.}}&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
string url;&lt;br /&gt;
key urlRequestId;&lt;br /&gt;
key selfCheckRequestId;&lt;br /&gt;
&lt;br /&gt;
request_url()&lt;br /&gt;
{&lt;br /&gt;
    llReleaseURL(url);&lt;br /&gt;
    url = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    urlRequestId = llRequestURL();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
throw_exception(string inputString)&lt;br /&gt;
{&lt;br /&gt;
    key owner = llGetOwner();&lt;br /&gt;
    llInstantMessage(owner, inputString);&lt;br /&gt;
&lt;br /&gt;
    // yeah, bad way to handle exceptions by restarting.&lt;br /&gt;
    // However this is just a demo script...&lt;br /&gt;
&lt;br /&gt;
    llResetScript();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    on_rez(integer start_param)&lt;br /&gt;
    {&lt;br /&gt;
        llResetScript();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    changed(integer change)&lt;br /&gt;
    {&lt;br /&gt;
        if (change &amp;amp; (CHANGED_OWNER | CHANGED_INVENTORY))&lt;br /&gt;
        {&lt;br /&gt;
            llReleaseURL(url);&lt;br /&gt;
            url = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            llResetScript();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (change &amp;amp; (CHANGED_REGION | CHANGED_REGION_START | CHANGED_TELEPORT))&lt;br /&gt;
            request_url();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        request_url();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    http_request(key id, string method, string body)&lt;br /&gt;
    {&lt;br /&gt;
        integer responseStatus = 400;&lt;br /&gt;
        string responseBody = &amp;quot;Unsupported method&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        if (method == URL_REQUEST_DENIED)&lt;br /&gt;
            throw_exception(&amp;quot;The following error occurred while attempting to get a free URL for this device:\n \n&amp;quot; + body);&lt;br /&gt;
&lt;br /&gt;
        else if (method == URL_REQUEST_GRANTED)&lt;br /&gt;
        {&lt;br /&gt;
            url = body;&lt;br /&gt;
            key owner = llGetOwner();&lt;br /&gt;
            llLoadURL(owner, &amp;quot;Click to visit my URL!&amp;quot;, url);&lt;br /&gt;
&lt;br /&gt;
            // check every 5 mins for dropped URL&lt;br /&gt;
            llSetTimerEvent(300.0);&lt;br /&gt;
        }&lt;br /&gt;
        else if (method == &amp;quot;GET&amp;quot;)&lt;br /&gt;
        {&lt;br /&gt;
            responseStatus = 200;&lt;br /&gt;
            responseBody = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        // else if (method == &amp;quot;POST&amp;quot;) ...;&lt;br /&gt;
        // else if (method == &amp;quot;PUT&amp;quot;) ...;&lt;br /&gt;
        // else if (method == &amp;quot;DELETE&amp;quot;) { responseStatus = 403; responseBody = &amp;quot;forbidden&amp;quot;; }&lt;br /&gt;
&lt;br /&gt;
        llHTTPResponse(id, responseStatus, responseBody);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    http_response(key id, integer status, list metaData, string body)&lt;br /&gt;
    {&lt;br /&gt;
        if (id == selfCheckRequestId)&lt;br /&gt;
        {&lt;br /&gt;
            // If you&#039;re not usually doing this,&lt;br /&gt;
            // now is a good time to get used to doing it!&lt;br /&gt;
            selfCheckRequestId = NULL_KEY;&lt;br /&gt;
&lt;br /&gt;
            if (status != 200)&lt;br /&gt;
                request_url();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        else if (id == NULL_KEY)&lt;br /&gt;
            throw_exception(&amp;quot;Too many HTTP requests too fast!&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    timer()&lt;br /&gt;
    {&lt;br /&gt;
        selfCheckRequestId = llHTTPRequest(url,&lt;br /&gt;
                                [HTTP_METHOD, &amp;quot;GET&amp;quot;,&lt;br /&gt;
                                    HTTP_VERBOSE_THROTTLE, FALSE,&lt;br /&gt;
                                    HTTP_BODY_MAXLENGTH, 16384],&lt;br /&gt;
                                &amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_articles&lt;br /&gt;
|also_events&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llRequestURL]]|Request a new LSL Server public URL}}&lt;br /&gt;
{{LSL DefineRow||[[llRequestSecureURL]]|Request a new LSL Server public URL}}&lt;br /&gt;
{{LSL DefineRow||[[llReleaseURL]]|Release a URL}}&lt;br /&gt;
{{LSL DefineRow||[[llHTTPResponse]]|For replying to HTTP requests}}&lt;br /&gt;
{{LSL DefineRow||[[llGetHTTPHeader]]|Returns the requested HTTP header&#039;s value}}&lt;br /&gt;
{{LSL DefineRow||[[llEscapeURL]]}}&lt;br /&gt;
{{LSL DefineRow||[[llUnescapeURL]]}}&lt;br /&gt;
|also_footer&lt;br /&gt;
|notes=&lt;br /&gt;
* When triggered by either [[llRequestURL]] or [[llRequestSecureURL]] the method will be either [[URL_REQUEST_GRANTED]] or [[URL_REQUEST_DENIED]]&lt;br /&gt;
** If the method is [[URL_REQUEST_GRANTED]] The body will contain the full url, minus the trailing forward slash.&lt;br /&gt;
|issues=&lt;br /&gt;
{{Issue/V1|SVC-1086|Design: LSL http_server|type=fs|status=ip}}&lt;br /&gt;
|cat1=HTTP&lt;br /&gt;
|cat2=HTTP/Server&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Http_request&amp;diff=1218315</id>
		<title>Http request</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Http_request&amp;diff=1218315"/>
		<updated>2025-05-23T23:32:16Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Note about pending events.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Event|event_id&lt;br /&gt;
|event=http_request|event_id=33|event_delay&lt;br /&gt;
|p1_type=key|p1_subtype=handle|p1_name=request_id|p1_desc=HTTP request id for response use, and function response identification.&lt;br /&gt;
|p2_type=string|p2_name=method|p2_desc=&amp;lt;code&amp;gt;{{String|GET}}&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;{{String|POST}}&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;{{String|PUT}}&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;{{String|DELETE}}&amp;lt;/code&amp;gt;, [[URL_REQUEST_GRANTED]], [[URL_REQUEST_DENIED]]|p2_hover={{String|GET}}, {{String|POST}}, {{String|PUT}}, {{String|DELETE}}, URL_REQUEST_GRANTED, URL_REQUEST_DENIED&lt;br /&gt;
|p3_type=string|p3_name=body|p3_desc=Contents of the request.&lt;br /&gt;
|event_desc=Triggered when task receives an HTTP request.&lt;br /&gt;
|constants=&lt;br /&gt;
|spec=See [[LSL_http_server]] for full specification.&lt;br /&gt;
|caveats=&lt;br /&gt;
* {{LSLP|body}} is [[limit]]ed to 2048 bytes; anything longer will be truncated to 2048 bytes.&lt;br /&gt;
** As per the above, this function is unaffected by any values passed to [[llHTTPRequest]] via [[HTTP_BODY_MAXLENGTH]], as they reside on the [[:Category:LSL HTTP#Understanding_LSL_HTTP_Communications | Outgoing]] pipeline, and this function is on the [[:Category:LSL HTTP#Understanding_LSL_HTTP_Communications | Incoming]] pipeline.&lt;br /&gt;
* headers (accessed with [[llGetHTTPHeader]]) are limited to 255 bytes.&lt;br /&gt;
* There is a limit of 64 pending http_request&lt;br /&gt;
** Be mindful of the event queue in general as well. http_request events may be discarded if your script&#039;s queue is filled by other events, such as incoming [[listen]] messages.&lt;br /&gt;
* {{LSLP|body}} is not sent with the request unless the method is set to &amp;lt;code&amp;gt;{{String|POST}}&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;{{String|PUT}}&amp;lt;/code&amp;gt;, or [[URL_REQUEST_GRANTED]].&lt;br /&gt;
* Requests may fail with a [https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_Error 503 status code] for at least two possible reasons:&lt;br /&gt;
** if the returned body is &amp;lt;code&amp;gt;ERROR: The requested URL could not be retrieved&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;(111) Connection refused&amp;lt;/code&amp;gt;, the problem will normally last only a few minutes - this is th nightly maint &amp;amp; log rotation.  It does reliably impact object to object HTTP at that time, and quite probably may impact object to/from web around the same time.  The interruption in service is fairly brief, and the precise timing may vary as LL adjust their nightly maint processes, or due to server load.&lt;br /&gt;
** this response can also happen if too many requests are received by scripts in the region owned by the same owner. In this case, a Retry-After header is returned with a number of seconds to wait before retrying. Note that if you have many requestors, some of them may have to wait longer. A good general strategy is to wait the suggested number of seconds (possibly with a little randomness added in) before retrying, and if that fails increase how long you wait by multiplying by a small number before retrying again. If all your requestors follow some method like this, eventually they&#039;ll get through.&lt;br /&gt;
|examples=&lt;br /&gt;
See [[LSL_http_server/examples]] for some examples from the feature design phase.&lt;br /&gt;
{{LSL Tip|Never ever forget to release a URL again which you have requested! URLs are region resources just like prims. If you take them all you can get into big trouble with the sim owner and/or estate managers.}}&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
string url;&lt;br /&gt;
key urlRequestId;&lt;br /&gt;
key selfCheckRequestId;&lt;br /&gt;
&lt;br /&gt;
request_url()&lt;br /&gt;
{&lt;br /&gt;
    llReleaseURL(url);&lt;br /&gt;
    url = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    urlRequestId = llRequestURL();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
throw_exception(string inputString)&lt;br /&gt;
{&lt;br /&gt;
    key owner = llGetOwner();&lt;br /&gt;
    llInstantMessage(owner, inputString);&lt;br /&gt;
&lt;br /&gt;
    // yeah, bad way to handle exceptions by restarting.&lt;br /&gt;
    // However this is just a demo script...&lt;br /&gt;
&lt;br /&gt;
    llResetScript();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    on_rez(integer start_param)&lt;br /&gt;
    {&lt;br /&gt;
        llResetScript();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    changed(integer change)&lt;br /&gt;
    {&lt;br /&gt;
        if (change &amp;amp; (CHANGED_OWNER | CHANGED_INVENTORY))&lt;br /&gt;
        {&lt;br /&gt;
            llReleaseURL(url);&lt;br /&gt;
            url = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            llResetScript();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (change &amp;amp; (CHANGED_REGION | CHANGED_REGION_START | CHANGED_TELEPORT))&lt;br /&gt;
            request_url();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        request_url();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    http_request(key id, string method, string body)&lt;br /&gt;
    {&lt;br /&gt;
        integer responseStatus = 400;&lt;br /&gt;
        string responseBody = &amp;quot;Unsupported method&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        if (method == URL_REQUEST_DENIED)&lt;br /&gt;
            throw_exception(&amp;quot;The following error occurred while attempting to get a free URL for this device:\n \n&amp;quot; + body);&lt;br /&gt;
&lt;br /&gt;
        else if (method == URL_REQUEST_GRANTED)&lt;br /&gt;
        {&lt;br /&gt;
            url = body;&lt;br /&gt;
            key owner = llGetOwner();&lt;br /&gt;
            llLoadURL(owner, &amp;quot;Click to visit my URL!&amp;quot;, url);&lt;br /&gt;
&lt;br /&gt;
            // check every 5 mins for dropped URL&lt;br /&gt;
            llSetTimerEvent(300.0);&lt;br /&gt;
        }&lt;br /&gt;
        else if (method == &amp;quot;GET&amp;quot;)&lt;br /&gt;
        {&lt;br /&gt;
            responseStatus = 200;&lt;br /&gt;
            responseBody = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        // else if (method == &amp;quot;POST&amp;quot;) ...;&lt;br /&gt;
        // else if (method == &amp;quot;PUT&amp;quot;) ...;&lt;br /&gt;
        // else if (method == &amp;quot;DELETE&amp;quot;) { responseStatus = 403; responseBody = &amp;quot;forbidden&amp;quot;; }&lt;br /&gt;
&lt;br /&gt;
        llHTTPResponse(id, responseStatus, responseBody);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    http_response(key id, integer status, list metaData, string body)&lt;br /&gt;
    {&lt;br /&gt;
        if (id == selfCheckRequestId)&lt;br /&gt;
        {&lt;br /&gt;
            // If you&#039;re not usually doing this,&lt;br /&gt;
            // now is a good time to get used to doing it!&lt;br /&gt;
            selfCheckRequestId = NULL_KEY;&lt;br /&gt;
&lt;br /&gt;
            if (status != 200)&lt;br /&gt;
                request_url();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        else if (id == NULL_KEY)&lt;br /&gt;
            throw_exception(&amp;quot;Too many HTTP requests too fast!&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    timer()&lt;br /&gt;
    {&lt;br /&gt;
        selfCheckRequestId = llHTTPRequest(url,&lt;br /&gt;
                                [HTTP_METHOD, &amp;quot;GET&amp;quot;,&lt;br /&gt;
                                    HTTP_VERBOSE_THROTTLE, FALSE,&lt;br /&gt;
                                    HTTP_BODY_MAXLENGTH, 16384],&lt;br /&gt;
                                &amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_articles&lt;br /&gt;
|also_events&lt;br /&gt;
|also_functions=&lt;br /&gt;
{{LSL DefineRow||[[llRequestURL]]|Request a new LSL Server public URL}}&lt;br /&gt;
{{LSL DefineRow||[[llRequestSecureURL]]|Request a new LSL Server public URL}}&lt;br /&gt;
{{LSL DefineRow||[[llReleaseURL]]|Release a URL}}&lt;br /&gt;
{{LSL DefineRow||[[llHTTPResponse]]|For replying to HTTP requests}}&lt;br /&gt;
{{LSL DefineRow||[[llGetHTTPHeader]]|Returns the requested HTTP header&#039;s value}}&lt;br /&gt;
{{LSL DefineRow||[[llEscapeURL]]}}&lt;br /&gt;
{{LSL DefineRow||[[llUnescapeURL]]}}&lt;br /&gt;
|also_footer&lt;br /&gt;
|notes=&lt;br /&gt;
* When triggered by either [[llRequestURL]] or [[llRequestSecureURL]] the method will be either [[URL_REQUEST_GRANTED]] or [[URL_REQUEST_DENIED]]&lt;br /&gt;
** If the method is [[URL_REQUEST_GRANTED]] The body will contain the full url, minus the trailing forward slash.&lt;br /&gt;
|issues=&lt;br /&gt;
{{Issue/V1|SVC-1086|Design: LSL http_server|type=fs|status=ip}}&lt;br /&gt;
|cat1=HTTP&lt;br /&gt;
|cat2=HTTP/Server&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=PBR_Materials&amp;diff=1218189</id>
		<title>PBR Materials</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=PBR_Materials&amp;diff=1218189"/>
		<updated>2025-04-18T03:46:46Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Reformatted probe ambiance values into a table. /* Fine-Tuning Reflection Sample Volumes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{multi-lang|1=PBR_Materials|2=/en}}&lt;br /&gt;
&#039;&#039;&#039;Physically Based Rendering&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{{note|1=This page is being actively updated with new content and links as GLTF PBR Materials and related new features are being released. Contributions to this page may be from Second Life residents and staff members alike.}}&lt;br /&gt;
&lt;br /&gt;
[[Category:glTF]]&lt;br /&gt;
&lt;br /&gt;
== What is it and Why is it used? ==&lt;br /&gt;
&lt;br /&gt;
The term “Physically Based Rendering” or “PBR” is a technical term that may need some defining for most people since its use is unique to composing images in computer software.  The term itself is an abbreviation for a collection of complex mathematical algorithms that attempt to accurately represent the ways that light reflects off and interacts with objects in the real world. In the real world, it is the behavior of light on a piece of metal that allows us, the observer, to recognize “that object is made of metal” without actually reaching out and touching it.  The way a metal reflects light differs from that of a polished plastic or some other material, and these differences have been quantified by science.  By mimicking real-world physics principles in the virtual world it allows for the creation of more immersive recognizable realistic spaces, but also it helps us relate to fantastical worlds a little better too.  While we may not be familiar with what a newly imagined creation is, a metal&#039;s inherent metal-ness and aged wood&#039;s inherent wood-ness remain constant, making it easier to intuitively understand what we are interacting with in a virtual environment.&lt;br /&gt;
&lt;br /&gt;
Because tying the mathematics to simulate materials in virtual spaces to how they behave in the real-world makes things more immediately recognizable, PBR has become the foundation for creating imagined worlds over the last decade.  The metallic shine of exoskeletal armor in superhero movies is driven by a PBR workflow, as is the plastic sheen of toys or the glint of frozen ice in animated classics.  Now we are moving to bring this standardized quality to your home in Second Life.&lt;br /&gt;
&lt;br /&gt;
Bringing PBR to Second Life means updating the basic calculations of how light is represented and interacts with the world of Second Life.  The goal is to integrate these changes while minimally changing how everything that presently exists in Second Life (designed prior to the introduction of PBR) looks.  While the preservation of creative intent and the aesthetic appeal of items users have enjoyed for over two decades of Second Life is always a priority, Second Life is an ever-evolving platform, and to continue to do so, some changes are inevitable.&lt;br /&gt;
&lt;br /&gt;
Lastly, while we are attempting to mimic real-world reflections and material properties, Second Life has to run on a wide variety of devices, so some shortcuts have to be made.  Reflections are not mirror-perfect, as has often been a long-standing hope and request from Residents.  While the addition of a reflection system does bring dreams of distortion free mirrors for our avatars in Second Life closer to reality, unfortunately, due to the calculation requirements of doing perfect reflections, mirrors are sadly still, for the moment, not practical. Please see the note in the [[#Understanding_and_Assisting_the_New_Reflections_System|section below]] for more info.&lt;br /&gt;
&lt;br /&gt;
== What does this mean for Residents? ==&lt;br /&gt;
[[File:PrePostPBR.png | thumb | right | The lamp in this screenshot uses [[PBR_Materials#Nomenclature_changes|Blinn-Phong]] (Classic SL Materials), and was created before PBR&#039;s integration into SL; and demonstrates some of the visual differences to existing content. The environment used for the screenshot is the viewer&#039;s default Midday preset (Note that the PBR viewer has a new Midday preset).&lt;br /&gt;
]]&lt;br /&gt;
=== Changes to Existing Content ===&lt;br /&gt;
The largest change by far is the addition of an environmental reflection system to Second Life.  For most existing items, this change shouldn’t have a drastic immediately observable impact.  A few things in your inventory that you already own may appear more reflective with the new graphics configuration and those reflections should feel more realistic and immersive with your current environment.  As a general rule: the “shinier” an object was before, the more environment reflections it will pick up and the more visual difference there will be.&lt;br /&gt;
&lt;br /&gt;
Another notable change is the addition of [https://en.wikipedia.org/wiki/Tone_mapping tonemapping] to the viewer. Tonemapping is a way of representing an image with a higher native dynamic range than the display can support. Tonemapping is a de-facto requirement of PBR pipelines. This means that colors in Second Life will generally appear more saturated with less detail being lost in shadows and highlights. [https://modelviewer.dev/examples/color.html Click here for some additional reading on the subject.]&lt;br /&gt;
&lt;br /&gt;
The tonemapping method used is called &#039;&#039;&#039;Academy Color Encoding System (&#039;&#039;&#039;aka &#039;&#039;&#039;&amp;quot;ACES&amp;quot;)&#039;&#039;&#039;, and can be read about [https://www.oscars.org/science-technology/sci-tech-projects/aces here] and [https://docs.nvidia.com/gameworks/index.html#devices/shield-hdr-dev-guide/hdr-dev-guide-nvidia-shield.htm here].&lt;br /&gt;
&lt;br /&gt;
[[PBR_Materials#Nomenclature_changes|Blinn-Phong]] content making use of the Specular parameters may look different if the items were never viewed under local lights, as the PBR viewer allows for the environment (sun &#039;&#039;and&#039;&#039; sky) to contribute to specular reflections. As such, if the object receives blue specular reflections from the sky, these reflections are tinted, and may look odd. This effect is the same as prior viewers, as if the object was lit by a blue local light.&lt;br /&gt;
&lt;br /&gt;
=== Removal of Advanced Lighting Model ( ALM ) Graphics Option ===&lt;br /&gt;
There have been changes to the &#039;&#039;&#039;Graphics &amp;gt; Advanced Settings...&#039;&#039;&#039; Preferences. The most notable of which is the removal of the “Atmospheric Shaders”, &amp;quot;Local lights&amp;quot; and “Advanced Lighting Model” options.&lt;br /&gt;
&lt;br /&gt;
For those users on lower-end hardware who depended heavily upon those options to navigate Second Life with an acceptable framerate, we recommend the following settings:&lt;br /&gt;
*{{code|Transparent Water: Disabled}}&lt;br /&gt;
*{{code|Screen Space Ambient Occlusion: Disabled}}&lt;br /&gt;
*{{code|Shadows: None}}&lt;br /&gt;
*{{code|Screen Space Reflections: Disabled}}&lt;br /&gt;
*{{code|Reflection Detail: Static Only}}&lt;br /&gt;
*{{code|Reflection Coverage: Manual only}}&lt;br /&gt;
&lt;br /&gt;
The {{code|Local Lights}} setting has been superceded by updates made to the &#039;&#039;&#039;World &amp;gt; Improve graphics speed...&#039;&#039;&#039; &amp;quot;Quality &amp;amp; Speed&amp;quot; slider options. Users on low-end hardware should ensure that their &amp;quot;Quality &amp;amp; Speed&amp;quot; slider is set at an appropriate level for their hardware.&lt;br /&gt;
&lt;br /&gt;
== What does this mean for Creators? ==&lt;br /&gt;
=== Nomenclature changes ===&lt;br /&gt;
The PBR project represents a large step towards integrating standard rendering techniques used in the games industry. As such, the nomenclature of some items has changed, notably, what was once called &#039;&#039;&#039;[[:Category:Materials|Materials]]&#039;&#039;&#039; is now referred to as &#039;&#039;&#039;Blinn-Phong&#039;&#039;&#039;. This does not represent any changes to the underlying rendering techniques (beyond those mentioned above); and as such Blinn-Phong &#039;&#039;is not&#039;&#039; the same as PBR Spec/Gloss workflows, seen in some game engines such as Unity.&lt;br /&gt;
&lt;br /&gt;
=== Importing PBR Materials from External Software: ===&lt;br /&gt;
For creators who work with external tools such as Blender, Adobe Substance Painter, Cinema4D, 3D-Coat, or have used the Unreal or Godot game engines, the use of PBR texture sets should already be familiar.  In fact, some creators have been using tools that use PBR workflows to create content for Second Life and have then been forced to sacrifice visual quality to convert that information into Second Life’s existing Blinn-Phong materials system.  &lt;br /&gt;
&lt;br /&gt;
Second Life is adopting the “Metallic/Roughness” PBR model, and in its ongoing commitment to using Open Source standards whenever it is practical to do so, the glTF 2.0 file format has been chosen as the upload format for PBR Material assets.  One of the primary goals of implementing PBR Materials is to have more continuity from content creation applications towards Second Life, and have more consistent content behavior once it’s inworld.&lt;br /&gt;
&lt;br /&gt;
=== Using Imported Materials in Second Life ===&lt;br /&gt;
[[File:Editing_Material.jpg|thumb|right|Material Editing]]&lt;br /&gt;
For people who build exclusively within Second Life, the addition of PBR also means that there will be a new [[LlGetInventoryType|Inventory Type]] called “Material”.  These new Materials can be purchased on the Marketplace and are shareable like any other permitted object in Second Life.&lt;br /&gt;
&lt;br /&gt;
PBR materials come as a bundle of textures.  These all travel as a single unit and are applied all at once.  If you wish to change tint, transparency, or other similar parameters of the Material you’ll need to modify the Material from the &amp;quot;Editing Material&amp;quot; floater.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Meshes uploaded after the PBR release will look slightly different (and more accurate to your editor of choice) due to a change in the way Second Life handles mesh assets (Previously, some data required for accurate tangent generation was discarded at upload time - this is no longer the case). This will mean that a PBR material applied to a mesh uploaded before PBR launched (28th November 2023) might look incorrect; a simple reupload of the mesh will solve this. &#039;&#039;&#039;This is not required, but recommended.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
Applying existing materials works similarly to existing textures. You have two means of doing so:&lt;br /&gt;
&lt;br /&gt;
==== Drag and Drop ====&lt;br /&gt;
Simply drag and drop onto the face of a prim or a mesh.  For example, if you have a tiled floor material and you place it on a selected prim cube face that face will now look like a tile floor and reflect light like a tiled floor would with all the material qualities contained in the material.&lt;br /&gt;
[[File:DragAndDrop_Mat_Wiki.png | thumb | none | Drag and Drop Functionality.]]&lt;br /&gt;
&lt;br /&gt;
==== Switch to PBR and select from Inventory ====&lt;br /&gt;
Alternatively, select a face on the prim or mesh you want to apply your material to and choose the “Blinn-Phong” drop-down and change it to “PBR Metallic Roughness”, then select “Choose an item from your inventory” and apply.&lt;br /&gt;
[[File:PBRSelectorV3.png |left| thumb | none | Menu drop-down demonstration.]][[File:Material_Dropdown.jpg|thumb|none|Dropdown as of v7.0.1.689]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Texture Transforms ====&lt;br /&gt;
With PBR materials, texture transforms work in a slightly different way to Blinn-Phong transforms. Blinn-Phong (and OpenGL) has the texture origin in the lower left corner of the texture, whereas glTF materials (and Vulkan) has the texture origin in the &amp;lt;b&amp;gt;upper left&amp;lt;/b&amp;gt; corner.&lt;br /&gt;
&lt;br /&gt;
This means that PBR materials will react differently (namely, they are inverted in the Y (glTF v) axis) to Blinn-Phong materials.&lt;br /&gt;
For a simple solution to convert a Blinn-Phong texture transform to a PBR texture transform, please see [https://community.secondlife.com/forums/topic/507448-lsl-math-for-gltf-transformation/?do=findComment&amp;amp;comment=2678959 this forum thread].&lt;br /&gt;
&lt;br /&gt;
For more information, [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#images see here] and [https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform here].&lt;br /&gt;
&lt;br /&gt;
=== Media-on-a-Prim ===&lt;br /&gt;
Media on a prim will continue to work largely as it has done prior to PBR&#039;s launch.&lt;br /&gt;
&lt;br /&gt;
When using MOAP on a face with a PBR texture, it will function as if the media texture has an override to the &#039;&#039;&#039;Base Color&#039;&#039;&#039; and &#039;&#039;&#039;Emissive&#039;&#039;&#039; maps. Note that the emissive map is overridden, however the emissive tint is not (so, to disable the emissive map you can set the emissive tint to black &amp;lt;0,0,0&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Tools &amp;amp; Tutorials ==&lt;br /&gt;
As the PBR system is new, it is expected that existing users may be confused at first on how everything works. As such, Linden Lab and some third parties have provided some tutorials and informational videos:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=JtjGf06B8ZA Second Life University - PBR Materials]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=BJ1eRTlWDYk Second Life University - How to create PBR Materials]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://substance3d.adobe.com/tutorials/courses/pbrguide Allegorithmic (Adobe) PBR Guide]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The above tutorials require knowledge and access to Adobe&#039;s Substance Painter (subscription based software on Adobe website, or sold as perpetual license with one year of updates on Steam). Below are some open source tools and links found Googling &#039;Blender&#039;, &#039;glTF&#039; (requiring Blender 3.3)&lt;br /&gt;
*&#039;&#039;&#039;[https://www.khronos.org/blog/art-pipeline-for-gltf Khronos Art Pipeline tutorial]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://materialmaker.org Material Maker software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://aiaicapta.in/gltf-packer/ glTF Packer software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://armorpaint.org Armorpaint software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://armorlab.org Armorlab software]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://youtu.be/KTPdNUGwIGc glTF Blender Tutorial]&#039;&#039;&#039;&lt;br /&gt;
*&#039;&#039;&#039;[https://youtu.be/p7OPRoT6FkY Exporting glTF Alpha Textures tutorial]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== PBR Stand-In Textures and Outdated Viewers ==&lt;br /&gt;
It is worth mentioning that during the adoption period of a new system, a substantial portion of Second Life Residents view the world through third party viewers and mobile viewer solutions that will not have updated to be able to see the new content.  Anyone viewing a PBR Material on a viewer that cannot display it will see the “underlying” non-PBR texture.  By default, this is a pine box or a completely blank texture.  Those who wish their content to be viewable by as many people as possible, might consider creating a Diffuse texture ( with baked lighting, the kind that is easily generated with tools like Substance Painter&#039;s “Baked Lighting Filter” ) and applying it as a regular texture to the object they’re placing the PBR material on, prior to applying the PBR material.  While this is an extra step in content creation, and complicates things somewhat, if you wish for your content to be appreciated by all, it’s worth considering adding this extra step to your workflow.  People designing PBR materials for distribution and sale might also consider offering a “Diffuse Only fallback” texture to accompany the PBR material specifically for people who cannot see the PBR content.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Once a PBR Material is applied, the Blinn-Phong textures cannot be updated. To remedy this, remove the PBR Material, then make changes to the Blinn-Phong textures and then reapply the PBR Material.}}&lt;br /&gt;
&lt;br /&gt;
== Understanding and Assisting the New Reflections System ==&lt;br /&gt;
Those creators that work at house-scale, or produce items that can be walked through, can gain additional control over the lighting in their creations by taking the time to fully understand the new toolkit that influences environmental reflections. There is a new type of volume that can be created and appended to object linksets specifically for scenes with these kinds of spaces.  These volumes define a custom area where reflections are calculated, overriding the default solution.  So-called “Reflection Probes” should be placed in a manner such that &#039;&#039;the fewest number of probes fills the largest amount of space possible with minimal overlap&#039;&#039;.  One probe per room is a good reference point for a general living space like a house.  If multiple probes exist in a given area they can cause visual artifacts and negatively impact performance  (also known as viewer lag).  &#039;&#039;&#039;&#039;&#039;Do not affix reflection probes to small creations such as furniture or decor.&#039;&#039;&#039;&#039;&#039;  Small items such as tables, chairs, musical instruments, candle sticks etc should use the reflection sample volume in the space in which they are placed.  They should not have one appended.  It is &#039;&#039;&#039;strongly&#039;&#039;&#039; recommended that any object that contains a reflection sample probe be left as “modify”, so the positioning of the probe can be adjusted or even removed by the owner of the item should they wish.&lt;br /&gt;
&lt;br /&gt;
{{Enote|Reflection probes &#039;&#039;&#039;are not&#039;&#039;&#039; intended for use with planar mirrors, and will look incorrect when being used to do so. Planar mirrors have been flagged for future work, but are not directly in the scope of the PBR project.}}&lt;br /&gt;
&amp;lt;!-- This image has been removed from public view as it is factually incorrect; as it references the blue tint on the floor as originating from the ocean, and not the sky. Note that the public project viewer currently also has a bug wherein the sky contribution on objects is too powerful; this has since been fixed however a public build containing the fix is yet to be released. [[File:ProblePlacementDemo_v.png | right | A visual demonstration of how manual probes interact with the surrounding environment and enhance it.]] --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== When is it Recommended to Create a Reflection Probe? ==&lt;br /&gt;
Manually placed probes are good for cleaning up undesirable noise and lighting from automatically placed probes that may be visually disruptive or confusing.  If you look in the image of the room in a house, on the left hand side of it, you can see a blue tint on the floor, which is the reflected blue light from the sky.  The probe filling half the room blocks this, and when it is extended to completely fill the room and just slightly beyond the thickness of the walls, (lower copy of the image) the problem is solved.  The probe does not need to be a part of the linkset for the rest of the room in order to function; however, once you have them placed it’s possible to add them into linksets like any other prim.&lt;br /&gt;
&lt;br /&gt;
=== Constructing a Reflection Sample Volume ( aka Reflection Probe ) ===&lt;br /&gt;
Start off in the Build Menu &#039;&#039;(Ctrl+3)&#039;&#039; by rezzing a basic prim. Under the &amp;lt;code&amp;gt;Features&amp;lt;/code&amp;gt; tab, at the bottom check the box labelled &amp;lt;code&amp;gt;Reflection Probe&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[File:ProbeUI.jpg | thumb | none | Manual Probe Creation UI.]]&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Reflection Probe&amp;lt;/code&amp;gt;:  Enabling this creates a Reflection Sampling Volume within the bounding box of the prim.&lt;br /&gt;
* &amp;lt;code&amp;gt;Box / Sphere Drop-down&amp;lt;/code&amp;gt;: Choose whether you want this sample to project reflections within a box shaped volume or a spherical one.&lt;br /&gt;
* &amp;lt;code&amp;gt;Dynamic&amp;lt;/code&amp;gt;: Allow skinned objects (e.g. Avatars, animesh, etc.) to be captured in the reflection.&lt;br /&gt;
* &amp;lt;code&amp;gt;Ambiance&amp;lt;/code&amp;gt;: Affects how much objects within the volume are lit as if they have bounced light hitting them from faked indirect illumination. &#039;&#039;See [[PBR_Materials#Fine-Tuning_Reflection_Sample_Volumes|&amp;quot;Fine-Tuning Reflection Sample Volumes&amp;quot;]] for more info.&lt;br /&gt;
* &amp;lt;code&amp;gt;Near Clip&amp;lt;/code&amp;gt;: Sometimes, there will be a large obstruction in the center of the volume you wish to use as a reflection probe. For example, suppose that there is a large architectural supporting structure in the middle of the room, such as a column. If the probe gets placed internally inside this, it will reflect the internal surface of the column instead of the room in which it is placed.  Increasing Near Clip will make the sampling volume exclude objects n meters from the center so they don’t get included in the reflection. &#039;&#039;See [[PBR_Materials#Fine-Tuning_Reflection_Sample_Volumes|&amp;quot;Fine-Tuning Reflection Sample Volumes&amp;quot;]] for more info.&lt;br /&gt;
&lt;br /&gt;
Select an appropriate probe shape for your scene. For example, if you want to create a reflection probe that covers a room, select a &amp;lt;code&amp;gt;Box&amp;lt;/code&amp;gt; shape, or for other objects a &amp;lt;code&amp;gt;Sphere&amp;lt;/code&amp;gt; shape is recommended.&lt;br /&gt;
&lt;br /&gt;
Move the reflection probe and resize it as necessary to fit your desired purpose. For the room example, resize the box so the probe just touches the walls, ceiling and floor of the room.&lt;br /&gt;
&lt;br /&gt;
Naming your probes is also recommended, so other people ( or yourself later on ) will remember why there’s a large transparent prim in the middle of your build.&lt;br /&gt;
&lt;br /&gt;
If you wish to come back and edit the reflection probe later on, enable both &amp;lt;code&amp;gt;Show Reflection Probe Volumes&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Select Reflection Probes&amp;lt;/code&amp;gt; under &amp;lt;code&amp;gt;Build &amp;gt; Options&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Fine-Tuning Reflection Sample Volumes ===&lt;br /&gt;
{{KBwarning|Sample volumes do not react to prim parameter changes.  Options such as “Hollow” or “Taper” or “Shear” do not change how the sampling area behaves.}}&lt;br /&gt;
&lt;br /&gt;
That said, it may be helpful to visualize &amp;lt;code&amp;gt;Near Clip&amp;lt;/code&amp;gt; as the hollowing out of your reflection sample volume, but the hollow and near-clip are not in any way linked and doing so is purely a convenience.  Reflection probe sample volumes only respond to changes in scale and position; however, different probe volumes behave differently when resized.&lt;br /&gt;
* &amp;lt;code&amp;gt;Box&amp;lt;/code&amp;gt; Probes: These are affected by position and non-uniform scale.&lt;br /&gt;
Example: If I create a box reflection probe and scale it to 10m,30m,10m size, the full volume will be affected by the probe.&lt;br /&gt;
* &amp;lt;code&amp;gt;Sphere&amp;lt;/code&amp;gt; Probes: These are affected by position and &#039;&#039;&#039;uniform&#039;&#039;&#039; scale only, otherwise the smallest dimension is used.&lt;br /&gt;
Example: If I create a spherical probe and scale it to 10m,30m, and 10m size , the probe will sample a 10 meter diameter spherical volume at the center of the probe.  However, a 30m,30m,30m sphere will create a sampling volume of a 30 meter diameter sphere.&lt;br /&gt;
[[File:SphereProbeErrror.jpg | thumb | none | A diagram of how spherical probes function (or, don&#039;t function as you may expect) while non-uniformly-scaled.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; controls how ambient light (Found in your [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]]) affects, or does not affect, the contents of the reflection probe, and the intensity of indirect lighting (aka Irradiance).&lt;br /&gt;
This value is influenced by the &amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; value found in the user&#039;s [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]], wherein if the Ambiance value given in the [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]] is higher than the value defined by the probe itself, it inherits the ambiance value of the [[Environmental_Enhancement_Project#EEP_Settings_Objects|environment preset]], or alternately if the ambiance value defined by the probe is higher, the probe&#039;s value is used.&lt;br /&gt;
&lt;br /&gt;
There are a few operation modes that are set with the &amp;lt;code&amp;gt;Reflection Probe Ambiance&amp;lt;/code&amp;gt; value, in tandem with above:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Probe Ambiance behavior&lt;br /&gt;
! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0.00 || Ambient color from the environment applies at full intensity. &#039;&#039;&#039;Default for pre-PBR skies.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0.01 .. 0.99 || Blends ambiance with indirect lighting (sun + emissive or illuminated surfaces) within the probe.&lt;br /&gt;
|-&lt;br /&gt;
| 1.00 || Indirect light applies at full intensity, without ambient color. &#039;&#039;&#039;Default for PBR skies.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 1.01 .. 4.00 || Multiplier for indirect light, makes everything brighter within the probe.&lt;br /&gt;
|-&lt;br /&gt;
| 4.01 .. 100.00 || Multiplier for indirect light, but only further affects light from the sun. (Local lights are capped to 4.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Complex Reflection Probe placement ===&lt;br /&gt;
&lt;br /&gt;
At times, you may wish to place a reflection probe in an area where the reflection probe types (Sphere or Box) do not conform to the shape of the area. For example, a Box probe in a loft room has a triangular shape, which results in &amp;quot;probe bleed&amp;quot; wherein the influence volume affects an area larger than what is needed, and thus bleeds out onto the exterior roof, which is undesirable.&lt;br /&gt;
&lt;br /&gt;
In these cases, you may need to place multiple probes in order to blend together the sample volumes to achieve the desired result. In the loft room example, it&#039;s best to start with 3 box probes, which cover the floor, and each side of the roof. Then, use an additional box probe as &amp;quot;fill&amp;quot; to cover the gap inbetween the 3 probes. The main fill probe may need to be rotated at an angle to get the most coverage possible. If it is not possible to fill the gap between the 3 probes with this fill probe, you may wish to add more box probes to act as &amp;quot;secondary fill&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[TODO: Put photo examples of the above here!]&lt;br /&gt;
&lt;br /&gt;
Alternately, Sphere probes may be used to achieve the same result. Sphere probes have considerably softer blending than box probes, so this may give you the best results in severe bleed conditions.&lt;br /&gt;
&lt;br /&gt;
=== Unsupported use-cases ===&lt;br /&gt;
{{KBwarning|Do not attempt to do anything listed below. These uses are liable to either intentionally be disabled, or stop working in future updates.}}&lt;br /&gt;
You may be tempted to use reflection probes in the following ways, however these use-cases are either intentionally disabled or result in undefined behavior, and may work now but may not in future. &#039;&#039;&#039;You have been warned.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Under any circumstances, you should NOT:&#039;&#039;&#039;&lt;br /&gt;
* Wear a reflection probe. This intentionally does not work. (Reflection probes are a property of the scene, not an individual object / avatar)&lt;br /&gt;
* Attach a reflection probe to a physics-enabled object, e.g. vehicles. This intentionally does not work. (As above, the reflection probes are part of the scene; not part of an individual object. Reflection probes by design do not update in real-time, thus the object would always have incorrect reflections anyway.)&lt;br /&gt;
&lt;br /&gt;
== PBR Material Composition ==&lt;br /&gt;
PBR-defined values are measured by optical sensors and other capture tools and recorded in databases.  These parameters are then put into commercial software to be used by content creators.  Using software designed to create PBR content is always recommended; however, understanding how PBR materials are assembled can assist with editing and compiling them.  Having a direct understanding of what each individual color channel contributes is essential for editing PBR textures without relying upon some of the more advanced toolkits available.&lt;br /&gt;
&lt;br /&gt;
All PBR Values are listed from 0 to 1.0, though in an actual image, the values range from 0 - 255.&lt;br /&gt;
PBR Materials are composed of a set of four specifically designed textures; they are as follows:&lt;br /&gt;
&lt;br /&gt;
===Base Color [ RGB ] + Transparency [ A ]===&lt;br /&gt;
[RGB]: This is the unlit color of the surface.  This differs from the “Diffuse” texture that Second Life uses.  Diffuse textures often include faked reflection and specular information as well as added Ambient Occlusion shadows.  Base Color textures do not get any of this added information.  For metals ( as defined by the metalness value ), Base Color also determines specular reflection color, whereas, in non-PBR systems, this is defined by the Specular texture and tint.  In certain PBR texturing applications, Base Color is sometimes also referred to as “Albedo”.&lt;br /&gt;
&amp;lt;br /&amp;gt;[ A ]: Alpha Channel, dictates the transparency of the entire material overall.&lt;br /&gt;
&lt;br /&gt;
The Base color texture should be devoid of lighting information.&lt;br /&gt;
&lt;br /&gt;
===Occlusion [R] / Roughness [G] / Metalness [B]===&lt;br /&gt;
This texture is composed of 3 unrelated grayscale images stored in 3 different color channels of an RGB texture.&lt;br /&gt;
&amp;lt;br /&amp;gt;[R]: (Ambient) Occlusion is lighting data, &#039;&#039;&#039;removing&#039;&#039;&#039; the need to bake down shadows on to the Base Color map. Note that &#039;&#039;white&#039;&#039; (&amp;lt;1,1,1&amp;gt;) means no occlusion is applied, and &#039;&#039;black&#039;&#039; (&amp;lt;0,0,0&amp;gt;) applies full occlusion.&lt;br /&gt;
&amp;lt;br /&amp;gt;[G]: Roughness data ranges from 0 to 1.0, but the actual range of physical surfaces ranges from approximately 0.05 to 0.985.  No surface is perfectly smooth or completely rough.  The rougher a surface is the less mirror-like it behaves.  &lt;br /&gt;
&amp;lt;br /&amp;gt;[B]: Metalness values are mostly black or white.  Either the material is a conductive metal like copper, or it’s a non-metal like fabric.  0.0 is Non-Metallic, 1.0 is Metallic.  There are almost no materials with gray metalness values.&lt;br /&gt;
&amp;lt;br /&amp;gt;The alpha channel is ignored, as per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification].&lt;br /&gt;
&lt;br /&gt;
{{Enote|The Occlusion map &#039;&#039;&#039;is not&#039;&#039;&#039; controlled by the &amp;quot;Screen Space Ambient Occlusion&amp;quot; toggle in the Advanced Graphics Settings. &#039;&#039;&#039;The Occlusion map is &#039;&#039;always&#039;&#039; enabled.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
===Emissive [ RGB ]===&lt;br /&gt;
This determines the amount and the color of unlit (ignores ambient light conditions) areas of your material. When giving an object a white emissive map, the object will act as if the Blinn-Phong &amp;quot;Fullbright&amp;quot; option was checked. This map is useful for items which are expected to emit light, e.g. a table lamp, where the lamp shade would appear to glow when the lamp is turned on. If you wish to toggle the lamp on and off, it&#039;s recommended to change the Emissive Tint value to black (functionally disabling the emissive map, thus turning the lamp off), and then changing the tint back to your desired color to turn the lamp back on. Leaving the Emissive slot empty is recommended when it’s unused.&lt;br /&gt;
&lt;br /&gt;
Note that glow (The postprocessing effect), is controlled by a separate parameter in the build floater and is intentionally not part of the PBR material window. Glow is modulated by the emissive map, so black areas of an object&#039;s emissive map will not glow, similar to how Blinn-Phong emissive maps and glow work.&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the alpha channel is ignored.&lt;br /&gt;
&lt;br /&gt;
===Normal [ RGB ]===&lt;br /&gt;
The normal maps generated by your baking application / normal map generation toolkit should be compatible with Second Life in most cases.  The most common pitfall is using normal maps generated with “inverted” Green channels, such as those that are used for Direct3D and Unreal Engine.  Normal texture data redirects light in a different direction based on the vectors indicated by the color, so if the green channel is backward, it’ll seem to be “pointing the wrong way”.  To phrase the problem a different way, all the things that should be bumps look like dents, and vice versa.  If this occurs, double check that your settings aren’t for Direct3D and try re-generating it.  Also, taking it into image editing software and inverting the green channel only sometimes is a sufficient fix.&lt;br /&gt;
&lt;br /&gt;
Normal map tangent spaces are an extremely technical subject that most users need not be concerned with as most modern applications default to the correct setting.  However, if your normal maps look drastically different inside Second Life, compared to your source application, and you’ve already confirmed it’s not an inverted green channel, then checking which tangent space settings are being used is the next step.  PBR Normals use Mikkelsen Tangent Space. (Often abbreviated MikkT)  If you are unsure what this means, use similar workflows and settings for Second Life PBR as those that are generally recommended for the Godot 4 game engine.&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the blue channel of a normal map is only allowed to contain values above 0.5 to a max of 1 (255).&lt;br /&gt;
&lt;br /&gt;
As per the [https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures glTF 2.0 specification], the alpha channel is ignored.&lt;br /&gt;
&lt;br /&gt;
== Material Reference Libraries ==&lt;br /&gt;
As PBR is &#039;&#039;Physically Based&#039;&#039;, you may wish to know how to recreate a real-life material in PBR form. What color should you use? What metalness value should it have?&lt;br /&gt;
&lt;br /&gt;
Fortunately, reference libraries exist which can tell you how to recreate a given material in a PBR workflow.&lt;br /&gt;
&lt;br /&gt;
=== List of Reference Color Palettes &amp;amp; libraries ===&lt;br /&gt;
*&#039;&#039;&#039;[https://physicallybased.info/ Physically Based]&#039;&#039;&#039;&lt;br /&gt;
** Select the &#039;&#039;Godot&#039;&#039; Engine, with the Color Space as &#039;&#039;sRGB Linear&#039;&#039; and Color Representation as &#039;&#039;0-1&#039;&#039; (Depending on what you are doing, you may need to convert the value into gamma-corrected sRGB space, which can be done [https://davengrace.com/dave/cspace/ here.] Enter the value on the 3rd row.)&lt;br /&gt;
*&#039;&#039;&#039;[https://www.youtube.com/watch?v=TcTh-1X2FsQ Grzegorz Baran Library]&#039;&#039;&#039;&lt;br /&gt;
**Available on [https://www.youtube.com/watch?v=TcTh-1X2FsQ YouTube (video format, older)], or [https://gbar.gumroad.com/l/cbwkvo PDF (most up-to-date)].&lt;br /&gt;
*&#039;&#039;&#039;[https://seblagarde.wordpress.com/2014/04/14/dontnod-physically-based-rendering-chart-for-unreal-engine-4/ Dontnod Entertainment Library]&#039;&#039;&#039;&lt;br /&gt;
** A very small library, but useful nonetheless.&lt;br /&gt;
*&#039;&#039;&#039;[https://docs.studio-397.com/developers-guide/general-reference/pbr-an-introduction-authoring-guide Studio 397]&#039;&#039;&#039;&lt;br /&gt;
** A small collection of various materials.&lt;br /&gt;
*&#039;&#039;&#039;[http://wiki.polycount.com/wiki/PBR Polycount Wiki]&#039;&#039;&#039;&lt;br /&gt;
** Not so much a library in itself, but links out to other libraries. &#039;&#039;&#039;See the Color Charts section&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== List of PBR asset libraries ===&lt;br /&gt;
*&#039;&#039;&#039;[https://ambientcg.com/ ambientCG]&#039;&#039;&#039;&lt;br /&gt;
** Provides CC0 licensed HDRi, Metal-Rough materials and .sbar (Substance Painter material) files.&lt;br /&gt;
*&#039;&#039;&#039;[https://polyhaven.com/ Poly Haven]&#039;&#039;&#039;&lt;br /&gt;
** Provides CC0 licensed HDRi, Metal-Rough materials and models.&lt;br /&gt;
&lt;br /&gt;
== Uploading Materials ==&lt;br /&gt;
There are two different methods of creating PBR material assets:&lt;br /&gt;
&lt;br /&gt;
=== Method 1 : Direct Upload a glTF File ===&lt;br /&gt;
The most convenient method to create an entirely new material is to directly upload a glTF file from your computer.&lt;br /&gt;
&lt;br /&gt;
This is accessible via &amp;lt;code&amp;gt;Build &amp;gt; Upload &amp;gt; Material...&amp;lt;/code&amp;gt; &lt;br /&gt;
[[File:PBRBuildMenu.jpg | thumb | none | PBR Upload in main build menu. ]]&lt;br /&gt;
&lt;br /&gt;
Select a .GLB or a .GLTF file from your computer and Open it:&lt;br /&gt;
This will give the following window:&lt;br /&gt;
[[File:PBRMatMenu.jpg | thumb | none| The layout of the PBR material creation and editing interface.]]&lt;br /&gt;
&lt;br /&gt;
All of the editable material properties that most creators are familiar with are integrated into the Material&#039;s vertically sizable window on upload.  &lt;br /&gt;
&lt;br /&gt;
Clicking “Save” pays the upload fees and creates the Material assets using the local filename. Using &amp;quot;Save As...&amp;quot; provides a window to name all the assets to unique inworld names. There is a 63 char limit for names of inventory assets but the naming floater on this Materials upload &amp;lt;b&amp;gt;has no length restrictions&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Textures will be uploaded to the inventory&#039;s default Textures folder (NoMod, NoCopy, Transfer) with an appended &amp;quot;type&amp;quot; added to the given name. (ie. asset name (Base Color), asset name (Normal), asset name (Metallic Roughness). In naming the asset keep in mind that the bracketed appendix is included in the name&#039;s 63 character limit. The PBR object is uploaded (Mod, NoCopy, Transfer) to the inventory&#039;s Material folder with the appended text (Material). Presently, naming the glTF asset from Save As... will NOT append (Material) to the inventory&#039;s name. Saving from the locally named file will.&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Each individual texture used to compose the Material object is charged a separate upload fee. (ie. If you have 4 textures (Base Color, Metallic Roughness, Emissive, Normal) you are charged 4 times. If you only have a Normal and Metallic-Roughness texture you will be charged for 2, etc.)}}&lt;br /&gt;
&lt;br /&gt;
=== Method 2 : Edit a Blank. Create PBR Materials without a glTF File ===&lt;br /&gt;
If you have the necessary textures to compose a PBR material but do not have a glTF file you will need to build a Material from inventory.  Make sure your textures are in the correct format, with the correct data in the proper channels.  This requires some understanding of how [[PBR_Materials#PBR_Material_Composition:|PBR materials work]].  Once you have that, upload your textures as you normally would.&lt;br /&gt;
&lt;br /&gt;
[[File:PBRMenuUI.jpg | thumb | none | The PBR Materials UI in the Build Floater.]]&lt;br /&gt;
&lt;br /&gt;
* Find the &amp;quot;Materials&amp;quot; folder in your inventory, right click, and select &amp;quot;New Material&amp;quot; from the context menu.&lt;br /&gt;
* Name your new material something appropriate (E.g. &amp;quot;Red Bricks&amp;quot; for a red brick wall, etc.)&lt;br /&gt;
* Right click on your new material, and select &amp;quot;Open&amp;quot; from the context menu.&lt;br /&gt;
* Upload your PBR textures using the standard texture workflow (Usually, under &amp;lt;code&amp;gt;Build &amp;gt; Upload &amp;gt; Image&amp;lt;/code&amp;gt;).&lt;br /&gt;
* In the material window, select the appropriate maps that you just uploaded in their respective slots.&lt;br /&gt;
{{KBwarning|While there may be a temptation to try and place a Blinn-Phong “Diffuse” texture into the “Base Color” slot and a Blinn-Phong “Specular&amp;quot; into the “Metallic/Roughness” slot, doing so will not produce desirable results.  However, trying this won’t generate any warnings as there is no way to check that you’ve put the right texture type into the inputs, and the material will save and be applicable to objects.  &#039;&#039;&#039;Unfortunately, what will be created is not a functional PBR material.  Please do not do this.&#039;&#039;&#039;}}&lt;br /&gt;
* Once done, verify the material parameters are correct (E.g. Base color tint, M/R factor, Emissive tint, etc.)&lt;br /&gt;
* Save your new material.&lt;br /&gt;
* EITHER: [[PBR Materials#Drag_and_Drop|Drag and drop the material onto a rezzed prim]], OR edit the prim, click the &amp;quot;Blinn-Phong&amp;quot; drop-down, select &amp;quot;PBR Metallic-Roughness&amp;quot;, and click &amp;quot;Choose from Inventory&amp;quot;, and select your new material.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- * Create a test cube and select a face, switch it to be a PBR material.&lt;br /&gt;
* Select “Choose from Inventory”&lt;br /&gt;
* Instead of choosing something from your inventory, choose “Blank” and click “Ok”&lt;br /&gt;
* Choose “Edit Selected”,  this will open up the material creation UI.&lt;br /&gt;
* Choose your appropriate maps from your texture inventory and slot them into their matching locations.&lt;br /&gt;
{{KBwarning|While there may be a temptation to try and place a pre-PBR “Diffuse” texture into the “Base Color” slot and a pre-PBR “Specular&amp;quot; into the “Metallic/Roughness” slot, doing so will not produce desirable results.  However, trying this won’t generate any warnings as there is no way to check that you’ve put the right texture type into the inputs, and the material will save and be applicable to objects.  &#039;&#039;&#039;Unfortunately, what will be created is not a functional PBR material.  Please do not do this.&#039;&#039;&#039;}}&lt;br /&gt;
* Click “Save to Inventory”, this will bring up a prompt to name your new PBR material. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Creating Color Variants ===&lt;br /&gt;
After you’ve uploaded your first Material for your project, if you’re a store owner who would like to release more than one color palette for your creation, as many clothing and furniture designers find it useful to do, &#039;&#039;&#039;it’s important to not upload a new glTF file for every single variant of your Material&#039;&#039;&#039;.  Most variant materials, only the base color will change.  The Occlusion, Roughness, Metalness, Emissive and Normal map texture slots will remain the same.  Since these maps have already been uploaded once, if we upload a second glTF File, they will be duplicated.  Having different copies of the same texture, with differing UUID’s means that they will clog up download bandwidth and video card memory (not to mention your inventory as well).  This is very bad for Second Life.  So, for this reason, it’s recommended that when you create texture variants, you upload the additional copies of the base-color texture separately.  Then open your newly uploaded material in edit mode, choose the Base Color texture, and change it out for one of your newly uploaded Base Color textures, and click “Save As”.  This will create a second copy of your material that uses all the correct texture maps without needlessly duplicating them and causing additional lag.&lt;br /&gt;
&lt;br /&gt;
{{KBtip| You can also avoid uploading additional textures altogether by uploading a white color variant, then making use of the Base Color Tint parameter!}}&lt;br /&gt;
&lt;br /&gt;
== Double Sided Parameter : Uses and Dangers ==&lt;br /&gt;
“Double Sided” is a new property unique to Materials.  When “Double Sided” is checked, the surface upon which this material is placed will be drawn twice; once for the outward-facing portion of the surface, and a second time for the inward-facing side of the surface that is normally invisible without a double-sided material.  This option should only be used for very specific meshes that were designed to be used with double-sided materials, since placing materials with this parameter checked on normal objects will simply cause the viewer to draw it twice ( and thereby create additional viewer lag ) for no observable change.  It is &#039;&#039;&#039;very strongly recommended&#039;&#039;&#039; that this option be unchecked for any material that is to be distributed for general use.  Even more so if the “Modify” permission is revoked.  If you wish to distribute a version of a material that has “Double Sided” checked, please include a second copy of the material that has “Double Sided” unchecked as well, with an accompanying explanation to the next user as to why this was done.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
*LeafTextures_DoubleSided&lt;br /&gt;
*LeafTextures_SingleSided&lt;br /&gt;
&lt;br /&gt;
When designing mesh content for use with the double-sided material parameter, it is also &#039;&#039;very strongly recommended that you separate the triangles you intend to use the double-sided material upon into a separate mesh “face”&#039;&#039;, so as to not unintentionally render the portions of the mesh that already have triangles designed to represent the internal portion of the object a second time.&lt;br /&gt;
&lt;br /&gt;
== LSL Scripting ==&lt;br /&gt;
&lt;br /&gt;
Documentation for the LSL interface with glTF materials can be found on the following pages:&lt;br /&gt;
*[[GLTF Overrides]]&lt;br /&gt;
*[https://wiki.secondlife.com/wiki/Category:LSL_Material Category:LSL Material]&lt;br /&gt;
&lt;br /&gt;
== Recommended Application Settings ==&lt;br /&gt;
The glTF file format is widely adopted and many applications have export functionality for this type of file.&lt;br /&gt;
Below is a non-comprehensive list of some of the more popular applications that export glTF files.&lt;br /&gt;
&lt;br /&gt;
{{KBtip|&#039;&#039;&#039;The recommended HDRi for use in Second Life content creation is available at [https://github.com/Jenna-Huntsman/Second-Life-Resources/tree/main/PBR/HDRi this Github repository].&#039;&#039;&#039; This HDRi will closely match the lighting of objects under the PBR &amp;quot;Midday&amp;quot; environment preset.}}&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|While every effort has been made to ensure the recommended HDRi matches SL as close as possible, you may need to adjust the &amp;quot;Environment Exposure&amp;quot; parameter in your editing program to get 1:1 results with SL.}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===[https://www.blender.org/ Blender]===&lt;br /&gt;
&lt;br /&gt;
{{KBwarning|Older Blender versions have bugs in their glTF export tools which make their output incompatible with Second Life. &#039;&#039;&#039;Use of Blender versions of 3.3 and above are highly recommended to avoid issues.&#039;&#039;&#039;}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{{KBcaution|Please ensure that you correctly set up an HDRi in your viewport, as otherwise the rendering in Blender and in SL will not match. Please see [[PBR Materials#Recommended_Application_Settings|here for a reference HDRi]]. &#039;&#039;&#039;Please see [https://www.polygonartists.com/hdri-environment-maps-in-blender/ this tutorial].&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
Upon starting your project:&lt;br /&gt;
&lt;br /&gt;
Under: &amp;lt;code&amp;gt;Scene &amp;gt; Render Properties &amp;gt; Color Management&amp;lt;/code&amp;gt;:&lt;br /&gt;
*{{code|View Transform: Standard}}&lt;br /&gt;
*{{code|Sequencer: Linear ACEScg}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Create your materials within Blender using Principled BSDF Shader Nodes and the glTF Settings node.  Export them according to the official &#039;&#039;&#039;[https://docs.blender.org/manual/en/3.6/addons/import_export/scene_gltf2.html Blender Documentation.]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{KBcaution|Second Life does not support BSDF Clearcoat, Subsurface, Anisotropy or Transmission parameters at this time.}}&lt;br /&gt;
&lt;br /&gt;
For materials creation, 2 example .blend files are provided, one using separate Occlusion, Roughness and Metallic textures (&amp;quot;Long Form&amp;quot;), and one that handles a pre-packed ORM map (&amp;quot;Short Form&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
The below files are intended for use with Blender versions 3.3 and above.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[https://github.com/Jenna-Huntsman/Second-Life-Resources/blob/main/glTF%20Node%20Tree%20-%20Long%20Form.blend glTF Node Tree - Long Form.blend]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[https://github.com/Jenna-Huntsman/Second-Life-Resources/blob/main/glTF%20Node%20Tree%20-%20Short%20Form.blend glTF Node Tree - Short Form.blend]&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.adobe.com/products/substance3d-painter.html Adobe Substance 3D Painter] === &lt;br /&gt;
{{KBcaution|While Substance Painter is generally considered to be stable, in some (rare) situations Substance may output a malformed glTF which will be rejected by the viewer. If this happens, import your materials into Blender, then export a glTF from Blender - this should be fixed in a future version of Substance.}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
{{KBcaution|Please ensure that you correctly set up an HDRi in your viewport, as otherwise the rendering in Substance and in SL will not match. Please see [[PBR Materials#Recommended_Application_Settings|here for a reference HDRi]]. Please see [http://y2u.be/6goXJ2CJzaM this tutorial].}}&lt;br /&gt;
When starting your project:&lt;br /&gt;
&lt;br /&gt;
*{{code|Template: PBR - Metallic Roughness Alpha-test (starter_assets)}}&lt;br /&gt;
&lt;br /&gt;
OR&lt;br /&gt;
&lt;br /&gt;
*{{code|Template: PBR - Metallic Roughness Alpha-blend (starter_assets)}}&lt;br /&gt;
&lt;br /&gt;
Verify the following settings:&lt;br /&gt;
*{{code|Document Resolution: [Set as desired]}}&lt;br /&gt;
*{{code|Normal Map Format: OpenGL (Y+)}}&lt;br /&gt;
*{{code|Compute Tangents Per Fragment: YES}}&lt;br /&gt;
&lt;br /&gt;
Under &amp;quot;Color Management&amp;quot;:&lt;br /&gt;
{{KBtip| For a more in-depth explanation of the below settings, and issues that you may encounter, visit [https://mrlixm.github.io/blog/substance-painter-color-management/#aces-workflow this page].}}&lt;br /&gt;
*{{code|Color Management: OpenColorIO}}&lt;br /&gt;
*{{code|OpenColorIO Configuration: ACES 1.0.3}}&lt;br /&gt;
*{{code|Standard sRGB color space: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|8 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|16 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|Material color space default: Utility - sRGB - Texture}}&lt;br /&gt;
&lt;br /&gt;
When exporting:&lt;br /&gt;
&lt;br /&gt;
Settings Tab - Global Settings&lt;br /&gt;
*{{code|Output template: glTF PBR Metal Roughness}}&lt;br /&gt;
*{{code|Size: Based on each Texture Set&#039;s size}}&lt;br /&gt;
*{{code|Padding: Dilation + default background color - 8 bit}} &#039;&#039;&#039;OR&#039;&#039;&#039; {{code|Padding: Dilation infinite}}&lt;br /&gt;
{{KBtip|If you encounter color banding (AKA posterization) in your base color texture after export, you may wish to enable dithering.}}&lt;br /&gt;
*To enable dithering, open the {{code|Export Textures}} window, under the {{code|Settings}} tab, enable an override for the Base Color texture and swap the output format from {{code|8 bits}} to {{code|8 bits + dithering}}&lt;br /&gt;
&lt;br /&gt;
Settings Tab - Per-Material settings&lt;br /&gt;
*{{code|Output maps &amp;gt; Normal Map: 8 bits}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt;[[File:Substance_Painter_-_Export_Global_settings.png|thumb|Settings Tab - Global Settings]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt;[[File:Substance_Painter_-_Export_Per-Material_settings.png|thumb|Settings Tab - Per-Material settings]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.adobe.com/products/substance3d-designer.html Adobe Substance 3D Designer] === &lt;br /&gt;
{{KBcaution|Substance Designer &#039;&#039;does not support glTF output&#039;&#039;, unlike Substance Painter.}}&lt;br /&gt;
&lt;br /&gt;
Under preferences, verify the following &#039;&#039;&#039;Project&#039;&#039;&#039; settings:&lt;br /&gt;
*{{code|Color Management: OpenColorIO}}&lt;br /&gt;
*{{code|OpenColorIO Configuration: ACES 1.0.3}}&lt;br /&gt;
*{{code|8 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|16 bit images: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|Floating point images: ACES - ACEScg}}&lt;br /&gt;
*{{code|2D and 3D View Display Default: sRGB}}&lt;br /&gt;
&lt;br /&gt;
When exporting (Export Outputs):&lt;br /&gt;
*{{code|basecolor: Utility - sRGB - Texture}}&lt;br /&gt;
*{{code|normal: Utility - Raw}}&lt;br /&gt;
*{{code|roughness: Utility - Raw}}&lt;br /&gt;
*{{code|metallic: Utility - Raw}}&lt;br /&gt;
*{{code|ambientocclusion: Utility - Raw}}&lt;br /&gt;
&lt;br /&gt;
If you use a custom node to output a [[PBR_Materials#Occlusion_.5BR.5D_.2F_Roughness_.5BG.5D_.2F_Metalness_.5BB.5D|pre-compiled ORM map]], this should also be set to Raw.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.maxon.net/en/cinema-4d Cinema 4D] === &lt;br /&gt;
Please follow the documentation provided in this [https://www.bakedpixels.nl/blog/export-to-gltf-with-cinema-4d blog post].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===[https://3dcoat.com/ 3DCoat]===&lt;br /&gt;
{{KBwarning|As of version &amp;quot;2022-58&amp;quot;, 3DCoat is unable to produce a spec-compliant glTF file, and thus should not be used for Second Life. &#039;&#039;&#039;Use at your own risk - you &#039;&#039;will&#039;&#039; have issues!}}&lt;br /&gt;
&lt;br /&gt;
glTF export has been implemented since 3DCoat version 2020 and is found in the &amp;lt;code&amp;gt;File &amp;gt;Export &amp;gt; Export to glTF &amp;gt; glTF Separate (gltf + .bin + textures)&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://quixel.com/mixer Quixel Mixer] === &lt;br /&gt;
Quixel Mixer has no glTF output, however, it does generate the functional textures, though they do need to be edited and combined in photo editing software.&lt;br /&gt;
Export them by going to &amp;lt;code&amp;gt;Export Target &amp;gt; Custom&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Texture Preset: Metalness Maps&lt;br /&gt;
&lt;br /&gt;
Select: &lt;br /&gt;
&lt;br /&gt;
Albedo , Roughness, Normal, AO , Metalness  and ( if need be ) Emissive.&lt;br /&gt;
&lt;br /&gt;
Click “Export to Disk” and open the folder that the files were placed into.&lt;br /&gt;
&lt;br /&gt;
Albedo is the Base Color texture in this case.&lt;br /&gt;
&lt;br /&gt;
AO , Roughness , Metalness get combined into the ORM map as per [[PBR_Materials#PBR_Material_Composition|this explanation.]]&lt;br /&gt;
&lt;br /&gt;
Normal Map : Quixel generates Direct3D Normal Maps. The green channel needs to be inverted as [[PBR_Materials#Normal_.5B_RGB_.5D_|per here.]]&lt;br /&gt;
&lt;br /&gt;
Upload to Second Life using [[PBR_Materials#Method_2_:_Edit_a_Blank._Create_PBR_Materials_without_a_GLTF_File|Method 2.]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.autodesk.eu/products/3ds-max/overview Autodesk 3DS Max 2023] === &lt;br /&gt;
Autodesk just added the ability to create glTF files using their new glTF Material and glTF Export functionalities as outlined in &#039;&#039;&#039;[https://help.autodesk.com/view/3DSMAX/2023/ENU/?guid=GUID-EFBB037D-C4EB-42D2-9CE1-30FCAD483C31 Autodesk&#039;s official documentation]&#039;&#039;&#039;.  All prior versions of Autodesk software do not have this functionality.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== [https://www.materialmaker.org/ Material Maker] ===&lt;br /&gt;
While [https://www.materialmaker.org/ Material Maker] does not currently export directly to glTF ([https://github.com/RodZill4/material-maker/issues/479 This may be added in future]), materials created with this program are compatible with Second Life.&lt;br /&gt;
&lt;br /&gt;
Export your materials using the &#039;&#039;&#039;Godot 4 ORM&#039;&#039;&#039; export preset.&lt;br /&gt;
&lt;br /&gt;
Upload to Second Life using [[PBR_Materials#Method_2_:_Edit_a_Blank._Create_PBR_Materials_without_a_GLTF_File|Method 2.]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== Adobe Photoshop ===&lt;br /&gt;
&#039;&#039;&#039;While Photoshop is not officially a PBR authoring tool&#039;&#039;&#039;, the changes to Alpha Blending (changing into Linear space from sRGB) may require you to change your PS settings to get consistent results. Please see [http://www.kiransprojects.com/blog/2014/photoshop-blending-is-broken/ this article] for more information. (See the section titled &amp;quot;A Partial Solution&amp;quot;). Alternatively, [https://www.reddit.com/r/photoshop/comments/61clf8/how_to_fix_photoshops_incorrect_and_ugly_color/ this Reddit post] also gives a few options on how to achieve blending in the correct manner.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
=== GIMP ===&lt;br /&gt;
&#039;&#039;&#039;While GIMP is not officially a PBR authoring tool&#039;&#039;&#039;, the changes to Alpha Blending (changing into Linear space from sRGB) in Second Life will make textures created in GIMP display their alphas correctly (GIMP defaults to linear alpha calculation).&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting content ==&lt;br /&gt;
&lt;br /&gt;
If you upload a piece of PBR content which does not match your editor, the advice from Linden Lab is to &#039;&#039;&#039;STOP: File a Feedback ticket. Do not attempt to &amp;quot;fix&amp;quot; the content.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
That said, there are some troubleshooting steps which you can do yourself:&lt;br /&gt;
* Check that you are using the PBR Linden Midday preset (called &amp;quot;Midday&amp;quot;) &#039;&#039;not &amp;quot;Midday (Legacy)&amp;quot;&#039;&#039;. Other environments may not match the reference HDRi, causing some visual differences. (This is intended behaviour, and content should be able to be viewed under any light correctly, but for troubleshooting purposes this may be required).&lt;br /&gt;
** At present, there is a bug with the PBR Linden Midday preset which results in an excessive blue sheen, due to the use of an over-saturated (unrealistic) sky color, among some other issues. This has been fixed in the upcoming glTF Maintenance viewer.&lt;br /&gt;
* Are you using a reflection probe? If not, does the problem reproduce if a manual reflection probe is placed over the object?&lt;br /&gt;
** This is because a common source of the &amp;quot;blue sheen&amp;quot; in interior scenes is the use of an auto-probe. Auto-probes sample their surroundings, and combined with the approximate (and often incorrect) placement this will mean the sky is sampled on all sides, thus meaning the reflected light from the surroundings (which counters the blue light from the sky) is absent, leading to a larger-than-expected level of sky contribution on the object. Auto-probe placement can be worse in skyboxes or sky platforms&lt;br /&gt;
* Triple-check the settings used for your editor match the ones given [[PBR_Materials#Recommended_Application_Settings|here]], including the reference HDRi. &#039;&#039;&#039;Any&#039;&#039;&#039; deviation from these settings may cause visual differences between your editor and in-world.&lt;br /&gt;
* Check your content against a glTF reference viewer; such as:&lt;br /&gt;
** [https://modelviewer.dev/editor/ Modelviewer.dev]&lt;br /&gt;
** [https://github.khronos.org/glTF-Sample-Viewer-Release/ Khronos Sample Viewer]&lt;br /&gt;
&lt;br /&gt;
If the above steps fail, then please &#039;&#039;&#039;[https://feedback.secondlife.com/ file some Feedback.]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A good Feedback ticket will include:&lt;br /&gt;
* An LM to a location where the problem can be examined in-world.&lt;br /&gt;
* A copy of the glTF content, attached to the ticket.&lt;br /&gt;
* Screenshots of the representation in-world, in a reference viewer, and in-editor.&lt;br /&gt;
** You should include screenshots of the object inside a manually placed reflection probe, and outside.&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlSetPayPrice&amp;diff=1218141</id>
		<title>LlSetPayPrice</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlSetPayPrice&amp;diff=1218141"/>
		<updated>2025-04-03T19:31:52Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Bug caveat&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2={{Issues/VWR-3048}}{{Issues/VWR-8744}}&lt;br /&gt;
|func_id=302|func_sleep=0.0|func_energy=10.0&lt;br /&gt;
|func=llSetPayPrice|sort=SetPayPrice&lt;br /&gt;
|p1_type=integer|p1_name=price|p1_desc=PAY_* constant or positive value (including zero)&lt;br /&gt;
|p2_type=list|p2_name=quick_pay_buttons|p2_desc=Four PAY_* constants and/or positive integer values (zero is not shown)&lt;br /&gt;
|func_desc=Suggest default amounts for the pay text field and pay buttons of the appearing dialog when someone chooses to pay this object.&lt;br /&gt;
|caveats=*This function should not be trusted to limit the values of money payable to the object; &#039;&#039;always&#039;&#039; verify the amount paid is the amount expected.&lt;br /&gt;
* Use only one call to this function in all the scripts on an object to prevent confusion about which values are used.   You still need to check in the money event that the amount is as expected.&lt;br /&gt;
* This function only works when called from the root prim of an object. Its effect applies to all the prims in the object. Calling it from a child prim has no effect.&lt;br /&gt;
** There is currently a [https://feedback.secondlife.com/slua-alpha/p/child-prim-in-linkset-cannot-be-paid-if-previously-a-root-prim-with-llsetpaypric viewer bug] where calls from child prims will &#039;&#039;&#039;prevent&#039;&#039;&#039; payment to the object.&lt;br /&gt;
* Payment to a prim can be blocked by the llSetPayPrice() setting in the prim, which persists even if the script with llSetPayPrice() is removed.&lt;br /&gt;
* &#039;&#039;&#039;Caution:&#039;&#039;&#039; Calling this function will enable payment on the prim (or the whole object if it is the root prim) for the current state, even when this state has no money event.&lt;br /&gt;
* Otherwise, the pay option will only be shown in prims having a running script with a [[money]] event (or in all the prims of the object if the root has a running script with a money event).&lt;br /&gt;
* The effect seems to persist even if the script is recompiled with out the [[llSetPayPrice]] function, even if the script is replaced with another one which includes a [[money]] event, but not [[llSetPayPrice]].&lt;br /&gt;
* Money cannot be paid to an attachment; &amp;quot;Pay&amp;quot; will go directly to the wearer instead.&lt;br /&gt;
* If &#039;&#039;&#039;quick_pay_buttons&#039;&#039;&#039; contains a negative value or zero, the button will not be shown at all.&lt;br /&gt;
** However, zero is allowed for &#039;&#039;&#039;price&#039;&#039;&#039;, which is used to set the custom text field&#039;s value within the Pay window.&lt;br /&gt;
&lt;br /&gt;
|examples=This will give the user a dialog box without the &#039;&#039;&#039;price&#039;&#039;&#039; field and only one button with a value of 150.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;llSetPayPrice(PAY_HIDE, [150,PAY_HIDE,PAY_HIDE,PAY_HIDE])&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;integer price = 10;&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        llSetPayPrice(PAY_HIDE, [PAY_HIDE ,PAY_HIDE, PAY_HIDE, PAY_HIDE]);&lt;br /&gt;
        llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);&lt;br /&gt;
    }&lt;br /&gt;
    run_time_permissions(integer perm)&lt;br /&gt;
    {&lt;br /&gt;
        if(perm &amp;amp; PERMISSION_DEBIT)&lt;br /&gt;
            state cash;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
state cash&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        llSetPayPrice(price, [price ,PAY_HIDE, PAY_HIDE, PAY_HIDE]);&lt;br /&gt;
    }&lt;br /&gt;
    money(key id, integer amount)&lt;br /&gt;
    {&lt;br /&gt;
        if(amount != price)&lt;br /&gt;
        {&lt;br /&gt;
            llGiveMoney(id, amount);&lt;br /&gt;
            llInstantMessage(id, &amp;quot;You paid &amp;quot;+(string)amount+&amp;quot;, which is the wrong price, the price is: &amp;quot;+(string)price);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            //insert your give code here.&lt;br /&gt;
            llInstantMessage(id, &amp;quot;You paid the right price&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
|spec&lt;br /&gt;
|constants=&lt;br /&gt;
{{{!}}&lt;br /&gt;
{{!}}&lt;br /&gt;
{{{!}}{{Prettytable|style=margin-top:0;}}&lt;br /&gt;
{{!}}-{{Hl2}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Constant&lt;br /&gt;
! {{HoverText|Alt|Alternate}}&lt;br /&gt;
! Description&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} [[PAY_HIDE]]&lt;br /&gt;
{{!}} -1&lt;br /&gt;
{{!}} &amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt;&lt;br /&gt;
{{!}} Hides this quick pay button.&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} [[PAY_DEFAULT]]&lt;br /&gt;
{{!}} -2&lt;br /&gt;
{{!}} &lt;br /&gt;
{{!}} Use the default value for this quick pay button.&lt;br /&gt;
{{!}}}&lt;br /&gt;
{{!}}&lt;br /&gt;
{{{!}} {{Prettytable|style=margin-top:0;}}&lt;br /&gt;
{{!}}-{{Hl2}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Button Order&lt;br /&gt;
{{!}}- &lt;br /&gt;
{{!}} &amp;lt;center&amp;gt;1&amp;lt;/center&amp;gt;&lt;br /&gt;
{{!}} &amp;lt;center&amp;gt;2&amp;lt;/center&amp;gt;&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}} &amp;lt;center&amp;gt;3&amp;lt;/center&amp;gt;&lt;br /&gt;
{{!}} &amp;lt;center&amp;gt;4&amp;lt;/center&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
{{!}}&lt;br /&gt;
{{{!}}{{Prettytable|style=margin-top:0;}}&lt;br /&gt;
{{!}}-{{Hl2}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Defaults&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}$1&lt;br /&gt;
{{!}}$5&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}$10&lt;br /&gt;
{{!}}$20&lt;br /&gt;
{{!}}}&lt;br /&gt;
{{!}}}&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions={{LSL DefineRow||[[llGiveMoney]]|}}&lt;br /&gt;
|also_tests={{LSL DefineRow||[[llSetPayPrice Test]]|}}&lt;br /&gt;
|also_events={{LSL DefineRow||[[money]]|}}&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes&lt;br /&gt;
|cat1=Money&lt;br /&gt;
|cat2=Prim&lt;br /&gt;
|cat3=Dialog&lt;br /&gt;
|cat4&lt;br /&gt;
|issues&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=LlRotateTexture&amp;diff=1218125</id>
		<title>LlRotateTexture</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=LlRotateTexture&amp;diff=1218125"/>
		<updated>2025-04-01T10:44:47Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: Rewording&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL_Function&lt;br /&gt;
|inject-2={{LSL_Function/face|face}}&lt;br /&gt;
{{LSL_Function/angle|angle}}&lt;br /&gt;
|func_id=56|func_sleep=0.2|func_energy=10.0&lt;br /&gt;
|func=llRotateTexture&lt;br /&gt;
|p1_type=float|p1_name=angle&lt;br /&gt;
|p2_type=integer|p2_name=face&lt;br /&gt;
|func_footnote&lt;br /&gt;
|func_desc=Sets the rotation of a texture on the chosen {{LSLP|face}} to {{LSLP|angle}}.&lt;br /&gt;
|return_text&lt;br /&gt;
|spec&lt;br /&gt;
|caveats&lt;br /&gt;
|constants&lt;br /&gt;
|examples=&amp;lt;source lang=&amp;quot;lsl2&amp;quot;&amp;gt;&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    touch_start(integer total_number) {&lt;br /&gt;
        // Makes the object&#039;s texture rotate a quarter of turn&lt;br /&gt;
        llRotateTexture(PI_BY_TWO, ALL_SIDES);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|helpers&lt;br /&gt;
|also_functions={{LSL DefineRow||[[llGetTextureRot]]|Gets the texture rotation}}&lt;br /&gt;
{{LSL DefineRow||[[llSetTextureAnim]]|Animates the texture}}&lt;br /&gt;
|also_tests&lt;br /&gt;
|also_events&lt;br /&gt;
|also_articles&lt;br /&gt;
|notes=&lt;br /&gt;
This function only sets the absolute orientation of the texture. See [[llSetTextureAnim]] for animations.&lt;br /&gt;
|cat1=Face&lt;br /&gt;
|cat2=Texture&lt;br /&gt;
|cat3&lt;br /&gt;
|cat4&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Luau_Examples&amp;diff=1218107</id>
		<title>Luau Examples</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Luau_Examples&amp;diff=1218107"/>
		<updated>2025-03-23T14:50:48Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: bugfix vehicle.lua (bit32.btest) and removed unused variables&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
&lt;br /&gt;
Example [[Luau Alpha|Luau]] scripts&lt;br /&gt;
&lt;br /&gt;
== vehicle.lua ==&lt;br /&gt;
This script demonstrates how one may create their own vehicle scripts, which don’t rely on the underlying LL vehicle system. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- These two will hold the link numbers for left and right front wheels&lt;br /&gt;
local LINK_WHEEL_LEFT = 0&lt;br /&gt;
local LINK_WHEEL_RIGHT = 0&lt;br /&gt;
&lt;br /&gt;
-- Integra Type R Engine properties&lt;br /&gt;
local Engine = {&lt;br /&gt;
    rpm = 800,              -- current engine RPM (starts at idle)&lt;br /&gt;
    gear = 0,               -- 0 = neutral; -1 = reverse; 1-5 for forward gears&lt;br /&gt;
    idleRPM = 800,          -- idle RPM&lt;br /&gt;
    maxRPM = 8300,          -- redline&lt;br /&gt;
    throttle = 0.0,         -- throttle value (0.0 to 1.0)&lt;br /&gt;
    gearRatios = {          -- Integra Type R gear ratios&lt;br /&gt;
        [-1] = 3.727,       -- reverse gear ratio&lt;br /&gt;
        [0]  = 0,          -- neutral&lt;br /&gt;
        [1]  = 3.727,&lt;br /&gt;
        [2]  = 2.256,&lt;br /&gt;
        [3]  = 1.729,&lt;br /&gt;
        [4]  = 1.416,&lt;br /&gt;
        [5]  = 1.194&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local FINAL_DRIVE = 4.1   -- Final drive ratio&lt;br /&gt;
local WHEEL_RADIUS = 0.3  -- in meters&lt;br /&gt;
&lt;br /&gt;
-- Vehicle properties (using realistic Integra Type R mass)&lt;br /&gt;
local Vehicle = {&lt;br /&gt;
    speed = 0,      -- speed in m/s; negative indicates reverse motion&lt;br /&gt;
    mass = 1200,    -- mass in kg&lt;br /&gt;
    angle = 0,      -- heading angle in radians&lt;br /&gt;
    steering = 0,   -- steering input: -1 (left), 0 (none), 1 (right)&lt;br /&gt;
    brake = false   -- flag indicating if brakes are applied&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Engine constants (using realistic Integra Type R values)&lt;br /&gt;
local MAX_TORQUE = 200   -- in Nm&lt;br /&gt;
&lt;br /&gt;
-- Brake force constant (for active braking)&lt;br /&gt;
local BRAKE_FORCE = 4000  -- in Newtons&lt;br /&gt;
&lt;br /&gt;
-- Define a torque curve function:&lt;br /&gt;
local function torqueCurve(rpm)&lt;br /&gt;
    local peakRPM = 6000&lt;br /&gt;
    if rpm &amp;lt; peakRPM then&lt;br /&gt;
        return 0.8 + 0.2 * ((rpm - Engine.idleRPM) / (peakRPM - Engine.idleRPM))&lt;br /&gt;
    else&lt;br /&gt;
        local factor = 1 - 0.5 * ((rpm - peakRPM) / (Engine.maxRPM - peakRPM))&lt;br /&gt;
        return math.max(0.5, factor)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Engine braking force when throttle is zero in gear.&lt;br /&gt;
local ENGINE_BRAKE_FORCE = 2000  -- in Newtons&lt;br /&gt;
&lt;br /&gt;
-- Friction and drag constants&lt;br /&gt;
local G = 9.81            -- gravity (m/s^2)&lt;br /&gt;
local C_rr_inGear = 0.015 -- rolling resistance coefficient in gear&lt;br /&gt;
local C_rr_neutral = 0.05 -- rolling resistance coefficient in neutral&lt;br /&gt;
&lt;br /&gt;
local airDensity = 1.225      -- kg/m^3&lt;br /&gt;
local dragCoefficient = 0.3   -- aerodynamic drag coefficient&lt;br /&gt;
local frontalArea = 2.2       -- in m^2&lt;br /&gt;
&lt;br /&gt;
local function getAerodynamicDrag(speed)&lt;br /&gt;
    return 0.5 * airDensity * dragCoefficient * frontalArea * speed * speed&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Compute engine RPM from vehicle speed when in gear.&lt;br /&gt;
local function computeEngineRPMFromSpeed()&lt;br /&gt;
    if Engine.gear == 0 then&lt;br /&gt;
        return Engine.rpm  -- In neutral, RPM is managed independently.&lt;br /&gt;
    else&lt;br /&gt;
        local conversionFactor = Engine.gearRatios[Engine.gear] * FINAL_DRIVE * (60 / (2 * math.pi * WHEEL_RADIUS))&lt;br /&gt;
        local rpm = math.abs(Vehicle.speed) * conversionFactor&lt;br /&gt;
        if rpm &amp;lt; Engine.idleRPM then rpm = Engine.idleRPM end&lt;br /&gt;
        return rpm&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- For neutral, update RPM toward a target based on throttle.&lt;br /&gt;
local function updateEngineRPMNeutral()&lt;br /&gt;
    local targetRPM = Engine.idleRPM + (Engine.maxRPM - Engine.idleRPM) * Engine.throttle&lt;br /&gt;
    local changeRate = 200  -- RPM per tick&lt;br /&gt;
    if Engine.rpm &amp;lt; targetRPM then&lt;br /&gt;
        Engine.rpm = math.min(Engine.rpm + changeRate, targetRPM)&lt;br /&gt;
    elseif Engine.rpm &amp;gt; targetRPM then&lt;br /&gt;
        Engine.rpm = math.max(Engine.rpm - changeRate, targetRPM)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Calculate the engine force delivered to the wheels.&lt;br /&gt;
local function getEngineForce()&lt;br /&gt;
    if Engine.gear == 0 then&lt;br /&gt;
        return 0  -- In neutral, no drive force is transmitted.&lt;br /&gt;
    else&lt;br /&gt;
        if Engine.throttle == 0 then&lt;br /&gt;
            -- Apply engine braking when throttle is released.&lt;br /&gt;
            return -ENGINE_BRAKE_FORCE&lt;br /&gt;
        else&lt;br /&gt;
            local torque = Engine.throttle * MAX_TORQUE * torqueCurve(Engine.rpm)&lt;br /&gt;
            local force = (torque * Engine.gearRatios[Engine.gear] * FINAL_DRIVE) / WHEEL_RADIUS&lt;br /&gt;
            return force&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Starts the engine by setting it to idle RPM.&lt;br /&gt;
local function startEngine()&lt;br /&gt;
    Engine.rpm = Engine.idleRPM&lt;br /&gt;
    ll.Say(0, &amp;quot;Engine started. RPM: &amp;quot; .. Engine.rpm)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Shifts the gear if valid.&lt;br /&gt;
local function shiftGear(newGear)&lt;br /&gt;
    if Engine.gearRatios[newGear] then&lt;br /&gt;
        Engine.gear = newGear&lt;br /&gt;
        ll.Say(0, &amp;quot;Shifted to gear &amp;quot; .. newGear)&lt;br /&gt;
    else&lt;br /&gt;
        ll.Say(0, &amp;quot;Invalid gear: &amp;quot; .. newGear)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function findWheels()&lt;br /&gt;
    local total = ll.GetNumberOfPrims()&lt;br /&gt;
    for i = 1, total do&lt;br /&gt;
        local name = ll.GetLinkName(i)&lt;br /&gt;
        if name == &amp;quot;WHEEL_LF&amp;quot; then&lt;br /&gt;
            LINK_WHEEL_LEFT = i&lt;br /&gt;
        elseif name == &amp;quot;WHEEL_RF&amp;quot; then&lt;br /&gt;
            LINK_WHEEL_RIGHT = i&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local castRayCounter = 0&lt;br /&gt;
local lastGrounded = false&lt;br /&gt;
&lt;br /&gt;
local function isVehicleOnGround()&lt;br /&gt;
    castRayCounter = castRayCounter + 1&lt;br /&gt;
    if castRayCounter &amp;gt;= 5 then&lt;br /&gt;
        castRayCounter = 0&lt;br /&gt;
        local pos = ll.GetPos()&lt;br /&gt;
        local rayDistance = 4.5  -- threshold distance (in meters) to consider as &amp;quot;grounded&amp;quot;&lt;br /&gt;
        local rayStart = pos&lt;br /&gt;
        local rayEnd = pos + vector(0, 0, -rayDistance)&lt;br /&gt;
        local hitResult = ll.CastRay(rayStart, rayEnd, {})&lt;br /&gt;
        if rawlen(hitResult) == 3 then&lt;br /&gt;
            lastGrounded = true&lt;br /&gt;
        else&lt;br /&gt;
            lastGrounded = false&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return lastGrounded&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Timer event: updates engine, vehicle physics, steering, and rotates the wheels based on steering.&lt;br /&gt;
function timer()&lt;br /&gt;
    local dt = 0.1  -- time step in seconds&lt;br /&gt;
&lt;br /&gt;
    -- For neutral, update RPM based on throttle.&lt;br /&gt;
    if Engine.gear == 0 then&lt;br /&gt;
        updateEngineRPMNeutral()&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Calculate forces.&lt;br /&gt;
    local C_rr = (Engine.gear == 0) and C_rr_neutral or C_rr_inGear&lt;br /&gt;
    local F_roll = Vehicle.mass * G * C_rr&lt;br /&gt;
    local engineForce = getEngineForce()&lt;br /&gt;
    local dragForce = getAerodynamicDrag(math.abs(Vehicle.speed))&lt;br /&gt;
    local netForce = engineForce - (F_roll + dragForce)&lt;br /&gt;
&lt;br /&gt;
    -- Apply braking force if brakes are engaged.&lt;br /&gt;
    if Vehicle.brake then&lt;br /&gt;
        netForce = netForce - BRAKE_FORCE&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local acceleration = netForce / Vehicle.mass&lt;br /&gt;
    local vel = ll.GetVel()&lt;br /&gt;
    local horizontal_vec = vector(vel.x, vel.y, 0)&lt;br /&gt;
    Vehicle.speed = (ll.VecMag(horizontal_vec) + acceleration * dt)&lt;br /&gt;
&lt;br /&gt;
    -- Limit vehicle speed based on gear.&lt;br /&gt;
    if Engine.gear ~= 0 then&lt;br /&gt;
        if Vehicle.speed &amp;lt; 0 then&lt;br /&gt;
            Vehicle.speed = 0&lt;br /&gt;
        end&lt;br /&gt;
        local conversionFactor = Engine.gearRatios[Engine.gear] * FINAL_DRIVE * (60 / (2 * math.pi * WHEEL_RADIUS))&lt;br /&gt;
        local maxSpeed = Engine.maxRPM / conversionFactor&lt;br /&gt;
        if Vehicle.speed &amp;gt; maxSpeed then&lt;br /&gt;
            Vehicle.speed = maxSpeed&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- When in gear, update RPM based on the new speed.&lt;br /&gt;
    if Engine.gear ~= 0 then&lt;br /&gt;
        Engine.rpm = computeEngineRPMFromSpeed()&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Steering update using angular velocity and quaternions:&lt;br /&gt;
    local STEERING_RATE = math.rad(6)  -- desired steering rate per tick (radians)&lt;br /&gt;
    local yawDelta = Vehicle.steering * STEERING_RATE  -- small rotation for this tick&lt;br /&gt;
&lt;br /&gt;
    if yawDelta ~= 0 then&lt;br /&gt;
        local angularSpeed = yawDelta / dt&lt;br /&gt;
        local angularVelocity = vector(0, 0, angularSpeed)&lt;br /&gt;
        ll.SetAngularVelocity(angularVelocity, 1)&lt;br /&gt;
        Vehicle.angle = Vehicle.angle + yawDelta&lt;br /&gt;
    else&lt;br /&gt;
        ll.SetAngularVelocity(vector(0, 0, 0), 1)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Display engine RPM and speed (converted to mph).&lt;br /&gt;
    local speed_mph = math.abs(Vehicle.speed) * 2.23694&lt;br /&gt;
    ll.SetText(&amp;quot;RPM: &amp;quot; .. math.floor(Engine.rpm) ..&lt;br /&gt;
               &amp;quot; | Speed: &amp;quot; .. string.format(&amp;quot;%.1f&amp;quot;, speed_mph) ..&lt;br /&gt;
               &amp;quot; mph&amp;quot;, vector(0,0,0), 1)&lt;br /&gt;
&lt;br /&gt;
    -- Only update velocity if the vehicle is near the ground, and we&#039;re in gear.&lt;br /&gt;
    if isVehicleOnGround() and Engine.gear ~= 0 then&lt;br /&gt;
        local velocityVector&lt;br /&gt;
        if Engine.gear &amp;gt; 0 then&lt;br /&gt;
            velocityVector = vector(Vehicle.speed, 0, 0)&lt;br /&gt;
        else&lt;br /&gt;
            -- Reverse gear&lt;br /&gt;
            velocityVector = vector(-Vehicle.speed, 0, 0)&lt;br /&gt;
        end&lt;br /&gt;
        ll.SetVelocity(velocityVector, 1)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Update the wheel rotations to visually match the steering.&lt;br /&gt;
    local maxWheelTurn = math.rad(30)  -- maximum wheel turn angle in radians&lt;br /&gt;
    local wheelAngle = -Vehicle.steering * maxWheelTurn&lt;br /&gt;
&lt;br /&gt;
    if LINK_WHEEL_LEFT &amp;gt; 0 then&lt;br /&gt;
        ll.SetLinkPrimitiveParamsFast(&lt;br /&gt;
            LINK_WHEEL_LEFT,&lt;br /&gt;
            {&lt;br /&gt;
                PRIM_ROT_LOCAL,&lt;br /&gt;
                ll.Euler2Rot(vector(-4.71239, -wheelAngle, 0))&lt;br /&gt;
            }&lt;br /&gt;
        )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if LINK_WHEEL_RIGHT &amp;gt; 0 then&lt;br /&gt;
        ll.SetLinkPrimitiveParamsFast(&lt;br /&gt;
            LINK_WHEEL_RIGHT,&lt;br /&gt;
            {&lt;br /&gt;
                PRIM_ROT_LOCAL,&lt;br /&gt;
                ll.Euler2Rot(vector(4.71239, wheelAngle, 0))&lt;br /&gt;
            }&lt;br /&gt;
        )&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Called when the object is touched: starts engine and simulation.&lt;br /&gt;
function touch_start(num)&lt;br /&gt;
    ll.Say(0, &amp;quot;Touch detected, requesting controls and starting engine.&amp;quot;)&lt;br /&gt;
    ll.RequestPermissions(ll.GetOwner(), PERMISSION_TAKE_CONTROLS)&lt;br /&gt;
    startEngine()&lt;br /&gt;
    findWheels()&lt;br /&gt;
    ll.SetTimerEvent(0.1)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Called when runtime permissions are granted.&lt;br /&gt;
function run_time_permissions(perm)&lt;br /&gt;
    if bit32.btest(perm, PERMISSION_TAKE_CONTROLS) then&lt;br /&gt;
        ll.Say(0, &amp;quot;Permissions granted.&amp;quot;)&lt;br /&gt;
        local controlMask = bit32.bor(CONTROL_FWD, CONTROL_BACK, CONTROL_UP, CONTROL_DOWN, CONTROL_LEFT, CONTROL_RIGHT)&lt;br /&gt;
        ll.TakeControls(controlMask, 1, 1)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Handle control events:&lt;br /&gt;
-- • CONTROL_FWD applies throttle.&lt;br /&gt;
-- • CONTROL_BACK applies the brakes.&lt;br /&gt;
-- • CONTROL_UP and CONTROL_DOWN shift gears.&lt;br /&gt;
-- • CONTROL_LEFT and CONTROL_RIGHT steer the vehicle.&lt;br /&gt;
function control(avatar_id, level, edge)&lt;br /&gt;
    local start = bit32.band(level, edge)&lt;br /&gt;
    local held = bit32.band(level, bit32.bnot(edge))&lt;br /&gt;
&lt;br /&gt;
    if bit32.band(held, CONTROL_BACK) ~= 0 then&lt;br /&gt;
        Vehicle.brake = true&lt;br /&gt;
        Engine.throttle = 0.0&lt;br /&gt;
    elseif bit32.band(held, CONTROL_FWD) ~= 0 then&lt;br /&gt;
        Vehicle.brake = false&lt;br /&gt;
        Engine.throttle = 1.0&lt;br /&gt;
        if Engine.gear == 0 then&lt;br /&gt;
            updateEngineRPMNeutral()&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        Vehicle.brake = false&lt;br /&gt;
        Engine.throttle = 0.0&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if bit32.band(start, CONTROL_UP) ~= 0 then&lt;br /&gt;
        shiftGear(Engine.gear + 1)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if bit32.band(start, CONTROL_DOWN) ~= 0 then&lt;br /&gt;
        shiftGear(Engine.gear - 1)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local steeringInput = 0&lt;br /&gt;
    if bit32.band(held, CONTROL_LEFT) ~= 0 then&lt;br /&gt;
        steeringInput = steeringInput + 1&lt;br /&gt;
    end&lt;br /&gt;
    if bit32.band(held, CONTROL_RIGHT) ~= 0 then&lt;br /&gt;
        steeringInput = steeringInput - 1&lt;br /&gt;
    end&lt;br /&gt;
    Vehicle.steering = steeringInput&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== weather_box.lua ==&lt;br /&gt;
This script demonstrates coroutines, as well as working with a JSON-based web API:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local TEXTBOX_CHANNEL = 323242&lt;br /&gt;
-- kludge because we don&#039;t have LSL constants in here yet&lt;br /&gt;
local HTTP_BODY_MAXLENGTH = integer(2)&lt;br /&gt;
local PRIM_TEXT = integer(26)&lt;br /&gt;
local PRIM_SIZE = integer(7)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
gCityName = &amp;quot;&amp;quot;&lt;br /&gt;
gLatLong = nil  -- A table of 2 elems when it&#039;s populated&lt;br /&gt;
gCityRequestTask = nil&lt;br /&gt;
gWeatherRequestTask = nil&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
-- JSON stuff&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
-- From https://github.com/rxi/json.lua/blob/master/json.lua&lt;br /&gt;
&lt;br /&gt;
local json = {}&lt;br /&gt;
&lt;br /&gt;
local escape_char_map = {&lt;br /&gt;
  [ &amp;quot;\\&amp;quot; ] = &amp;quot;\\&amp;quot;,&lt;br /&gt;
  [ &amp;quot;\&amp;quot;&amp;quot; ] = &amp;quot;\&amp;quot;&amp;quot;,&lt;br /&gt;
  [ &amp;quot;\b&amp;quot; ] = &amp;quot;b&amp;quot;,&lt;br /&gt;
  [ &amp;quot;\f&amp;quot; ] = &amp;quot;f&amp;quot;,&lt;br /&gt;
  [ &amp;quot;\n&amp;quot; ] = &amp;quot;n&amp;quot;,&lt;br /&gt;
  [ &amp;quot;\r&amp;quot; ] = &amp;quot;r&amp;quot;,&lt;br /&gt;
  [ &amp;quot;\t&amp;quot; ] = &amp;quot;t&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local escape_char_map_inv = { [ &amp;quot;/&amp;quot; ] = &amp;quot;/&amp;quot; }&lt;br /&gt;
for k, v in pairs(escape_char_map) do&lt;br /&gt;
  escape_char_map_inv[v] = k&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local parse&lt;br /&gt;
&lt;br /&gt;
local function create_set(...)&lt;br /&gt;
  local res = {}&lt;br /&gt;
  for i = 1, select(&amp;quot;#&amp;quot;, ...) do&lt;br /&gt;
    res[ select(i, ...) ] = true&lt;br /&gt;
  end&lt;br /&gt;
  return res&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local space_chars   = create_set(&amp;quot; &amp;quot;, &amp;quot;\t&amp;quot;, &amp;quot;\r&amp;quot;, &amp;quot;\n&amp;quot;)&lt;br /&gt;
local delim_chars   = create_set(&amp;quot; &amp;quot;, &amp;quot;\t&amp;quot;, &amp;quot;\r&amp;quot;, &amp;quot;\n&amp;quot;, &amp;quot;]&amp;quot;, &amp;quot;}&amp;quot;, &amp;quot;,&amp;quot;)&lt;br /&gt;
local escape_chars  = create_set(&amp;quot;\\&amp;quot;, &amp;quot;/&amp;quot;, &#039;&amp;quot;&#039;, &amp;quot;b&amp;quot;, &amp;quot;f&amp;quot;, &amp;quot;n&amp;quot;, &amp;quot;r&amp;quot;, &amp;quot;t&amp;quot;, &amp;quot;u&amp;quot;)&lt;br /&gt;
local literals      = create_set(&amp;quot;true&amp;quot;, &amp;quot;false&amp;quot;, &amp;quot;null&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local literal_map = {&lt;br /&gt;
  [ &amp;quot;true&amp;quot;  ] = true,&lt;br /&gt;
  [ &amp;quot;false&amp;quot; ] = false,&lt;br /&gt;
  [ &amp;quot;null&amp;quot;  ] = nil,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function next_char(str, idx, set, negate)&lt;br /&gt;
  for i = idx, #str do&lt;br /&gt;
    if set[str:sub(i, i)] ~= negate then&lt;br /&gt;
      return i&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return #str + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function decode_error(str, idx, msg)&lt;br /&gt;
  local line_count = 1&lt;br /&gt;
  local col_count = 1&lt;br /&gt;
  for i = 1, idx - 1 do&lt;br /&gt;
    col_count = col_count + 1&lt;br /&gt;
    if str:sub(i, i) == &amp;quot;\n&amp;quot; then&lt;br /&gt;
      line_count = line_count + 1&lt;br /&gt;
      col_count = 1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  error( string.format(&amp;quot;%s at line %d col %d&amp;quot;, msg, line_count, col_count) )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function codepoint_to_utf8(n)&lt;br /&gt;
  -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;amp;id=iws-appendixa&lt;br /&gt;
  local f = math.floor&lt;br /&gt;
  if n &amp;lt;= 0x7f then&lt;br /&gt;
    return string.char(n)&lt;br /&gt;
  elseif n &amp;lt;= 0x7ff then&lt;br /&gt;
    return string.char(f(n / 64) + 192, n % 64 + 128)&lt;br /&gt;
  elseif n &amp;lt;= 0xffff then&lt;br /&gt;
    return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)&lt;br /&gt;
  elseif n &amp;lt;= 0x10ffff then&lt;br /&gt;
    return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,&lt;br /&gt;
                       f(n % 4096 / 64) + 128, n % 64 + 128)&lt;br /&gt;
  end&lt;br /&gt;
  error( string.format(&amp;quot;invalid unicode codepoint &#039;%x&#039;&amp;quot;, n) )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function parse_unicode_escape(s)&lt;br /&gt;
  local n1 = tonumber( s:sub(1, 4),  16 )&lt;br /&gt;
  local n2 = tonumber( s:sub(7, 10), 16 )&lt;br /&gt;
   -- Surrogate pair?&lt;br /&gt;
  if n2 then&lt;br /&gt;
    return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)&lt;br /&gt;
  else&lt;br /&gt;
    return codepoint_to_utf8(n1)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function parse_string(str, i)&lt;br /&gt;
  local res = &amp;quot;&amp;quot;&lt;br /&gt;
  local j = i + 1&lt;br /&gt;
  local k = j&lt;br /&gt;
&lt;br /&gt;
  while j &amp;lt;= #str do&lt;br /&gt;
    local x = str:byte(j)&lt;br /&gt;
&lt;br /&gt;
    if x &amp;lt; 32 then&lt;br /&gt;
      decode_error(str, j, &amp;quot;control character in string&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    elseif x == 92 then -- `\`: Escape&lt;br /&gt;
      res = res .. str:sub(k, j - 1)&lt;br /&gt;
      j = j + 1&lt;br /&gt;
      local c = str:sub(j, j)&lt;br /&gt;
      if c == &amp;quot;u&amp;quot; then&lt;br /&gt;
        local hex = str:match(&amp;quot;^[dD][89aAbB]%x%x\\u%x%x%x%x&amp;quot;, j + 1)&lt;br /&gt;
                 or str:match(&amp;quot;^%x%x%x%x&amp;quot;, j + 1)&lt;br /&gt;
                 or decode_error(str, j - 1, &amp;quot;invalid unicode escape in string&amp;quot;)&lt;br /&gt;
        res = res .. parse_unicode_escape(hex)&lt;br /&gt;
        j = j + #hex&lt;br /&gt;
      else&lt;br /&gt;
        if not escape_chars[c] then&lt;br /&gt;
          decode_error(str, j - 1, &amp;quot;invalid escape char &#039;&amp;quot; .. c .. &amp;quot;&#039; in string&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
        res = res .. escape_char_map_inv[c]&lt;br /&gt;
      end&lt;br /&gt;
      k = j + 1&lt;br /&gt;
&lt;br /&gt;
    elseif x == 34 then -- `&amp;quot;`: End of string&lt;br /&gt;
      res = res .. str:sub(k, j - 1)&lt;br /&gt;
      return res, j + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    j = j + 1&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  decode_error(str, i, &amp;quot;expected closing quote for string&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function parse_number(str, i)&lt;br /&gt;
  local x = next_char(str, i, delim_chars)&lt;br /&gt;
  local s = str:sub(i, x - 1)&lt;br /&gt;
  local n = tonumber(s)&lt;br /&gt;
  if not n then&lt;br /&gt;
    decode_error(str, i, &amp;quot;invalid number &#039;&amp;quot; .. s .. &amp;quot;&#039;&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
  return n, x&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function parse_literal(str, i)&lt;br /&gt;
  local x = next_char(str, i, delim_chars)&lt;br /&gt;
  local word = str:sub(i, x - 1)&lt;br /&gt;
  if not literals[word] then&lt;br /&gt;
    decode_error(str, i, &amp;quot;invalid literal &#039;&amp;quot; .. word .. &amp;quot;&#039;&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
  return literal_map[word], x&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function parse_array(str, i)&lt;br /&gt;
  local res = {}&lt;br /&gt;
  local n = 1&lt;br /&gt;
  i = i + 1&lt;br /&gt;
  while 1 do&lt;br /&gt;
    local x&lt;br /&gt;
    i = next_char(str, i, space_chars, true)&lt;br /&gt;
    -- Empty / end of array?&lt;br /&gt;
    if str:sub(i, i) == &amp;quot;]&amp;quot; then&lt;br /&gt;
      i = i + 1&lt;br /&gt;
      break&lt;br /&gt;
    end&lt;br /&gt;
    -- Read token&lt;br /&gt;
    x, i = parse(str, i)&lt;br /&gt;
    res[n] = x&lt;br /&gt;
    n = n + 1&lt;br /&gt;
    -- Next token&lt;br /&gt;
    i = next_char(str, i, space_chars, true)&lt;br /&gt;
    local chr = str:sub(i, i)&lt;br /&gt;
    i = i + 1&lt;br /&gt;
    if chr == &amp;quot;]&amp;quot; then break end&lt;br /&gt;
    if chr ~= &amp;quot;,&amp;quot; then decode_error(str, i, &amp;quot;expected &#039;]&#039; or &#039;,&#039;&amp;quot;) end&lt;br /&gt;
  end&lt;br /&gt;
  return res, i&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function parse_object(str, i)&lt;br /&gt;
  local res = {}&lt;br /&gt;
  i = i + 1&lt;br /&gt;
  while 1 do&lt;br /&gt;
    local key, val&lt;br /&gt;
    i = next_char(str, i, space_chars, true)&lt;br /&gt;
    -- Empty / end of object?&lt;br /&gt;
    if str:sub(i, i) == &amp;quot;}&amp;quot; then&lt;br /&gt;
      i = i + 1&lt;br /&gt;
      break&lt;br /&gt;
    end&lt;br /&gt;
    -- Read key&lt;br /&gt;
    if str:sub(i, i) ~= &#039;&amp;quot;&#039; then&lt;br /&gt;
      decode_error(str, i, &amp;quot;expected string for key&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
    key, i = parse(str, i)&lt;br /&gt;
    -- Read &#039;:&#039; delimiter&lt;br /&gt;
    i = next_char(str, i, space_chars, true)&lt;br /&gt;
    if str:sub(i, i) ~= &amp;quot;:&amp;quot; then&lt;br /&gt;
      decode_error(str, i, &amp;quot;expected &#039;:&#039; after key&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
    i = next_char(str, i + 1, space_chars, true)&lt;br /&gt;
    -- Read value&lt;br /&gt;
    val, i = parse(str, i)&lt;br /&gt;
    -- Set&lt;br /&gt;
    res[key] = val&lt;br /&gt;
    -- Next token&lt;br /&gt;
    i = next_char(str, i, space_chars, true)&lt;br /&gt;
    local chr = str:sub(i, i)&lt;br /&gt;
    i = i + 1&lt;br /&gt;
    if chr == &amp;quot;}&amp;quot; then break end&lt;br /&gt;
    if chr ~= &amp;quot;,&amp;quot; then decode_error(str, i, &amp;quot;expected &#039;}&#039; or &#039;,&#039;&amp;quot;) end&lt;br /&gt;
  end&lt;br /&gt;
  return res, i&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local char_func_map = {&lt;br /&gt;
  [ &#039;&amp;quot;&#039; ] = parse_string,&lt;br /&gt;
  [ &amp;quot;0&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;1&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;2&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;3&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;4&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;5&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;6&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;7&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;8&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;9&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;-&amp;quot; ] = parse_number,&lt;br /&gt;
  [ &amp;quot;t&amp;quot; ] = parse_literal,&lt;br /&gt;
  [ &amp;quot;f&amp;quot; ] = parse_literal,&lt;br /&gt;
  [ &amp;quot;n&amp;quot; ] = parse_literal,&lt;br /&gt;
  [ &amp;quot;[&amp;quot; ] = parse_array,&lt;br /&gt;
  [ &amp;quot;{&amp;quot; ] = parse_object,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
parse = function(str, idx)&lt;br /&gt;
  local chr = str:sub(idx, idx)&lt;br /&gt;
  local f = char_func_map[chr]&lt;br /&gt;
  if f then&lt;br /&gt;
    return f(str, idx)&lt;br /&gt;
  end&lt;br /&gt;
  decode_error(str, idx, &amp;quot;unexpected character &#039;&amp;quot; .. chr .. &amp;quot;&#039;&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function json.decode(str)&lt;br /&gt;
  if type(str) ~= &amp;quot;string&amp;quot; then&lt;br /&gt;
    error(&amp;quot;expected argument of type string, got &amp;quot; .. type(str))&lt;br /&gt;
  end&lt;br /&gt;
  local res, idx = parse(str, next_char(str, 1, space_chars, true))&lt;br /&gt;
  idx = next_char(str, idx, space_chars, true)&lt;br /&gt;
  if idx &amp;lt;= #str then&lt;br /&gt;
    decode_error(str, idx, &amp;quot;trailing garbage&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
  return res&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
-- Event Loop&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
-- A naive little event loop that lets you track running coroutines and dispatches&lt;br /&gt;
-- the events they&#039;re waiting for to them.&lt;br /&gt;
&lt;br /&gt;
local EventLoop = {&lt;br /&gt;
    -- Coroutine -&amp;gt; awaited event&lt;br /&gt;
    _coros = {},&lt;br /&gt;
    running = false&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function EventLoop:create_task(func)&lt;br /&gt;
    local coro = coroutine.create(func)&lt;br /&gt;
    -- false has no semantic meaning here, we just want to reserve the space for&lt;br /&gt;
    -- the coroutine in the table without saying we&#039;re awaiting anything yet.&lt;br /&gt;
    self._coros[coro] = false&lt;br /&gt;
    self:_run_coro(coro)&lt;br /&gt;
    return coro&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function EventLoop:kill_task(coro)&lt;br /&gt;
    self._coros[coro] = nil&lt;br /&gt;
    if coroutine.status ~= &amp;quot;dead&amp;quot; then&lt;br /&gt;
        coroutine.close(coro)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- (internal) run the event&lt;br /&gt;
function EventLoop:_run_coro(coro, ...)&lt;br /&gt;
    assert(coroutine.status(coro) ~= &amp;quot;dead&amp;quot;)&lt;br /&gt;
    -- assert(not self.running)&lt;br /&gt;
    local old_running = self.running&lt;br /&gt;
    self.running = true&lt;br /&gt;
&lt;br /&gt;
    -- Since we&#039;re running, this isn&#039;t currently awaiting anything&lt;br /&gt;
    self._coros[coro] = false&lt;br /&gt;
    local success, ret = coroutine.resume(coro, ...)&lt;br /&gt;
    self.running = old_running&lt;br /&gt;
&lt;br /&gt;
    -- If we&#039;re not done then ret should be the kind of event we&#039;re awaiting.&lt;br /&gt;
    -- Otherwise it&#039;s an error message or something. Who cares.&lt;br /&gt;
    if not success then&lt;br /&gt;
        -- Get rid of the coro&lt;br /&gt;
        if ret then&lt;br /&gt;
            ll.OwnerSay(`Might have an error: {ret}`)&lt;br /&gt;
        end&lt;br /&gt;
        ret = nil&lt;br /&gt;
    end&lt;br /&gt;
    self._coros[coro] = ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Wake up any tasks that are waiting for this kind of event&lt;br /&gt;
function EventLoop:handle_event(name, ...)&lt;br /&gt;
    -- We may mutate self._coros, so do a clone&lt;br /&gt;
    local coros = table.clone(self._coros)&lt;br /&gt;
    ll.OwnerSay(`Handling {name}`)&lt;br /&gt;
&lt;br /&gt;
    for coro, awaited_event in coros do&lt;br /&gt;
        if coroutine.status(coro) == &amp;quot;dead&amp;quot; then&lt;br /&gt;
            -- Hmm, this coroutine is dead, prune it.&lt;br /&gt;
            self._coros[coro] = nil&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if awaited_event == name then&lt;br /&gt;
            ll.OwnerSay(`Dispatching {name} to {coro}`)&lt;br /&gt;
            self:_run_coro(coro, ...)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function EventLoop:create_event_catcher(name)&lt;br /&gt;
    local function catcher(...)&lt;br /&gt;
        ll.OwnerSay(&amp;quot;yay&amp;quot;)&lt;br /&gt;
        EventLoop:handle_event(name, ...)&lt;br /&gt;
    end&lt;br /&gt;
    return catcher&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Helper function for waiting and telling the EventLoop what event you want&lt;br /&gt;
local function await_event(kind)&lt;br /&gt;
    -- You had better do this inside a running event loop&lt;br /&gt;
    assert(EventLoop.running)&lt;br /&gt;
    -- This will yield, the EventLoop will resume us once it has&lt;br /&gt;
    -- an event we might be interested in.&lt;br /&gt;
    return coroutine.yield(kind)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
-- Script-specific functions&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
local function hide_children()&lt;br /&gt;
    for i=2,ll.GetNumberOfPrims() do&lt;br /&gt;
        -- Hide all the child prims&lt;br /&gt;
        ll.SetLinkAlpha(i, 0, -1)&lt;br /&gt;
        ll.SetLinkPrimitiveParamsFast(i, {&lt;br /&gt;
            PRIM_SIZE, vector(0.5, 0.5, 0.5),&lt;br /&gt;
            PRIM_TEXT, &amp;quot;&amp;quot;, vector(1,1,1), 1&lt;br /&gt;
        })&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function send_textbox(avatar_id, message, channel)&lt;br /&gt;
    ll.TextBox(avatar_id, message, channel)&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        local resp_channel, name, id, resp = await_event(&amp;quot;listen&amp;quot;)&lt;br /&gt;
        if id ~= avatar_id or channel ~= resp_channel then&lt;br /&gt;
            -- If this isn&#039;t a message we&#039;re interested in then wait for the next event&lt;br /&gt;
            ll.OwnerSay(`{id}, {avatar_id}, {resp_channel}, {channel}`)&lt;br /&gt;
            continue&lt;br /&gt;
        end&lt;br /&gt;
        return resp&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function make_json_request(url)&lt;br /&gt;
    local req_id = ll.HTTPRequest(url, {HTTP_BODY_MAXLENGTH, integer(16000)}, &amp;quot;&amp;quot;)&lt;br /&gt;
    while true do&lt;br /&gt;
        local ev_id, status, metadata, body = await_event(&amp;quot;http_response&amp;quot;)&lt;br /&gt;
        if ev_id ~= req_id then&lt;br /&gt;
            -- Not a response for the HTTP request we were waiting on, wait for the next event.&lt;br /&gt;
            continue&lt;br /&gt;
        end&lt;br /&gt;
        return status, metadata, body&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function request_weather()&lt;br /&gt;
    -- And you can use a wrapper like this to make an async function callable within both normal and coroutine contexts.&lt;br /&gt;
    -- Normally this sort of thing would not be necessary, but here it is since the EventLoop lives in our own code,&lt;br /&gt;
    -- and isn&#039;t a property of the actual script engine.&lt;br /&gt;
    if gWeatherRequestTask then&lt;br /&gt;
        EventLoop:kill_task(gWeatherRequestTask)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    gWeatherRequestTask = EventLoop:create_task(function()&lt;br /&gt;
    ll.OwnerSay(&amp;quot;Requesting weather&amp;quot;)&lt;br /&gt;
        local status, metadata, body = make_json_request(`https://api.open-meteo.com/v1/forecast?latitude={gLatLong[1]}&amp;amp;longitude={gLatLong[2]}&amp;amp;current=temperature_2m&amp;amp;daily=temperature_2m_max`)&lt;br /&gt;
        if status ~= 200 then&lt;br /&gt;
            ll.OwnerSay(&amp;quot;Error requesting weather&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
        end&lt;br /&gt;
        local parsed = json.decode(body)&lt;br /&gt;
&lt;br /&gt;
        local current = parsed[&amp;quot;current&amp;quot;]&lt;br /&gt;
        ll.OwnerSay(`Temperature is {current[&amp;quot;temperature_2m&amp;quot;]}C`)&lt;br /&gt;
        ll.SetText(`Temperature in {gCityName} is currently {current[&amp;quot;temperature_2m&amp;quot;]}C`, vector(1,1,1), 1)&lt;br /&gt;
&lt;br /&gt;
        -- Show the highs for each day&lt;br /&gt;
        local days = parsed[&amp;quot;daily&amp;quot;][&amp;quot;temperature_2m_max&amp;quot;]&lt;br /&gt;
        for i=1,#days do&lt;br /&gt;
            local link_num = i + 1&lt;br /&gt;
            -- Higher temps = bigger on Z, negatives are unrepresentable :)&lt;br /&gt;
            local z_size = (days[i] / 30) * 2&lt;br /&gt;
            ll.SetLinkAlpha(link_num, 1, -1)&lt;br /&gt;
            ll.SetLinkPrimitiveParamsFast(link_num, {&lt;br /&gt;
                PRIM_SIZE, vector(0.5, 0.5, z_size),&lt;br /&gt;
                PRIM_TEXT, `{days[i]}C`, vector(1,1,1), 1&lt;br /&gt;
            })&lt;br /&gt;
        end&lt;br /&gt;
    end)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ask_for_city()&lt;br /&gt;
    local resp = send_textbox(ll.GetOwner(), &amp;quot;What city do you want the weather for?&amp;quot;, TEXTBOX_CHANNEL)&lt;br /&gt;
    if not resp then&lt;br /&gt;
        ll.OwnerSay(&amp;quot;Got no response for city request.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    ll.OwnerSay(`You want the weather for {resp}.`)&lt;br /&gt;
    gLatLong = nil&lt;br /&gt;
    gCityName = resp&lt;br /&gt;
&lt;br /&gt;
    hide_children()&lt;br /&gt;
&lt;br /&gt;
    -- request the lat and long for this city&lt;br /&gt;
    local status, metadata, body = make_json_request(`https://nominatim.openstreetmap.org/search.php?city={ll.EscapeURL(resp)}&amp;amp;format=jsonv2`)&lt;br /&gt;
    local parsed = json.decode(body)&lt;br /&gt;
    if not #parsed then&lt;br /&gt;
        ll.OwnerSay(`Got no results for {resp}!`)&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local first_result = parsed[1]&lt;br /&gt;
    gLatLong = {first_result[&amp;quot;lat&amp;quot;], first_result[&amp;quot;lon&amp;quot;]}&lt;br /&gt;
    ll.OwnerSay(`City Lat: {gLatLong[1]}, Long: {gLatLong[2]}`)&lt;br /&gt;
&lt;br /&gt;
    request_weather()&lt;br /&gt;
&lt;br /&gt;
    -- request weather again in 3 mins&lt;br /&gt;
    ll.SetTimerEvent(180)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
-- Event Handlers&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- If we don&#039;t care about conventionally dispatched events, we can just set the event handler to directly send&lt;br /&gt;
-- event data to the event loop.&lt;br /&gt;
http_response = EventLoop:create_event_catcher(&amp;quot;http_response&amp;quot;)&lt;br /&gt;
listen = EventLoop:create_event_catcher(&amp;quot;listen&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- More conventional event handlers work too.&lt;br /&gt;
function touch_start(num)&lt;br /&gt;
    for i=0,num-1 do&lt;br /&gt;
        if ll.GetOwner() ~= ll.DetectedKey(i) then&lt;br /&gt;
            -- Go to next loop if it wasn&#039;t the owner touching us.&lt;br /&gt;
            continue&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if gCityRequestTask then&lt;br /&gt;
            EventLoop:kill_task(gCityRequestTask)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        -- Spin up a task to ask the user for their city&lt;br /&gt;
        gCityRequestTask = EventLoop:create_task(ask_for_city)&lt;br /&gt;
        break&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function timer()&lt;br /&gt;
    request_weather()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Not strictly necessary, but I like having the main function have its own scope.&lt;br /&gt;
-- Rather than just have top-level logic.&lt;br /&gt;
local function main()&lt;br /&gt;
    ll.Listen(TEXTBOX_CHANNEL, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
    ll.SetText(&amp;quot;&amp;quot;, vector(1,1,1), 1)&lt;br /&gt;
    hide_children()&lt;br /&gt;
    ll.OwnerSay(&amp;quot;Weatherbox operational!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
main()&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== dialog_coroutine.lua ==&lt;br /&gt;
This script demonstrates how one could use coroutines to handle dialog responses, with multi-user support.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-------------------------&lt;br /&gt;
-- Minimal EventLoop&lt;br /&gt;
-------------------------&lt;br /&gt;
local EventLoop = {&lt;br /&gt;
    -- Coroutine -&amp;gt; eventName it’s waiting for&lt;br /&gt;
    _coros = {},&lt;br /&gt;
    running = false&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function EventLoop:create_task(func)&lt;br /&gt;
    local coro = coroutine.create(func)&lt;br /&gt;
    self._coros[coro] = false&lt;br /&gt;
    self:_run_coro(coro)&lt;br /&gt;
    return coro&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function EventLoop:kill_task(coro)&lt;br /&gt;
    self._coros[coro] = nil&lt;br /&gt;
    if coroutine.status(coro) ~= &amp;quot;dead&amp;quot; then&lt;br /&gt;
        coroutine.close(coro)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Internal helper: resumes a coroutine&lt;br /&gt;
function EventLoop:_run_coro(coro, ...)&lt;br /&gt;
    if coroutine.status(coro) == &amp;quot;dead&amp;quot; then&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local old_running = self.running&lt;br /&gt;
    self.running = true&lt;br /&gt;
&lt;br /&gt;
    self._coros[coro] = false&lt;br /&gt;
    local ok, eventAwaited = coroutine.resume(coro, ...)&lt;br /&gt;
    self.running = old_running&lt;br /&gt;
&lt;br /&gt;
    if not ok then&lt;br /&gt;
        ll.OwnerSay(`Coroutine error: {eventAwaited}`)&lt;br /&gt;
        self._coros[coro] = nil&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- If still alive, &#039;eventAwaited&#039; is the next event it wants&lt;br /&gt;
    self._coros[coro] = eventAwaited&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function EventLoop:handle_event(eventName, ...)&lt;br /&gt;
    ll.OwnerSay(`Handling event {eventName}`)&lt;br /&gt;
    local snapshot = table.clone(self._coros)&lt;br /&gt;
    for coro, waitingFor in pairs(snapshot) do&lt;br /&gt;
        if coroutine.status(coro) == &amp;quot;dead&amp;quot; then&lt;br /&gt;
            self._coros[coro] = nil&lt;br /&gt;
        elseif waitingFor == eventName then&lt;br /&gt;
            ll.OwnerSay(`Dispatching event {eventName} to {coro}`)&lt;br /&gt;
            self:_run_coro(coro, ...)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Coroutines use this to yield until an event&lt;br /&gt;
local function await_event(name)&lt;br /&gt;
    assert(EventLoop.running, &amp;quot;await_event called outside a coroutine!&amp;quot;)&lt;br /&gt;
    return coroutine.yield(name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-------------------------&lt;br /&gt;
-- Script Logic&lt;br /&gt;
-------------------------&lt;br /&gt;
local buttons = {&amp;quot;-&amp;quot;, &amp;quot;Red&amp;quot;, &amp;quot;Green&amp;quot;, &amp;quot;Yellow&amp;quot;}&lt;br /&gt;
local dialogInfo = &amp;quot;\nPlease make a choice.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
-- Use the chat listener to feed the event-loop&lt;br /&gt;
function listen(channel, name, sender_id, message)&lt;br /&gt;
    -- We handle all &#039;listen&#039; events via the event-loop&lt;br /&gt;
    EventLoop:handle_event(`listen_{channel}`, channel, name, sender_id, message)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Called when the script starts&lt;br /&gt;
function state_entry()&lt;br /&gt;
    -- Seed math.random so each new script run doesn’t repeat the same channels&lt;br /&gt;
    math.randomseed(ll.GetUnixTime())&lt;br /&gt;
    ll.OwnerSay(&amp;quot;Script started with random channels for each user.&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- A coroutine function for a single user&#039;s dialog flow&lt;br /&gt;
local function handle_dialog_for_user(userId)&lt;br /&gt;
    -- Use a random channel&lt;br /&gt;
    local channel = math.random(0x1, 0xFFFF)&lt;br /&gt;
    -- Create a listener for that channel&lt;br /&gt;
    local listenHandle = ll.Listen(channel, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        -- Show the user a dialog&lt;br /&gt;
        ll.Dialog(userId, dialogInfo, buttons, channel)&lt;br /&gt;
&lt;br /&gt;
        -- Wait for the next &#039;listen&#039; event (channel, name, sender_id, message)&lt;br /&gt;
        local c, n, sid, msg = await_event(`listen_{channel}`)&lt;br /&gt;
&lt;br /&gt;
        -- If this &amp;quot;listen&amp;quot; event isn&#039;t for our channel/user, ignore it&lt;br /&gt;
        if c == channel and sid == userId then&lt;br /&gt;
            -- If user pressed &amp;quot;-&amp;quot;, re-display the menu, else they picked a final color&lt;br /&gt;
            if msg ~= &amp;quot;-&amp;quot; then&lt;br /&gt;
                ll.Say(0, `{n} selected {msg}`)&lt;br /&gt;
                -- Now that they&#039;ve chosen something else, remove the listener and finish&lt;br /&gt;
                ll.ListenRemove(listenHandle)&lt;br /&gt;
                return&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Called when the object is touched&lt;br /&gt;
function touch_start(num_detected)&lt;br /&gt;
    for i=0, num_detected-1 do&lt;br /&gt;
        local toucherId = ll.DetectedKey(i)&lt;br /&gt;
        -- Create a separate coroutine for each person who touches&lt;br /&gt;
        EventLoop:create_task(function()&lt;br /&gt;
            handle_dialog_for_user(toucherId)&lt;br /&gt;
        end)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Run state_entry on load:&lt;br /&gt;
state_entry()&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Lua_Alpha&amp;diff=1218106</id>
		<title>Lua Alpha</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Lua_Alpha&amp;diff=1218106"/>
		<updated>2025-03-23T14:34:03Z</updated>

		<summary type="html">&lt;p&gt;Wulfie Reanimator: /* default_script.lua */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Warning|This functionality is in alpha. Instability is to be expected, and there may be very sharp edges. At this point it is expected that Luau can crash regions and perform other types of undesirable behavior.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🚨 PLEASE NOTE Memory and performance characteristics, and API specifics may change! Scripts are currently being run in unoptimized form for development purposes.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
= Second Life Lua (SLua) Alpha =&lt;br /&gt;
&lt;br /&gt;
[[File:Luau.png|720px|thumb|right|Luau logo]]&lt;br /&gt;
&lt;br /&gt;
We&#039;re thrilled to announce the launch of the SLua Alpha for Second Life! This significant update introduces the Lua scripting language, offering creators enhanced performance, improved memory efficiency, and a more versatile scripting environment.&lt;br /&gt;
&lt;br /&gt;
To get started, [https://wiki.secondlife.com/wiki/Try_SLua read the instructions here.]&lt;br /&gt;
&lt;br /&gt;
== What is SLua? ==&lt;br /&gt;
&lt;br /&gt;
SLua is scripting for Second Life based on Luau, a fast, small, safe, and gradually typed embeddable scripting language derived from Lua. It is designed to be backwards compatible with Lua 5.1, incorporating features from future Lua releases and expanding the feature set with type annotations and a state-of-the-art type inference system. Luau is largely implemented from scratch, with the language runtime being a heavily modified version of the Lua 5.1 runtime, featuring a completely rewritten interpreter and other performance innovations.&lt;br /&gt;
&lt;br /&gt;
== Why Lua? ==&lt;br /&gt;
&lt;br /&gt;
The decision to integrate Lua into Second Life was driven by its ability to meet all the requirements for a scripting engine within the platform. Lua offers a high-quality scripting experience to creators, addressing many of the limitations present in the current LSL (Linden Scripting Language) environment. Its lightweight nature and performance optimizations make it an ideal choice for enhancing the scripting capabilities in Second Life. For more information on why Lua was chosen, please see the [https://wiki.secondlife.com/wiki/Lua_FAQ Lua FAQ].&lt;br /&gt;
&lt;br /&gt;
== How to Get Started with SLua ==&lt;br /&gt;
&lt;br /&gt;
In order to play with SLua, you&#039;ll need to download our Lua project viewer, and log onto our [https://lindenlab.freshdesk.com/support/solutions/articles/31000156725-accessing-aditi Aditi beta grid].&lt;br /&gt;
&lt;br /&gt;
* Access the latest build of the SLua-enabled Second Life Viewer from [https://releasenotes.secondlife.com/viewer/7.1.12.13526902562.html here].&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve got the new viewer and have logged onto the beta grid, head over to these SLua-enabled regions:&lt;br /&gt;
&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Yardang/241/235/27 SLua Yardang]&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Tombolo/241/235/27 SLua Tombolo]&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Mesa/241/235/27 SLua Mesa]&lt;br /&gt;
* [secondlife://Aditi/secondlife/SLua%20Tideland/241/235/27 SLua Tideland]&lt;br /&gt;
&lt;br /&gt;
When editing a script in the new Lua project viewer, you&#039;ll notice a new &#039;&#039;&#039;Compiler&#039;&#039;&#039; drop-down near the save button. This drop-down will allow you to select which compiler will be used, as well as which script runtime will be used (LSO2, Mono, Luau).&lt;br /&gt;
&lt;br /&gt;
[[File:Compiler_dropdown.png|Compiler selection dropdown]]&lt;br /&gt;
&lt;br /&gt;
Compiler drop-down options:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LSL: Legacy (LSO2)&#039;&#039;&#039; - Scripts written in LSL, to be run on the old LSO2 VM&lt;br /&gt;
* &#039;&#039;&#039;LSL: Mono&#039;&#039;&#039;- Scripts written in LSL, to be run on the Mono VM&lt;br /&gt;
* &#039;&#039;&#039;Lua&#039;&#039;&#039; - Scripts written in Lua, to be run on the SLua VM&lt;br /&gt;
* &#039;&#039;&#039;LSL/Luau&#039;&#039;&#039;- Scripts written in LSL, to be run on the SLua VM&lt;br /&gt;
&lt;br /&gt;
=== Transitioning from LSL to SLua ===&lt;br /&gt;
* &#039;&#039;&#039;Function Namespacing:&#039;&#039;&#039;&lt;br /&gt;
** In SLua, Linden Lab functions have been moved under the &#039;&#039;&#039;ll&#039;&#039;&#039; namespace.&lt;br /&gt;
** For example:&lt;br /&gt;
*** &#039;&#039;llSay&#039;&#039; becomes &#039;&#039;ll.Say&#039;&#039;&lt;br /&gt;
*** &#039;&#039;llGetPos&#039;&#039; becomes &#039;&#039;ll.GetPos&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Lists&#039;&#039;&#039;&lt;br /&gt;
** Lua indexes begin from 1, unlike LSL where indexes begin from 0.&lt;br /&gt;
** Lua uses &amp;lt;code&amp;gt;{}&amp;lt;/code&amp;gt; for &#039;&#039;tables&#039;&#039;, unlike LSL where &amp;lt;code&amp;gt;[]&amp;lt;/code&amp;gt; is used for &#039;&#039;lists&#039;&#039;.&lt;br /&gt;
* Types&lt;br /&gt;
** SLua doesn&#039;t support the usual vector/rotation syntax &amp;lt;code&amp;gt;&amp;lt;x, y, z&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
*** Instead, these values are created with the functions &amp;lt;code&amp;gt;vector(x, y, z)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rotation(x, y, z, s)&amp;lt;/code&amp;gt;&lt;br /&gt;
*** Similar functions exist for &#039;&#039;&#039;uuid&#039;&#039;&#039; (key) and &#039;&#039;&#039;integer&#039;&#039;&#039; (distinct from the built-in &#039;&#039;&#039;number&#039;&#039;&#039; type)&lt;br /&gt;
&lt;br /&gt;
=== SLua Libraries ===&lt;br /&gt;
* &#039;&#039;&#039;Coroutines:&#039;&#039;&#039;&lt;br /&gt;
** SLua supports coroutines, allowing for cooperative multitasking within scripts.&lt;br /&gt;
** Key functions include:&lt;br /&gt;
*** &#039;&#039;coroutine.create&#039;&#039;&lt;br /&gt;
*** &#039;&#039;coroutine.status&#039;&#039;&lt;br /&gt;
*** &#039;&#039;coroutine.resume&#039;&#039;&lt;br /&gt;
** Refer to the [https://luau.org/library#coroutine-library coroutine library documentation] for more details.&lt;br /&gt;
* &#039;&#039;&#039;Bitwise Operations:&#039;&#039;&#039;&lt;br /&gt;
** SLua includes a &#039;&#039;bit32&#039;&#039; library for bitwise operations, enabling more efficient data manipulation.&lt;br /&gt;
** Refer to the [https://luau.org/library#bit32-library bit32 library documentation] for more details.&lt;br /&gt;
* &#039;&#039;&#039;Standard Library:&#039;&#039;&#039;&lt;br /&gt;
** SLua comes equipped with a standard library of functions designed to manipulate built-in data types.&lt;br /&gt;
** Explore the [https://luau.org/library Luau Standard Library] for a comprehensive list of available functions.&lt;br /&gt;
&lt;br /&gt;
== Feedback and Support ==&lt;br /&gt;
&lt;br /&gt;
We encourage all creators to explore the new scripting capabilities and provide feedback. Your insights are invaluable in refining and enhancing this feature. For more information and to share your experiences, please refer to our [https://wiki.secondlife.com/wiki/Lua_FAQ Lua FAQ].&lt;br /&gt;
&lt;br /&gt;
== Example Scripts ==&lt;br /&gt;
&lt;br /&gt;
To help you get started, we&#039;ve assembled some example scripts that demonstrate the capabilities of SLua. These scripts cover various functionalities and can serve as a foundation for your own creations. Please feel free to propose changes to these scripts, or modify them to your heart&#039;s desire!&lt;br /&gt;
&lt;br /&gt;
=== default_script.lua ===&lt;br /&gt;
This script is roughly equivalent to the default &amp;quot;new script&amp;quot; that gets created for LSL.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function state_entry()&lt;br /&gt;
   ll.Say(0, &amp;quot;Hello, Avatar!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function touch_start(total_number)&lt;br /&gt;
   ll.Say(0, &amp;quot;Touched.&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Simulate the state_entry event&lt;br /&gt;
state_entry()&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dialog.lua ===&lt;br /&gt;
This script demonstrates how one can interact with dialog menus.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Define the menu buttons and dialog message.&lt;br /&gt;
local buttons = {&amp;quot;-&amp;quot;, &amp;quot;Red&amp;quot;, &amp;quot;Green&amp;quot;, &amp;quot;Yellow&amp;quot;}&lt;br /&gt;
local dialogInfo = &amp;quot;\nPlease make a choice.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local ToucherID = nil&lt;br /&gt;
local dialogChannel = nil&lt;br /&gt;
local listenHandle = nil&lt;br /&gt;
&lt;br /&gt;
-- This function is called when the script first starts.&lt;br /&gt;
function state_entry()&lt;br /&gt;
    -- Get the object&#039;s key and compute a dialog channel number.&lt;br /&gt;
    local key = ll.GetKey()&lt;br /&gt;
    -- Extract the last 7 characters of the key and convert it from hex.&lt;br /&gt;
    dialogChannel = -1 - tonumber(string.sub(tostring(key), -7, -1), 16)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Called when the object is touched.&lt;br /&gt;
function touch_start(num_detected)&lt;br /&gt;
    ToucherID = ll.DetectedKey(0)&lt;br /&gt;
    -- If there is already a listen handle, then remove it&lt;br /&gt;
    if listenHandle then&lt;br /&gt;
        ll.ListenRemove(listenHandle)&lt;br /&gt;
    end&lt;br /&gt;
    listenHandle = ll.Listen(dialogChannel, &amp;quot;&amp;quot;, ToucherID, &amp;quot;&amp;quot;)&lt;br /&gt;
    ll.Dialog(ToucherID, dialogInfo, buttons, dialogChannel)&lt;br /&gt;
    -- Set a 60-second timer for response.&lt;br /&gt;
    ll.SetTimerEvent(60.0)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Called when a dialog response is received.&lt;br /&gt;
function listen(channel, name, sender_id, message)&lt;br /&gt;
    if message == &amp;quot;-&amp;quot; then&lt;br /&gt;
        -- Redisplay the dialog if the &amp;quot;-&amp;quot; option is selected.&lt;br /&gt;
        ll.Dialog(ToucherID, dialogInfo, buttons, dialogChannel)&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
    -- Stop the timer, and stop the listening handler.&lt;br /&gt;
    ll.ListenRemove(listenHandle)&lt;br /&gt;
    ll.SetTimerEvent(0)&lt;br /&gt;
    -- Let the user know what they selected&lt;br /&gt;
    ll.Say(0, `You selected {message}`)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Called when the timer expires.&lt;br /&gt;
function timer()&lt;br /&gt;
    -- Stop the timer and clean up the listener.&lt;br /&gt;
    if listenHandle then&lt;br /&gt;
        ll.SetTimerEvent(0)&lt;br /&gt;
        ll.ListenRemove(listenHandle)&lt;br /&gt;
        ll.Whisper(0, &amp;quot;Sorry. You snooze; you lose.&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Invoke state_entry on startup, since simulator doesn&#039;t invoke &lt;br /&gt;
-- it like it does in LSL&lt;br /&gt;
state_entry()&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== user_input_coroutine.lua ===&lt;br /&gt;
This script demonstrates [https://www.lua.org/pil/9.html coroutines] and how they can simplify the overarching logic of a script, enabling us to write the bulk of our multi-event code within a centralized function instead of fragmenting across separate event handlers.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Wait for user input mid-function before doing something useful with it.&lt;br /&gt;
main = function(toucher)&lt;br /&gt;
    local handle = ll.Listen(0, &amp;quot;&amp;quot;, toucher, &amp;quot;&amp;quot;)&lt;br /&gt;
    local event = touch_start   -- save function for later&lt;br /&gt;
    touch_start = nil           -- disable touch_start&lt;br /&gt;
&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Do you want pants or gloves?&amp;quot;)&lt;br /&gt;
    local clothing = coroutine.yield() -- pause the routine&#039;s execution here&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;For men or women?&amp;quot;)&lt;br /&gt;
    local gender = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Favorite color?&amp;quot;)&lt;br /&gt;
    local color = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Here&#039;s &amp;quot;..color..&amp;quot; &amp;quot;..clothing..&amp;quot; for &amp;quot;..gender)&lt;br /&gt;
&lt;br /&gt;
    ll.ListenRemove(handle)&lt;br /&gt;
    touch_start = event -- restore touch_start&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function touch_start(total_num)&lt;br /&gt;
    local toucher = ll.DetectedKey(0)&lt;br /&gt;
    routine = coroutine.create(main)    -- new coroutine&lt;br /&gt;
    coroutine.resume(routine, toucher)  -- run coroutine (with one argument)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- When the coroutine is suspended, incoming events can be handled&lt;br /&gt;
-- and we can resume() execution of the routine&lt;br /&gt;
-- and pass any number of arguments to be returned by yield()&lt;br /&gt;
function listen(channel, name, id, message)&lt;br /&gt;
    coroutine.resume(routine, message)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== multi_user_input_coroutine.lua ===&lt;br /&gt;
Following from the above example, how can we handle multiple users? This is where coroutines shine.&lt;br /&gt;
&lt;br /&gt;
Instead of disabling touches to prevent others from interacting with the object, we can create new copies of the coroutine each time an avatar touches the object. We can then resume whichever coroutine is needed, based on the avatar, while all of them track their own progress separately and automagically.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Key: avatar uuid; Value: coroutine thread&lt;br /&gt;
routines = {}&lt;br /&gt;
&lt;br /&gt;
main = function(toucher)&lt;br /&gt;
    local handle = ll.Listen(0, &amp;quot;&amp;quot;, toucher, &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Do you want pants or gloves?&amp;quot;)&lt;br /&gt;
    local clothing = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;For men or women?&amp;quot;)&lt;br /&gt;
    local gender = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Favorite color?&amp;quot;)&lt;br /&gt;
    local color = coroutine.yield()&lt;br /&gt;
    ll.RegionSayTo(toucher, 0, &amp;quot;Here&#039;s &amp;quot;..color..&amp;quot; &amp;quot;..clothing..&amp;quot; for &amp;quot;..gender)&lt;br /&gt;
&lt;br /&gt;
    ll.ListenRemove(handle)&lt;br /&gt;
    routines[toucher] = nil -- Remove from collection&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function touch_start(total_num)&lt;br /&gt;
    local toucher = ll.DetectedKey(0)&lt;br /&gt;
    local routine = routines[toucher]&lt;br /&gt;
    if not routine then -- New user needs new routine&lt;br /&gt;
        routine = coroutine.create(main)&lt;br /&gt;
        routines[toucher] = routine -- Add to collection&lt;br /&gt;
        coroutine.resume(routine, toucher)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function listen(channel, name, id, message)&lt;br /&gt;
    coroutine.resume(routines[id], message) -- Resume a specific coroutine&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More Examples ===&lt;br /&gt;
&lt;br /&gt;
* Find more example scripts at [[Luau Example Scripts]]&lt;br /&gt;
&lt;br /&gt;
* [https://roblox.github.io/lua-style-guide/gotchas/ Lua Gotchas, Footguns and Other Hazards]&lt;/div&gt;</summary>
		<author><name>Wulfie Reanimator</name></author>
	</entry>
</feed>