secondlife / jira-archive

3 stars 0 forks source link

[BUG-230335] llCastRay returns inaccurate hit information for moving child prims #8038

Open sl-service-account opened 3 years ago

sl-service-account commented 3 years ago

What just happened?

Setting up a build like an penny-arcade style shooter that moves targets around a background by setting the local position of child prims. Using llCastRay to determine whether a player has "hit" a moving child prim returns inconsistent results: Some casts are accurate, some see "through" child prims to hit the background, and some casts claim to hit child prims that are no longer in the ray's path.

Results seem to vary by region (but not by altitude or region type). In my primary lab (on a homestead) the issue is easily reproducible. On other regions, the issue never happens or happens so rarely that it wouldn't impact game play.

Attached video illustrates the issue: the coloured squares are "targets" (r for red, b for blue, g for green) while the grey background is not. You can see several points in the video where ray casts miss targets, report inaccurate target information, and report hitting targets that are no longer in the path of the ray.

What were you doing when it happened?

Attempting to debug an arcade-style game as described above.

What were you expecting to happen instead?

llCastRay should return accurate hit information at the time the ray is cast.

Other information

I have a simple working repro set up in my lab; the objects and code are available on request. I'd rather not give a SLURL in public but Lindens are welcome to stop by and check it out.

Attachments

Original Jira Fields | Field | Value | | ------------- | ------------- | | Issue | BUG-230335 | | Summary | llCastRay returns inaccurate hit information for moving child prims | | Type | Bug | | Priority | Unset | | Status | Accepted | | Resolution | Triaged | | Reporter | Lou Netizen (lou.netizen) | | Created at | 2021-03-02T23:01:21Z | | Updated at | 2021-03-08T17:00:35Z | ``` { 'Build Id': 'unset', 'Business Unit': ['Platform'], 'Date of First Response': '2021-03-04T17:59:45.251-0600', "Is there anything you'd like to add?": "I have a simple working repro set up in my lab; the objects and code are available on request. I'd rather not give a SLURL in public but Lindens are welcome to stop by and check it out.", 'ReOpened Count': 0.0, 'Severity': 'Unset', 'System': 'SL Simulator', 'Target Viewer Version': 'viewer-development', 'What just happened?': 'Setting up a build like an penny-arcade style shooter that moves targets around a background by setting the local position of child prims. Using llCastRay to determine whether a player has "hit" a moving child prim returns inconsistent results: Some casts are accurate, some see "through" child prims to hit the background, and some casts claim to hit child prims that are no longer in the ray\'s path.\r\n\r\nResults seem to vary by region (but not by altitude or region type). In my primary lab (on a homestead) the issue is easily reproducible. On other regions, the issue never happens or happens so rarely that it wouldn\'t impact game play.\r\n\r\nAttached video illustrates the issue: the coloured squares are "targets" (r for red, b for blue, g for green) while the grey background is not. You can see several points in the video where ray casts miss targets, report inaccurate target information, and report hitting targets that are no longer in the path of the ray.', 'What were you doing when it happened?': 'Attempting to debug an arcade-style game as described above.', 'What were you expecting to happen instead?': 'llCastRay should return accurate hit information at the time the ray is cast.', } ```
sl-service-account commented 3 years ago

Maestro Linden commented at 2021-03-04T23:59:45Z

Hi [~lou.netizen], thanks for the report. I tested this out, and can reproduce it - though it depends on how quickly the child prims are moving.

My test setup is:

  1. Rez 2 default boxes and link them together
  2. Add this script to the root prim: {code}/*

    This script repeatedly moves a child prim, then ray casts to see if the child prim was hit. The child prim is supposed to be hit when it's in one position, and missed when it's in the other position. If there's an unexpected hit or miss, the script sleeps for a frame and tries again, keeping track how many retries/frames were required before getting the expected hit/miss.

*/

// How long to wait between each move + castray event float TestInterval = 0.9;

// Link number of child prim to hit integer ChildPrimIndex = 2;

// Where to move the child prim (relative to root) when the object should be hit by the ray cast vector ChildPrimHitPos = <0, 2, 0>;

// Where to move the child prim (relative to root) when the object should be missed by the ray cast vector ChildPrimMissPos = <0, 2, 1>;

// Ray cast start and end, relative to root - should intersect ChildPrimHitPos vector RayStart = <-1, 2, 0>; vector RayEnd = <1, 2, 0>;

integer Counter = 0;

// Count of ray casts which should have been hits, but were misses integer FailedHits = 0;

// Count of ray casts which should have been misses, but were hits integer FailedMisses = 0;

