Difference between revisions of "HUD Dots Radar"

From Second Life Wiki
Jump to navigation Jump to search
(noe on ctrtl-9)
m
Line 1: Line 1:
Here is a simple example of drawing points on the HUD that cover a point in the region. It is based on Babbage's [[SLateIt]], with the HUD drawing parts extracted for easier reuse.
Here is a simple example of drawing points on the HUD that cover a point in the region. It is based on Babbage's [[SLateIt]], with the HUD drawing parts extracted for easier reuse.


__TOC__
==build script==
First you will need to create a suitable HUD object. Use this first script to do that. It will ask for link permission, grant it.
First you will need to create a suitable HUD object. Use this first script to do that. It will ask for link permission, grant it.
<lsl>
<lsl>
Line 18: Line 20:
   
   
float gPrimSize = .02;
float gPrimSize = .02;
string gWorkPrim;
string gWorkPrim;
integer gNewChild;
integer gNewChild;
integer gWorking = FALSE;
   
   
default
default
Line 26: Line 30:
     {
     {
         llSetText("Rez a generic cube, take it to your inventory,\n"
         llSetText("Rez a generic cube, take it to your inventory,\n"
            + "ctrl+drag it to inside this prim, then touch to begin", <1., 1., 1.>, 1.);
                  + "Ctrl+drag it to inside this prim, then touch to begin", <1., 1., 1.>, 1.);
     }
     }
   
   
     touch_start(integer num)
     touch_start(integer num)
     {
     {
        if (gWorking)
            return;
         if (llDetectedKey(0) != llGetOwner())
         if (llDetectedKey(0) != llGetOwner())
             return;
             return;
Line 38: Line 45:
         {
         {
             llSetText("No object found in object inventory. Rez a generic cube, take it to your\n"
             llSetText("No object found in object inventory. Rez a generic cube, take it to your\n"
                + "inventory, ctrl+drag it to inside this prim, then touch to try again.", <1., 1., 0.>, 1.);
                      + "inventory, Ctrl+drag it to inside this prim, then touch to try again.", <1., 1., 0.>, 1.);
             return;
             return;
         }
         }
Line 46: Line 53:
     run_time_permissions(integer perm)
     run_time_permissions(integer perm)
     {
     {
        if (gWorking)
            return;
         if (perm & PERMISSION_CHANGE_LINKS)
         if (perm & PERMISSION_CHANGE_LINKS)
         {
         {
            gWorking = TRUE;
             llSetText("Working...", <1., 1., 0.>, 1.);
             llSetText("Working...", <1., 1., 0.>, 1.);
             llSetLinkPrimitiveParamsFast(LINK_THIS, [
             llSetLinkPrimitiveParamsFast(LINK_THIS, [
Line 59: Line 70:
                     ALL_SIDES,
                     ALL_SIDES,
                     TEXTURE_BLANK,
                     TEXTURE_BLANK,
                     <1., 1., 0.>,
                     <1., 1., 0.>,   // repeats
                     ZERO_VECTOR,
                     ZERO_VECTOR,   // offsets
                     .5,
                     0.,             // rotation in rads
                 PRIM_COLOR,
                 PRIM_COLOR,
                     ALL_SIDES,
                     ALL_SIDES,
                     <1., 1., 0.>,
                     <1., 1., 0.>,   // color
                     1.,
                     1.,             // alpha
                 PRIM_FULLBRIGHT,
                 PRIM_FULLBRIGHT,
                     ALL_SIDES,
                     ALL_SIDES,
Line 81: Line 92:
             PRIM_TYPE,
             PRIM_TYPE,
                 PRIM_TYPE_SPHERE,
                 PRIM_TYPE_SPHERE,
                     PRIM_HOLE_DEFAULT,  //hole shape
                     PRIM_HOLE_DEFAULT,  // hole shape
                     <0., 1., 0.>,      // cut
                     <0., 1., 0.>,      // cut
                     0.,                //hollow
                     0.,                // hollow
                     <0., 0., 0.>,      // twist
                     <0., 0., 0.>,      // twist
                     <0., 1., 0.>,      // dimple
                     <0., 1., 0.>,      // dimple
Line 95: Line 106:
                 ALL_SIDES,
                 ALL_SIDES,
                 TEXTURE_BLANK,
                 TEXTURE_BLANK,
                 <1., 1., 0.>,
                 <1., 1., 0.>,   // repeats
                 ZERO_VECTOR,
                 ZERO_VECTOR,   // offsets
                 0.,
                 0.,             // rotation in rads
             PRIM_COLOR,
             PRIM_COLOR,
                 ALL_SIDES,
                 ALL_SIDES,
                 <1., 1., 0.>,
                 <1., 1., 0.>,   // color
                 .5,
                 .5,             // alpha
             PRIM_FULLBRIGHT,
             PRIM_FULLBRIGHT,
                 ALL_SIDES,
                 ALL_SIDES,
Line 124: Line 135:
</lsl>
</lsl>


 
==radar script==
When you have created the object, put this script in its root prim.
When you have created the object, put this script in its root prim.


<lsl>
<lsl>
// HUD dots radar
// HUD dots radar
// Derived from Babbage Linden's SlateIt, http://wiki.secondlife.com/wiki/SLateIt
// Available under the Creative Commons Attribution-ShareAlike 2.5 license
// http://creativecommons.org/licenses/by-sa/2.5/
// This is a simple example of drawing a dot on the HUD covering a point
// in the region.
//
//
// Based by Cerise Sorbet on Babbage Linden's SLateIt, with the HUD drawing part
// Derived from Babbage Linden's SlateIt,
// extracted to make it easier to follow and reuse.
// http://wiki.secondlife.com/wiki/SLateIt
// Available under the Creative Commons Attribution-ShareAlike 2.5
// license, http://creativecommons.org/licenses/by-sa/2.5/
//
// Based by Cerise Sorbet on Babbage Linden's SLateIt, with the HUD
// drawing part extracted to make it easier to follow and reuse.
//
//
// This little example does only simple things for demonstration. The HUD has 16
// This little example does only simple things for demonstration. The
// pointer prims that it moves over avatars in radar range. Only avatars within the
// HUD has 16 pointer prims that it moves over avatars in radar
// HUD square get dots, the rest are skipped. The floating text will be a distraction
// range. Only avatars within the HUD square get dots, the rest are
// in a real application, but it helps to see how things work.
// skipped. The floating text will be a distraction in a real
 
// application, but it helps to see how things work.
 
vector gHomePosition = <0., 0., -1.1>; // puts the root prim off screen
vector gHomePosition = <0., 0., -1.1>; // puts the root prim off screen
// vector gHomePosition = <0., 0., -0.2>; // puts the root prim on the screen for debugging
// vector gHomePosition = <0., 0., -0.2>; // puts the root prim on the screen for debugging
 
float gPrimSize = .02; // make this match the build script. it is only for tidiness
float gPrimSize = .02; // make this match the build script. it is only for tidiness
 
integer gHadSensor;
integer gHadSensor;
 
 
// Here is the function that does the interesting part.
// Here is the function that does the interesting part.
 
//
// convert region coordinates to a spot on the center or center2 HUD position.
// convert region coordinates to a spot on the center or center2 HUD
// returns ZERO_VECTOR if the coordinates are off screen
// position.  returns ZERO_VECTOR if the coordinates are off screen
//
//
//
// Derived from Babbage Linden's SlateIt, http://wiki.secondlife.com/wiki/SLateIt
// Derived from Babbage Linden's SlateIt,
// Available under the Creative Commons Attribution-ShareAlike 2.5 license
// http://wiki.secondlife.com/wiki/SLateIt
// http://creativecommons.org/licenses/by-sa/2.5/
// Available under the Creative Commons Attribution-ShareAlike 2.5
 
// license, http://creativecommons.org/licenses/by-sa/2.5/
// precalculated stuff for perspective, FOV is 60 degrees,
// precalculated stuff for perspective, FOV is 60 degrees,
// gFOV is 1./ llTan(((60. * DEG_TO_RAD) / 2.))
// gFOV is 1./ llTan(((degrees * DEG_TO_RAD) / 2.))
//
// 60 degrees works with default "zoom" setting (Ctrl+9). The dots will still
// 60 degrees works with default "zoom" setting (Ctrl+9). The dots will still
// line up correctly if you use mousewheel zoom or alt-zoom.
// line up correctly if you use mousewheel zoom or alt-zoom.
//
// The number 60 comes from the LL viewer source, DEFAULT_FIELD_OF_VIEW in llmath/llcamera.h
// Search for LLZoomer in newview/llviewermenu.cpp to learn how Ctrl+8 and Ctrl+0 work.
float gFOV = 1.7320508075688774;
float gFOV = 1.7320508075688774;
 
vector Region2HUD(vector objectPos, vector cameraPos, rotation cameraRot)
vector Region2HUD(vector objectPos, vector cameraPos, rotation cameraRot)
{
{
//    vector cameraPos = llGetCameraPos();
//    vector cameraPos = llGetCameraPos();
//    rotation cameraRot = llGetCameraRot();
//    rotation cameraRot = llGetCameraRot();
 
     // Translate object in to camera space.
     // Translate object in to camera space.
     objectPos = (objectPos - cameraPos)
     objectPos = (objectPos - cameraPos)
         // Rotate object in camera space.
         // Rotate object in camera space.
         * (ZERO_ROTATION / cameraRot);
         * (ZERO_ROTATION / cameraRot);
 
     // Switch axes from Z = up to RHS (X = left, Y = up, Z = forward)
     // Switch axes from Z = up to RHS (X = left, Y = up, Z = forward)
     objectPos = <-objectPos.y, objectPos.z, objectPos.x>;
     objectPos = <-objectPos.y, objectPos.z, objectPos.x>;
 
     // Apply perspective distortion and clip object to HUD
     // Apply perspective distortion and clip object to HUD
     float xHUD = (objectPos.x * gFOV) / objectPos.z;
     float xHUD = (objectPos.x * gFOV) / objectPos.z;
     if (xHUD > -1. && xHUD < 1.) {
     if (xHUD > -1. && xHUD < 1.)
    {
         float yHUD = (objectPos.y * gFOV) / objectPos.z;
         float yHUD = (objectPos.y * gFOV) / objectPos.z;
         if (yHUD > -1. && yHUD < 1.) {
         if (yHUD > -1. && yHUD < 1.)
        {
             // Set front clipping plane to 1m and back clipping plane to infinity.
             // Set front clipping plane to 1m and back clipping plane to infinity.
             float zHUD = (objectPos.z - 2) / objectPos.z;
             float zHUD = (objectPos.z - 2) / objectPos.z;
Line 193: Line 211:
         }
         }
     }
     }
 
     return ZERO_VECTOR;
     return ZERO_VECTOR;
}
}
 
 
// Move uninteresting prims out of the way behind the root
// Move uninteresting prims out of the way behind the root
ResetChildPositions(integer startPrim)
ResetChildPositions(integer startPrim)
{
{
     integer i = llGetNumberOfPrims();
     integer i = llGetNumberOfPrims();
     if (i > 1) {
     if (i > 1) // so it does not run away when unlinked
    {
         for (; i >= startPrim; i--)
         for (; i >= startPrim; i--)
             llSetLinkPrimitiveParamsFast(i, [
             llSetLinkPrimitiveParamsFast(i, [
Line 214: Line 233:
     }
     }
}
}
 
 
Setup()
Setup()
{
{
     ResetChildPositions(2);
     ResetChildPositions(2);
 
     // make sure the object is attached to one of the center HUD points
     // make sure the object is attached to one of the center HUD points.
    // The script could run in other places but the dot positions won't make sense.
     integer attachPoint = llGetAttached();
     integer attachPoint = llGetAttached();
     if (attachPoint != ATTACH_HUD_CENTER_1 && attachPoint != ATTACH_HUD_CENTER_2)
     if (attachPoint != ATTACH_HUD_CENTER_1 && attachPoint != ATTACH_HUD_CENTER_2)
     {
     {
         llOwnerSay("Attach me to the Center or Center 2 HUD position.");
         llOwnerSay("Attach me to the Center or Center 2 HUD position.");
         // some floating text to make the object easier to find
         // some floating text to make the object easier to find
         llSetText ("HUD dots radar\n|\n|\nV", <1.,1.,0.>, 1.);
         llSetText ("HUD dots radar\n|\n|\nV", <1.,1.,0.>, 1.);
         return;
         return;
     }
     }
 
     // put the root where it belongs
     // put the root where it belongs
     llSetRot(ZERO_ROTATION);
     llSetRot(ZERO_ROTATION);
     llSetPos(gHomePosition);
     llSetPos(gHomePosition);
   
     llSetText("", ZERO_VECTOR, 0.);
     llSetText("", ZERO_VECTOR, 0.);
 
     llRequestPermissions(llGetOwner(), PERMISSION_TRACK_CAMERA|PERMISSION_TAKE_CONTROLS);
     llRequestPermissions(llGetOwner(), PERMISSION_TRACK_CAMERA|PERMISSION_TAKE_CONTROLS);
}
}
 
 
default
default
{
{
Line 246: Line 267:
         Setup();
         Setup();
     }
     }
 
     attach(key id)
     attach(key id)
     {
     {
Line 252: Line 273:
             Setup();
             Setup();
     }
     }
 
     on_rez(integer p)
     on_rez(integer p)
     {
     {
         Setup();
         Setup();
     }
     }
   
 
     run_time_permissions(integer p)
     run_time_permissions(integer p)
     {
     {
Line 264: Line 285:
         if (p & PERMISSION_TAKE_CONTROLS)
         if (p & PERMISSION_TAKE_CONTROLS)
             llTakeControls(CONTROL_LBUTTON, FALSE, TRUE);
             llTakeControls(CONTROL_LBUTTON, FALSE, TRUE);
 
         if (p & PERMISSION_TRACK_CAMERA)
         if (p & PERMISSION_TRACK_CAMERA)
             llSetTimerEvent(5.0);
             llSetTimerEvent(5.0);
Line 270: Line 291:
             llOwnerSay("Something went wrong, did not get PERMISSION_TRACK_CAMERA!");
             llOwnerSay("Something went wrong, did not get PERMISSION_TRACK_CAMERA!");
     }
     }
 
     timer()
     timer()
     {
     {
Line 276: Line 297:
         llSensor("", "", AGENT, 96., PI);
         llSensor("", "", AGENT, 96., PI);
     }
     }
 
     no_sensor()
     no_sensor()
     {
     {
Line 283: Line 304:
             ResetChildPositions(2);
             ResetChildPositions(2);
     }
     }
 
     sensor(integer num)
     sensor(integer num)
     {
     {
         vector cameraPos = llGetCameraPos();
         vector cameraPos = llGetCameraPos();
         rotation cameraRot = llGetCameraRot();
         rotation cameraRot = llGetCameraRot();
 
         integer i;
         integer i;
         for (i = 0; i < num; i++)
         for (i = 0; i < num; i++)
         {
         {
             vector childPos = Region2HUD(llDetectedPos(i), cameraPos, cameraRot);
             vector childPos = Region2HUD(llDetectedPos(i), cameraPos, cameraRot);
             if (childPos) { // only show avatars in the hud square
             if (childPos)
            { // only show avatars in the hud square
                 llSetLinkPrimitiveParamsFast(i + 2, [
                 llSetLinkPrimitiveParamsFast(i + 2, [
                     PRIM_POSITION,
                     PRIM_POSITION,
Line 303: Line 325:
                 ]);
                 ]);
             }
             }
             else { // off screen, put the dot away
             else
            { // off screen, put the dot away
                 llSetLinkPrimitiveParamsFast(i + 2, [
                 llSetLinkPrimitiveParamsFast(i + 2, [
                     PRIM_POSITION,
                     PRIM_POSITION,

Revision as of 13:01, 28 November 2010

Here is a simple example of drawing points on the HUD that cover a point in the region. It is based on Babbage's SLateIt, with the HUD drawing parts extracted for easier reuse.

build script

First you will need to create a suitable HUD object. Use this first script to do that. It will ask for link permission, grant it. <lsl> /**************************************************

   This is the script that creates the HUD dots radar object.

   1. Rez a cube and take it to your inventory.
   2. Drag the cube from your inventory back on to the ground.
   3. Go back to your inventory, this time ctrl+drag the same cube to inside the one you just rezzed.
   4. Edit the first cube, create a new script, paste in this code, and save.
   5. Touch to create the object.
   6. Edit the radar object again, create a new script, paste in the second script (the actual radar), and save.
   7. Take the result to your inventory, it should be ready.

   To use, attach it to your HUD on the Center or Center 2 point.
**************************************************/

float gPrimSize = .02;

string gWorkPrim; integer gNewChild; integer gWorking = FALSE;

default {

   state_entry()
   {
       llSetText("Rez a generic cube, take it to your inventory,\n"
                 + "Ctrl+drag it to inside this prim, then touch to begin", <1., 1., 1.>, 1.);
   }

   touch_start(integer num)
   {
       if (gWorking)
           return;
       if (llDetectedKey(0) != llGetOwner())
           return;

       gWorkPrim = llGetInventoryName(INVENTORY_OBJECT, 0);
       if (gWorkPrim == "")
       {
           llSetText("No object found in object inventory. Rez a generic cube, take it to your\n"
                     + "inventory, Ctrl+drag it to inside this prim, then touch to try again.", <1., 1., 0.>, 1.);
           return;
       }
       llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
   }

   run_time_permissions(integer perm)
   {
       if (gWorking)
           return;
       if (perm & PERMISSION_CHANGE_LINKS)
       {
           gWorking = TRUE;
           llSetText("Working...", <1., 1., 0.>, 1.);
           llSetLinkPrimitiveParamsFast(LINK_THIS, [
               PRIM_NAME,
                   "HUD dots radar",
               PRIM_ROTATION,
                   ZERO_ROTATION,
               PRIM_SIZE,
                   <gPrimSize, gPrimSize, gPrimSize>,
               PRIM_TEXTURE,
                   ALL_SIDES,
                   TEXTURE_BLANK,
                   <1., 1., 0.>,   // repeats
                   ZERO_VECTOR,    // offsets
                   0.,             // rotation in rads
               PRIM_COLOR,
                   ALL_SIDES,
                   <1., 1., 0.>,   // color
                   1.,             // alpha
               PRIM_FULLBRIGHT,
                   ALL_SIDES,
                   TRUE
           ]);
           gNewChild = 0;
           llRezObject(gWorkPrim, llGetPos() + <.5, 0., 0.>, ZERO_VECTOR, ZERO_ROTATION, 0);
       }
   }

   object_rez(key id)
   {
       llCreateLink(id, LINK_ROOT);
       llSetLinkPrimitiveParamsFast(2, [
           PRIM_TYPE,
               PRIM_TYPE_SPHERE,
                   PRIM_HOLE_DEFAULT,  // hole shape
                   <0., 1., 0.>,       // cut
                   0.,                 // hollow
                   <0., 0., 0.>,       // twist
                   <0., 1., 0.>,       // dimple
           PRIM_POSITION,
               <gPrimSize * (gNewChild + 1), 0., 0.>,
           PRIM_ROTATION,
               ZERO_ROTATION,
           PRIM_SIZE,
               <gPrimSize, gPrimSize, gPrimSize>,
           PRIM_TEXTURE,
               ALL_SIDES,
               TEXTURE_BLANK,
               <1., 1., 0.>,   // repeats
               ZERO_VECTOR,    // offsets
               0.,             // rotation in rads
           PRIM_COLOR,
               ALL_SIDES,
               <1., 1., 0.>,   // color
               .5,             // alpha
           PRIM_FULLBRIGHT,
               ALL_SIDES,
               TRUE,
           PRIM_DESC,
               (string)gNewChild
       ]);

       gNewChild++;
       if (gNewChild < 16)
       {
           llRezObject(gWorkPrim, llGetPos() + <.5, 0., 0.>, ZERO_VECTOR, ZERO_ROTATION, 0);
       }
       else
       {
           llRemoveInventory(gWorkPrim);
           llSetText("Object created, you can drag in the radar script now.", <0., 1., 0.>, 1.);
           llRemoveInventory(llGetScriptName());
       }
   }

} </lsl>

radar script

When you have created the object, put this script in its root prim.

<lsl> // HUD dots radar // // Derived from Babbage Linden's SlateIt, // http://wiki.secondlife.com/wiki/SLateIt // Available under the Creative Commons Attribution-ShareAlike 2.5 // license, http://creativecommons.org/licenses/by-sa/2.5/ // // Based by Cerise Sorbet on Babbage Linden's SLateIt, with the HUD // drawing part extracted to make it easier to follow and reuse. // // This little example does only simple things for demonstration. The // HUD has 16 pointer prims that it moves over avatars in radar // range. Only avatars within the HUD square get dots, the rest are // skipped. The floating text will be a distraction in a real // application, but it helps to see how things work.


vector gHomePosition = <0., 0., -1.1>; // puts the root prim off screen // vector gHomePosition = <0., 0., -0.2>; // puts the root prim on the screen for debugging

float gPrimSize = .02; // make this match the build script. it is only for tidiness

integer gHadSensor;


// Here is the function that does the interesting part. // // convert region coordinates to a spot on the center or center2 HUD // position. returns ZERO_VECTOR if the coordinates are off screen // // // Derived from Babbage Linden's SlateIt, // http://wiki.secondlife.com/wiki/SLateIt // Available under the Creative Commons Attribution-ShareAlike 2.5 // license, http://creativecommons.org/licenses/by-sa/2.5/

// precalculated stuff for perspective, FOV is 60 degrees, // gFOV is 1./ llTan(((degrees * DEG_TO_RAD) / 2.)) // // 60 degrees works with default "zoom" setting (Ctrl+9). The dots will still // line up correctly if you use mousewheel zoom or alt-zoom. // // The number 60 comes from the LL viewer source, DEFAULT_FIELD_OF_VIEW in llmath/llcamera.h // Search for LLZoomer in newview/llviewermenu.cpp to learn how Ctrl+8 and Ctrl+0 work. float gFOV = 1.7320508075688774;

vector Region2HUD(vector objectPos, vector cameraPos, rotation cameraRot) { // vector cameraPos = llGetCameraPos(); // rotation cameraRot = llGetCameraRot();

   // Translate object in to camera space.
   objectPos = (objectPos - cameraPos)
       // Rotate object in camera space.
       * (ZERO_ROTATION / cameraRot);

   // Switch axes from Z = up to RHS (X = left, Y = up, Z = forward)
   objectPos = <-objectPos.y, objectPos.z, objectPos.x>;

   // Apply perspective distortion and clip object to HUD
   float xHUD = (objectPos.x * gFOV) / objectPos.z;
   if (xHUD > -1. && xHUD < 1.)
   {
       float yHUD = (objectPos.y * gFOV) / objectPos.z;
       if (yHUD > -1. && yHUD < 1.)
       {
           // Set front clipping plane to 1m and back clipping plane to infinity.
           float zHUD = (objectPos.z - 2) / objectPos.z;
           if (zHUD > -1. && zHUD < 1.)
               return <0., -xHUD / 2., yHUD / 2.>;
       }
   }

   return ZERO_VECTOR;

}


// Move uninteresting prims out of the way behind the root ResetChildPositions(integer startPrim) {

   integer i = llGetNumberOfPrims();
   if (i > 1) // so it does not run away when unlinked
   {
       for (; i >= startPrim; i--)
           llSetLinkPrimitiveParamsFast(i, [
               PRIM_POSITION,
                   <gPrimSize * i, 0., 0.>,
               PRIM_TEXT,
                   "",
                   ZERO_VECTOR,
                   0.
           ]);
   }

}


Setup() {

   ResetChildPositions(2);

   // make sure the object is attached to one of the center HUD points.
   // The script could run in other places but the dot positions won't make sense.
   integer attachPoint = llGetAttached();
   if (attachPoint != ATTACH_HUD_CENTER_1 && attachPoint != ATTACH_HUD_CENTER_2)
   {
       llOwnerSay("Attach me to the Center or Center 2 HUD position.");
       // some floating text to make the object easier to find
       llSetText ("HUD dots radar\n|\n|\nV", <1.,1.,0.>, 1.);
       return;
   }

   // put the root where it belongs
   llSetRot(ZERO_ROTATION);
   llSetPos(gHomePosition);

   llSetText("", ZERO_VECTOR, 0.);

   llRequestPermissions(llGetOwner(), PERMISSION_TRACK_CAMERA|PERMISSION_TAKE_CONTROLS);

}


default {

   state_entry()
   {
       Setup();
   }

   attach(key id)
   {
       if (id)
           Setup();
   }

   on_rez(integer p)
   {
       Setup();
   }


   run_time_permissions(integer p)
   {
       // keep-running hack
       if (p & PERMISSION_TAKE_CONTROLS)
           llTakeControls(CONTROL_LBUTTON, FALSE, TRUE);

       if (p & PERMISSION_TRACK_CAMERA)
           llSetTimerEvent(5.0);
       else
           llOwnerSay("Something went wrong, did not get PERMISSION_TRACK_CAMERA!");
   }

   timer()
   {
       // using llSensor on a timer because llSensorRepeat is quirky
       llSensor("", "", AGENT, 96., PI);
   }

   no_sensor()
   {
       // clean up if there were visible dots before
       if (gHadSensor)
           ResetChildPositions(2);
   }

   sensor(integer num)
   {
       vector cameraPos = llGetCameraPos();
       rotation cameraRot = llGetCameraRot();

       integer i;
       for (i = 0; i < num; i++)
       {
           vector childPos = Region2HUD(llDetectedPos(i), cameraPos, cameraRot);
           if (childPos)
           { // only show avatars in the hud square
               llSetLinkPrimitiveParamsFast(i + 2, [
                   PRIM_POSITION,
                       childPos - llGetLocalPos(),
                   PRIM_TEXT,
                       llDetectedName(i),
                       <1.,1.,1.>,
                       1.
               ]);
           }
           else
           { // off screen, put the dot away
               llSetLinkPrimitiveParamsFast(i + 2, [
                   PRIM_POSITION,
                       <gPrimSize * i + 2, 0., 0.>,
                   PRIM_TEXT,
                       "",
                       ZERO_VECTOR,
                       0.
               ]);
           }
       }
       ResetChildPositions(i + 2);
   }

} </lsl>