Description
When a user touches an object that was imported from a GLTF mesh, the ObjectGrab it sends includes inverted-Y UVCoord and STCoord values. Rather than the expected 'V' or 'T' range of [0,1], the viewer instead sends in a range of [-1,0]. This invalid data is surfaced when an LSL script calls llDetectedTouchST / llDetectedTouchUV to resolve the touch position on the prim face.
Speaking with @cosmic-linden about this issue, she believes that the UV-flipping of GLTF meshes at import time might be intentional to have them render correctly (which they do). It seems that we also need to flip the data in ObjectGrab so that it aligns with expectations.
Steps to reproduce
- Rez a default box and put this LSL script in it:
default
{
state_entry()
{
// this texture illustrates the object's UV mapping
llSetTexture("d06a0ebf-839d-dc2f-ddca-9760ec589429", ALL_SIDES);
}
touch_start(integer total_number)
{
integer face = llDetectedTouchFace(0);
vector touchUV = llDetectedTouchUV(0);
vector touchST = llDetectedTouchST(0);
if (face == TOUCH_INVALID_FACE)
llWhisper(PUBLIC_CHANNEL, "Sorry, your viewer doesn't support touched faces.");
else if (touchUV == TOUCH_INVALID_TEXCOORD)
llWhisper(PUBLIC_CHANNEL, "Sorry, the touch position upon the texture could not be determined.");
else
{
llSay(PUBLIC_CHANNEL, "llDetectedTouchUV(" + (string)touchUV + ")"
+ "\ntouchUV.x = " + (string)touchUV.x
+ "\ntouchUV.y = " + (string)touchUV.y);
llSay(PUBLIC_CHANNEL, "llDetectedTouchST(" + (string)touchST + ")"
+ "\ntouchST.x = " + (string)touchST.x
+ "\ntouchST.y = " + (string)touchST.y);
}
}
}
- Touch various parts of the box, and observe that the touch UV and ST values are in the [0,1] range, with the lower-left corner of the texture mapping to 0,0 and upper-right corner mapped to 1,1.
- Import a basic GLTF mesh. I used
Box With Spaces.gltf from https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/Box%20With%20Spaces/glTF - it's a basic box with UV mapping
- Rez
Box With Spaces in-world, and place the same LSL script in it
- Touch
Box With Spaces a few times and note the range of output.
Expected results:
- The range of touch UV and ST values of the GLTF-imported mesh in (5) should match what a basic prim box returned in (2) for the same rendered part of the texture.
- For this particular GLTF model, when imported to Blender, one can see that the lower-left corner is at UV 0,0, and that the upper-right corner is at UV 1,1 - the same as a regular box prim in SL
Actual results:
The range of touch UV and ST values in the GLTF-imported mesh is a bit different:
- The horizontal 'U' and 'S' values match the basic prim cube (range [0,1], with left side being 0 and right side being 1)
- The vertical 'V' and 'T' values in the [-1,0] range - the bottom edge returns -1.0, while the top edge returns 0.0. Basically, there's a 1.0 offset in values.
- When viewing the viewer<>simulator conversation in Hippolyzer, I can see that the invalid UV data is coming from the
ObjectGrab message sent by the viewer:
OUT ObjectGrab [ZEROCODED]
# ID: 3560
[AgentData]
AgentID = [[AGENT_ID]]
SessionID = [[SESSION_ID]]
[ObjectData]
LocalID = 908656
GrabOffset = <0.0, 0.0, 0.0>
[SurfaceInfo]
UVCoord = <0.24838119745254517, -0.736335039138794, 0.0>
STCoord = <0.24838119745254517, -0.7363349795341492, 0.0>
FaceIndex = 0
Position = <67.54548645019531, 46.106849670410156, 23.999998092651367>
Normal = <-1.52587890625e-05, -1.52587890625e-05, 1.0>
Binormal = <0.0, 1.0, 1.52587890625e-05>
this touch UV data is passed as-is by the test script:
Box With Spaces: llDetectedTouchUV(<0.24838, -0.73634, 0.00000>)
Box With Spaces: llDetectedTouchST(<0.24838, -0.73634, 0.00000>)
Environment
Second Life Release 7.2.3.19375695301 (64bit)
Release Notes
You are at 67.5, 42.3, 23.0 in vivox1 located at simhost-0462d668818ec3f3b.aditi
SLURL: secondlife://Aditi/secondlife/vivox1/68/42/23
(global coordinates 259908.0, 245546.0, 23.0)
Second Life Server 2025-11-24.19651578074
Release Notes
CPU: Apple M1 Pro (2400 MHz)
Memory: 16384 MB
OS Version: macOS 26.2.0 Darwin 25.2.0 Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:40 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6000 arm64
Graphics Card Vendor: Apple
Graphics Card: Apple M1 Pro
OpenGL Version: 4.1 Metal - 90.5
Window size: 1105x697
Font Size Adjustment: 96pt
UI Scaling: 0.75
Draw distance: 96m
Bandwidth: 10000kbit/s
LOD factor: 1.125
Render quality: 1
Texture memory: 12124MB
Disk cache: Max size 2150.4 MB (99.9% used)
HiDPI display mode:
J2C Decoder Version: KDU v8.4.1
Audio Driver Version: OpenAL, version 1.1 ALSOFT 1.24.2 / OpenAL Community / OpenAL Soft: OpenAL Soft
Dullahan: 1.24.0.202510081737
CEF: 139.0.40+g465474a+chromium-139.0.7258.139
Chromium: 139.0.7258.139
LibVLC Version: 3.0.21
Voice Server Version: Vivox 4.10.0000.32327.5fc3fe7c.5942f08
Packets Lost: 0/8443 (0.0%)
January 15 2026 14:13:56
Original Canny Report
According to the documentation, llDetectedTouchST and llDetectedTouchUV use the bottom-left corner of the surface as the origin (0,0). This behavior is correct when interacting with meshes uploaded using COLLADA.
However, when the same geometry is uploaded as a GLTF/GLB mesh, the origin appears to be the top-left corner instead. As a result, the returned Y coordinate is inverted, producing negative or flipped values compared to the expected result.
This issue is demonstrated in the attached video, which shows a single plane mesh containing a script that outputs llDetectedTouchST. The Y value becomes negative when touching the surface. The same behavior occurs with llDetectedTouchUV:
https://gyazo.com/839b47c39612dd6d600eb54a083cbc50
Impact
This is a significant problem because it causes these functions to behave differently depending on the mesh upload format. Scripts that rely on consistent UV/ST coordinates cannot work reliably across COLLADA and GLTF meshes.
Steps to Reproduce
-
Create a simple mesh in a 3D application (for example, a single plane or a cube).
-
Export the mesh as GLTF or GLB.
-
Upload the mesh to Second Life.
-
Add a script that outputs llDetectedTouchST(0) and/or llDetectedTouchUV(0) to local chat on touch.
-
Touch the mesh and observe that the Y coordinate is inverted compared to a COLLADA-uploaded mesh.
https://secondlife.canny.io/admin/board/bug-reports/p/gltf-meshes-return-inverted-y-in-lldetectedtouchst-lldetectedtouchuv
This repo is using Opire - what does it mean? π
π΅ Everyone can add rewards for this issue commenting /reward 100 (replace 100 with the amount).
π΅οΈββοΈ If someone starts working on this issue to earn the rewards, they can comment /try to let everyone know!
π And when they open the PR, they can comment /claim #5284 either in the PR description or in a PR's comment.
πͺ Also, everyone can tip any user commenting /tip 20 @canny[bot] (replace 20 with the amount, and @canny[bot] with the user to tip).
π If you want to learn more, check out our documentation.
Description
When a user touches an object that was imported from a GLTF mesh, the
ObjectGrabit sends includes inverted-YUVCoordandSTCoordvalues. Rather than the expected 'V' or 'T' range of [0,1], the viewer instead sends in a range of [-1,0]. This invalid data is surfaced when an LSL script calls llDetectedTouchST / llDetectedTouchUV to resolve the touch position on the prim face.Speaking with @cosmic-linden about this issue, she believes that the UV-flipping of GLTF meshes at import time might be intentional to have them render correctly (which they do). It seems that we also need to flip the data in
ObjectGrabso that it aligns with expectations.Steps to reproduce
Box With Spaces.gltffrom https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/Box%20With%20Spaces/glTF - it's a basic box with UV mappingBox With Spacesin-world, and place the same LSL script in itBox With Spacesa few times and note the range of output.Expected results:
Actual results:
The range of touch UV and ST values in the GLTF-imported mesh is a bit different:
ObjectGrabmessage sent by the viewer:this touch UV data is passed as-is by the test script:
Environment
Original Canny Report
According to the documentation, llDetectedTouchST and llDetectedTouchUV use the bottom-left corner of the surface as the origin (0,0). This behavior is correct when interacting with meshes uploaded using COLLADA.
However, when the same geometry is uploaded as a GLTF/GLB mesh, the origin appears to be the top-left corner instead. As a result, the returned Y coordinate is inverted, producing negative or flipped values compared to the expected result.
This issue is demonstrated in the attached video, which shows a single plane mesh containing a script that outputs llDetectedTouchST. The Y value becomes negative when touching the surface. The same behavior occurs with llDetectedTouchUV:
https://gyazo.com/839b47c39612dd6d600eb54a083cbc50
Impact
This is a significant problem because it causes these functions to behave differently depending on the mesh upload format. Scripts that rely on consistent UV/ST coordinates cannot work reliably across COLLADA and GLTF meshes.
Steps to Reproduce
Create a simple mesh in a 3D application (for example, a single plane or a cube).
Export the mesh as GLTF or GLB.
Upload the mesh to Second Life.
Add a script that outputs llDetectedTouchST(0) and/or llDetectedTouchUV(0) to local chat on touch.
Touch the mesh and observe that the Y coordinate is inverted compared to a COLLADA-uploaded mesh.
https://secondlife.canny.io/admin/board/bug-reports/p/gltf-meshes-return-inverted-y-in-lldetectedtouchst-lldetectedtouchuv
This repo is using Opire - what does it mean? π
π΅ Everyone can add rewards for this issue commenting
/reward 100(replace100with the amount).π΅οΈββοΈ If someone starts working on this issue to earn the rewards, they can comment
/tryto let everyone know!π And when they open the PR, they can comment
/claim #5284either in the PR description or in a PR's comment.πͺ Also, everyone can tip any user commenting
/tip 20 @canny[bot](replace20with the amount, and@canny[bot]with the user to tip).π If you want to learn more, check out our documentation.