llCastRay

From Second Life Wiki
Revision as of 15:53, 14 January 2016 by Kayaker Magic (talk | contribs) (caveats for the cases where the cast ray starts inside a prim and detecting the prim the script is in)
Jump to navigation Jump to search

Summary

Function: list llCastRay( vector start, vector end, list options );

Cast a ray from start to end and report collision data for intersections with objects
Returns a list of strided values with an additional integer status_code on the end. Each stride consists of two mandatory values {key uuid, vector position} and possibly some optional values {integer link_number, vector normal} see RC_DATA_FLAGS for details. The status_code if it is negative is an error code, otherwise it is the number of hits (and strides) returned.

• vector start starting location
• vector end ending location
• list options can consists of any number of option flags and their parameters.

Specification

status_code

status_code is a number tacked onto the end of the strided list to give you extra information about the ray cast. If the cast succeeded, it will be >=0 and will indicate the number of hits. If the ray cast failed (which should only happen right now if the simulator performance is running low), you'll get a negative status code. RCERR_SIM_PERF_LOW will be used as the status code if the overall physics time in the simulator is too high to perform raycasts. The idea is that you will know to try your cast again in a few frames.

status_code error codes and their meanings.
Status Code V Description
RCERR_UNKNOWN-1The raycast failed for an unspecified reason. Please submit a bug report.
RCERR_SIM_PERF_LOW-2The raycast failed because simulator performance is low. Wait a while and then try again. If possible reduce the scene complexity.
RCERR_CAST_TIME_EXCEEDED-3The raycast failed because the parcel or agent has exceeded the maximum time allowed for raycasting. This resource pool is continually replenished, so waiting a few frames and retrying is likely to succeed.
RCERR_CAST_TIME_EXCEEDED

Ray casts are throttled by the amount of time actually taken to perform the cast. Full regions are each allotted a 4ms pool, divided proportionally over parcels the same way prim limits are. Homestead and Openspace regions behave similarly, except that their pool size is limited to 1ms. Each agent is allotted 200us. All scripts in attachments and, scripts in vehicles, use the agent pool whereas all other scripts use the parcel pool. For the purpose of ray cast accounting (and script limits in general), a 'vehicle' is defined as an object which had one or more avatars seated on it when it entered the parcel. A ray cast can be performed if at least 30us of raycast time remain in the appropriate pool. If there is insufficient time remaining, RCERR_CAST_TIME_EXCEEDED is returned as the status code. The exact time used by the ray cast is measured when it is performed and that number (in microseconds) is subtracted from the pool. (The time remaining can be a negative number.) Over time, the pool is automatically replenished (at a rate of 25% of the max time per frame).

For example, if you start out with 100us and perform a 50us raycast, 50us will be remain. If you then a 70us raycast during the same frame, you will have -20us remaining. Subsequent calls to llCastRay that frame will fail with status code RCERR_CAST_TIME_EXCEEDED. At the start of the next frame, you will have 5us available (25us are restored each frame) and any attempt to call llCastRay will again fail as you need 30us to execute a raycast. One frame after that, 30us will be available and a raycast can once again be performed.

This method of throttling puts the scripter "closer to the machine". That is, you're only being charged for what you use, and more efficient raycast techniques will automatically be charged less than less efficient ones. The exact throttle values are subject to change at any time. To ensure robust results, be sure to check for RCERR_CAST_TIME_EXCEEDED and RCERR_SIM_PERF_LOW and sleep or do other work for a while before trying again.

Tips for Efficient Raycasts:

  • Keep the max number of hits returned as small as possible
  • Set as many RC_REJECT_TYPES as possible (of factors you can control, this will likely have the largest impact). For example, if you only want to know where the nearest agent is along a ray, use RC_REJECT_LAND | RC_REJECT_PHYSICAL | RC_REJECT_NONPHYSICAL
  • When possible, avoid raycasting through piles of prims and avoid raycasting against concave physics objects (anything with cut, hollow, twist, and so on, and any mesh object that has no decomposition and has physics type "prim"). Obviously this can't always be avoided, so some casts may take significantly longer than others. Plan for that with robust scripts that handle RCERR_CAST_TIME_EXCEEDED responsibly, namely by sleeping briefly after the call and waiting for a few frames to go by before trying again.

Note: SCR-199 indicates that pools have been removed from the main grid, so this return code should not appear.

options parameter

options flags and their parameters
Flag V Parameters Default Value Description
RC_REJECT_TYPES ] 0 integer filter ] [ 0 ] Mask used to ignore specific types of objects (and avatars).
RC_DATA_FLAGS ] 2 integer flags ] [ 0 ] Described in the RC_DATA_FLAGS section.
RC_MAX_HITS ] 3 integer max_hits ] [ 1 ] Maximum number of hits to return. Maximum value is 256. To avoid performance issues, keep it small.
RC_DETECT_PHANTOM ] 1 integer detect_phantom ] [ FALSE ] Set to TRUE (or nonzero) to detect phantom AND volume detect objects. It is not possible to detect only phantom objects or only volume detect objects. If set to TRUE, phantom and volume detect objects will always be detected, even if RC_REJECT_NONPHYSICAL and RC_REJECT_PHYSICAL are set in RC_REJECT_TYPES.
RC_REJECT_TYPES

filter is a bitwise-or combination of the following constants: RC_REJECT_AGENTS, RC_REJECT_PHYSICAL, RC_REJECT_NONPHYSICAL, and RC_REJECT_LAND except that if you select all four of them, a script runtime error will be generated (it makes no sense to cast a ray and reject everything!). Note that phantom and volume detect objects are never returned and that seated agents are treated like unseated agents. I.e., you either get seated and unseated agents in your results, or you use RC_REJECT_AGENTS and get neither. Using 0 as the filter value will return all hits.