default { state_entry() { // How frequently do we move the child prim and perform a ray cast

    llOwnerSay("Starting Script");
    llSetLinkPrimitiveParamsFast(ChildPrimIndex, [PRIM_NAME, "target"]);

    while(TRUE)
    {
        llSleep(TestInterval);
        ++Counter;

        vector childPosition;
        if(Counter % 2)
        {
            childPosition = ChildPrimHitPos;
        }
        else
        {
            childPosition = ChildPrimMissPos;
        }

        llSetLinkPrimitiveParamsFast(ChildPrimIndex, [PRIM_POSITION, childPosition,
            PRIM_LINK_TARGET, LINK_ROOT, PRIM_TEXT, 
            "Test #" + (string)Counter + "\nFailed Hits: " + (string)FailedHits
            + "\nFailed Misses: " + (string)FailedMisses, <0,0,1>, 1.0]
        );
        integer movedFrame = (integer)llGetEnv("frame_number");

        // wait at least a single frame for the child prim to actually move to the new position
        llSleep(0.02);

        // This method is unreliable - the prim appears to move without any waiting at all - we need to wait at least 1 frame!
        /\*while(llVecDist(llList2Vector(llGetLinkPrimitiveParams(ChildPrimIndex, [PRIM_POS_LOCAL]), 0), childPosition) > 0.001)
        {
            llSleep(0.02);
        }\*/

        // ray cast for nonphysical objects only
        list rayResults = llCastRay(llGetPos() + RayStart, llGetPos() + RayEnd, 
            [RC_REJECT_TYPES, RC_REJECT_AGENTS \| RC_REJECT_PHYSICAL | RC_REJECT_LAND]
        );
        integer rayStatus = llList2Integer(rayResults, -1);
        integer attempts = 1;

        if(rayStatus < 0)
        {
            llOwnerSay("General raycast failure! Got code " + (string)rayStatus);
        }
        // child prim in hit position - expect a hit
        else if(Counter % 2)
        {
            // Didn't hit exactly 1 object; retry until we get the expected hit count
            while(rayStatus != 1)
            {
                llOwnerSay("Test #" + (string)Counter + "Error: expected single hit, but hit "
                    + (string)rayStatus + " objects!"
                );
                ++FailedHits;

                // wait a frame and try again
                llSleep(0.02);
                rayResults = llCastRay(llGetPos() + RayStart, llGetPos() + RayEnd, 
                    [RC_REJECT_TYPES, RC_REJECT_AGENTS \| RC_REJECT_PHYSICAL | RC_REJECT_LAND]
                );
                rayStatus = llList2Integer(rayResults, -1);
                attempts++;
            }

            if(llList2Key(rayResults, 0) != llGetLinkKey(ChildPrimIndex))
            {
                llOwnerSay("Test #" + (string)Counter + " Error: expected to hit " 
                    + (string)llGetLinkKey(ChildPrimIndex)
                    + " but hit object \"" + llKey2Name(llList2Key(rayResults, 0)) + "\" (" 
                    + (string)llList2Key(rayResults, 0) + ") instead!"
                );
                ++FailedHits;
            }
            else
            {
                integer successFrame = (integer)llGetEnv("frame_number");
                llOwnerSay("Test #" + (string)Counter + ": hit child prim, as expected, after "
                    + (string)attempts + " attempts (" + (string)(successFrame-movedFrame)
                    + " frames after move call)"
                );
            }
        }
        else
        {
            // Hit something when expecting a miss
            while(rayStatus != 0)
            {
                llOwnerSay("Test #" + (string)Counter + " Error: expected miss, but hit " 
                    + (string)rayStatus + " objects! First hit: \"" 
                    + llKey2Name(llList2Key(rayResults, 0)) + "\" (" 
                    + (string)llList2Key(rayResults, 0) + ")."
                );
                ++FailedMisses;

                // wait a frame and try again
                llSleep(0.02);
                rayResults = llCastRay(llGetPos() + RayStart, llGetPos() + RayEnd, 
                    [RC_REJECT_TYPES, RC_REJECT_AGENTS \| RC_REJECT_PHYSICAL | RC_REJECT_LAND]
                );
                rayStatus = llList2Integer(rayResults, -1);
                attempts++;
            }

            integer successFrame = (integer)llGetEnv("frame_number");
            llOwnerSay("Test #" + (string)Counter + ": missed child prim, as expected, after "
                + (string)attempts + " attempts (" + (string)(successFrame-movedFrame)
                + " frames after move call)"
            );
        }       
    }
}

}{code}

sl-service-account commented 3 years ago

Lou Netizen commented at 2021-03-08T16:59:57Z, updated at 2021-03-08T17:00:36Z

I see very similar results with your code. The behaviour does seem to vary quite a lot by region: some regions produce more-or-less constant issues, while others track much better (though not perfectly). I'm not seeing anything obvious to account for the variation—my thought was that physics time in the poorer-behaving regions was perhaps significantly higher than the better-behaving regions—but that doesn't seem to be the case. Similarly, script time doesn't seem to matter much, so long as it's "basically OK."