RC_DATA_FLAGS

flags is a bitwise-or combination of: RC_GET_NORMAL, RC_GET_ROOT_KEY, and RC_GET_LINK_NUM. These select whether you want link numbers and hit normals in your results list. By default, you will get the UUID ('key') of the exact child prim hit. If instead you want the key of the root prim, set RC_GET_ROOT_KEY. A terrain hit will register as NULL_KEY.

Caveats

  • Depending upon the value of flags (provided via RC_DATA_FLAGS), the number and types of values in the strides will vary. See RC_DATA_FLAGS for details.
  • llGetRot will not return an avatar's exact visual rotation because the viewer doesn't update the avatar's rotation under a threshold (see VWR-1331). To get an avatar's exact looking direction while in mouselook, use llGetCameraRot instead.
  • llCastRay will not detect prims having no physics shape (PRIM_PHYSICS_SHAPE_TYPE = PRIM_PHYSICS_SHAPE_NONE).
  • llCastRay will not detect a prim if the ray starts inside the prim. This makes it safe to use the prim position as the start location.
  • llCastRay can detect the prim the script is in, if the start location is outside the prim.

Examples

integer filter;// default is 0

default
{
    state_entry()
    {
        string ownerName = llKey2Name(llGetOwner());
        llOwnerSay("Hello, " + ownerName + "!");
    }

    touch_start(integer total_number)
    {
        vector start = llGetPos();
        vector end = start - <0.0, -25.0, 0.0>;

        if ( filter > 8 )
            filter = 0;

        llOwnerSay("Filter " + (string)filter);

        list results = llCastRay(start, end, [RC_REJECT_TYPES, filter, RC_MAX_HITS, 4] );

        integer hitNum = 0;
        // Handle error conditions here by checking llList2Integer(results, -1) >= 0
        while (hitNum < llList2Integer(results, -1))
        {
            // Stride is 2 because we didn't request normals or link numbers
            key uuid = llList2Key(results, 2*hitNum);

            string name = "Land"; // if (uuid == NULL_KEY)

            if (uuid != NULL_KEY)
                name = llKey2Name(uuid);

            llOwnerSay("Hit " + name + ".");

            ++hitNum;
        }

        ++filter;
    }
}
// Fire a weapon at a target, report a hit

integer gTargetChan = -9934917;

default
{
    attach(key id)
    {
        if (id != NULL_KEY)
        { 
            llRequestPermissions(id,PERMISSION_TAKE_CONTROLS|PERMISSION_TRACK_CAMERA);
        }
    }

    run_time_permissions (integer perm)
    {
        if (perm & PERMISSION_TAKE_CONTROLS|PERMISSION_TRACK_CAMERA) 
        {
            llTakeControls(CONTROL_LBUTTON|CONTROL_ML_LBUTTON,TRUE,FALSE);
        }
    }

    control (key id, integer level, integer edge)
    {
        // User must be in mouselook to aim the weapon
        if (level & edge & CONTROL_LBUTTON)
        {
            llSay(0,"You must be in Mouselook to shoot.  Type \"CTRL + M\" or type \"Esc\" and scroll your mouse wheel forward to enter Mouselook.");
        }
        // User IS in mouselook        
        if (level & edge & CONTROL_ML_LBUTTON)
        {
            vector start = llGetCameraPos();
            // Detect only a non-physical, non-phantom object. Report its root prim's UUID.
            list results = llCastRay(start, start+<60.0,0.0,0.0>*llGetCameraRot(),[RC_REJECT_TYPES,RC_REJECT_PHYSICAL|RC_REJECT_AGENTS|RC_REJECT_LAND,RC_DETECT_PHANTOM,FALSE,RC_DATA_FLAGS,RC_GET_ROOT_KEY,RC_MAX_HITS,1]);
            llTriggerSound(llGetInventoryName(INVENTORY_SOUND,0),1.0);
            llSleep(0.03);
            key target = llList2Key(results,0);
            // Tell target that it has been hit. 
            llRegionSayTo(target,gTargetChan,"HIT");
            // Target, scripted to listen on gTargetChan, can explode, change color, fall over .....
        }
    }            
}

Notes

Use llDumpList2String to see what the output looks like when you try a new set of flags.

To quickly get the status code use llList2Integer(result, -1).

Ideas for uses:

  • Weapons - Raycasts are the traditional tool used in game development for simulating projectile weapons. They are orders of magnitude more efficient than rezzing a prim and launching it from a weapon.
  • AI Objects - Line-of-sight detection of avatars and other objects, or for navigating an environment by tracing rays about themselves. For example; casting rays directly downwards to determine the height and angle (normal) of the current floor surface, useful for non-physical object movement.
  • Intelligent Object Placement - Static objects can be placed in-scene, but adjust themselves to their environment. For example; an object rezzed too high up may adjust its height to floor-level, or a computer console placed low down may cause an avatar to kneel to use it rather than standing.
  • Environment Analysis - Can be used to determine the limitations of a surrounding area, such as determining if an object has been placed within a closed room. Not a test to be performed frequently due to quantity of rays required, but could be used by objects to switch off effects if unobserved (no-one within the room). Auto-adjusting furniture or objects to snap to walls, floors, and ceilings.

Deep Notes

History

  • Date of Release 23/09/2011
  • SCR-199 - fixed - The throttle was too low and thus rendered the function not as useful as it could be.

Signature

function list llCastRay( vector start, vector end, list options );