One prim, multiple clickable regions
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
11-21-2006 10:52
As a member of the ranks of residents who'd like to be able to identify clickable regions that activate multiple functions on a single prim, I've been working on this problem for about a week. I came up with a solution that uses only two prims. I don't have a script to post yet because it's still in the proof-of-concept phase, but there is a working demo in front of my husband's workshop in-world for anyone who is interested: TeXML + ClickMap "Webpage" DemoThe demo is pulling a TeXML file in-world via the parcel media control. There is a bit of blue "hyperlink" text in the file which I've identified as a clickable region with my ClickMap script. The ClickMap pointer, which looks like a big Windows white arrow pointer, can be CTRL+dragged onto the "link" and clicked. Then, like magic, a new "page" loads on the prim face via the media control. As I said, this is a proof-of-concept demo, nothing extravagant. A few notes: -- one prim face can have several clickable regions. Each region can execute a different action. -- the scripted action for each clickable region can be anything that LSL can do on the touch events (so you could have one prim that delivers inventory, processes payments, etc. for several different products, all of which can be viewed at the same time.) -- you don't need to use the media control to do this, you can use local textures stored in the ClickMap board's inventory. We opted for TeXML because we're experimenting with Kisa Naumova's rich-content tutorial ( found here). -- you can define clickable regions in a notecard and store them in the object inventory, or conceivably, send textures and region definition notecards on the fly. -- here are a couple of my blog posts about the project that give more details: - TeXML on a prim
- Two prims to rule them all
Any feedback on the demo is welcome! I'll post some script blocks as the project gels a bit more.
|
|
Jesse Barnett
500,000 scoville units
Join date: 21 May 2006
Posts: 4,160
|
11-21-2006 15:47
It sounds like you are using llDetectedGrab(). Deja vu. A freind and I were discussing using it and llGetLinkNumber to make more efficient HUDS that use less prims and scripts.
_____________________
I (who is a she not a he) reserve the right to exercise selective comprehension of the OP's question at anytime. From: someone I am still around, just no longer here. See you across the aisle. Hope LL burns in hell for archiving this forum
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
llDetectedGrab
11-21-2006 15:55
Yep, I'm using llDetectedGrab and adding it to the value of the pointer's position in order to determine where it is relative to the board. This is what allows you to define clickable regions and to determine if you've landed in one or not.
The downside is the delay in llSetPos. Makes the pointer movement a bit sluggish and floaty. But hey, it's functional.
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
11-21-2006 16:16
From: Junie Ginsburg The downside is the delay in llSetPos. Makes the pointer movement a bit sluggish and floaty. Mhmm i suppose you could make it smoother utilizing the nonphys movement script approach -- use a face of prim which is normally not visible to user to transmit movement offsets encoded in RGB values, and have 2(+) separate scripts running in tight loop and translating that data into location change?
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
movement offsets
11-21-2006 16:24
From: Joannah Cramer ...use a face of prim which is normally not visible to user to transmit movement offsets encoded in RGB values, and have 2(+) separate scripts running in tight loop and translating that data into location change? Cool, I've never come across this method, having done very little with prim movement before. Do you know of any public example scripts that use this kind of movement? It sounds like it would really help make the pointing more precise. Thanks for the suggestion!
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
11-21-2006 16:31
/54/65/85562/1.htmlthe original thing itself ^^ another option could be also using link messages to movement scripts, with something like number of script in the Int and position translated into string in well, string .. this way if you alternate between receiving scripts, that should smooth things out a bit too i figure o.o;
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
11-22-2006 10:19
Thanks for posting the URL, Joannah, looks like an interesting way to do it, and potentially a lot quicker.
Now if I can solve some rotation problems I'm running into, I'll definitely look at implementing something like this to speed up the pointer movement.
|
|
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
|
11-23-2006 00:51
I never really got satisfactory results doing this sort of thing. The llDetectGrab vector is in cords on the same axis as the region, so you have to devide out the camera angle, but if the camera isn't in the locked mode (you have moved it with the alt+(arrow keys | page (up|down))) then the camera info is bogus.
How exactly did you get around this problem?
Not to mention the camera movement. I had some limited suchess with locking the camera in position but that only worked if the camera was still in locked mode. A very annoying problem ~_~
I've been wanting to add sliders to TLWAPI but i haven't found a way to get them to work nicely. After my current project I intend to rework TLML (so it uses llSetPrimitiveParams to speed things up); which I think will be version 1.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river. - Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence. - James Nachtwey
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
11-23-2006 01:24
Hi, Strife: I've never had to compensate for camera angle. You can get this to work from any angle. At the moment I am specifying movement of a pointer against another prim all in region coordinates. Things get considerably more complicated when I add coordinates relative to a parent prim to llDetectedGrab region coordinates, so I haven't solved the problem of how to *move* a presentation once the clickable regions are defined. Here is a very basic hardcoded version of the script: vector currentPos; vector movePos;
vector region1a = <81.99642, 90.34553, 53.88977>; vector region1b = <82.46154, 90.34553, 53.78559>;
default { state_entry() { currentPos = llGetPos(); }
touch(integer num_detected) { movePos = llDetectedGrab(0); currentPos.x = currentPos.x + movePos.x; currentPos.z = currentPos.z + movePos.z; llSetPos(currentPos); //llSay(0, (string)currentPos); } touch_end(integer num_detected) { if (currentPos.x >= region1a.x && currentPos.z >= region1b.z) { if (currentPos.x <= region1b.x && currentPos.z <= region1a.z) { state executeClick; } else state noClick; } else state noClick; } }
state executeClick { state_entry() { } touch_start(integer num_detected) { llSay(0, "I'm in a defined region."); state default; } }
state noClick { touch_start(integer num_detected) { llSay(0, "I'm sorry, this is not a clickable region."); state default; } }
The trick to defining a region is to turn on the llSay in the touch event and then CTRL+drag the pointer prim to the upper left coord for the clickable region. Record the number. Same for the lower right coord for the clickable region. Those vectors are hardcoded in global variables in this example script. It's all hard to describe, which is why I've been working on demos. The demo that we had out for the last couple of days isn't up right now because I'm working on defining multiple clickable regions for multiple "page" textures. Will have the demo available again very soon.
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
11-23-2006 08:00
From: Junie Ginsburg At the moment I am specifying movement of a pointer against another prim all in region coordinates. Things get considerably more complicated when I add coordinates relative to a parent prim to llDetectedGrab region coordinates, so I haven't solved the problem of how to *move* a presentation once the clickable regions are defined. Hmmm since you can have the region coordinates of both pointer prim and the regions prim, wouldn't that simply be matter of calculating position of pointer relative to region prim (vector offset = pointer_prim_pos - region_prim_pos) and comparing that offset to relative coordinates of regions of the region prim..? o.O edit: if the problem is having regions defined initially in region coordinates as well from readout of pointer position, then you can use the same trick (pointer - region) to convert them to coordinates relative to the region prim... and go from there ^^;
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
Far more than you probably want to know...
11-23-2006 09:33
Thanks for the ideas, Joannah. It does get a little more complicated than that since I need to anticipate the positive/negative vector values of llDetectedGrab as they come in. Say that ClickableRegionA is defined by the sim region vectors <1,2,10> and <10,2,1>. To start, I capture the current sim region position vector of the pointer -- say <20,2,20>. As I drag the pointer, llDetectedGrab's first vector value is <0,0,0>. If you are facing due North, the values of the first coordinate will increase as you move to the right, and decrease if you move to the left. The third coordinate will increase as you move up and decrease as you move down. I capture the current sim region position of the pointer and add it to the llDetectedGrab value to understand where it is in space. (The "board" prim behind the pointer that is displaying a texture is really only for reference right now.) On touch_stop, I do a comparison to find out if the pointer is in ClickableRegionA like this: If the first coordinate of the current position is *greater* than the first coordinate of the first vector of ClickableRegionA AND the last coordinate of the current position is *lower* than the third coordinate of the second vector of ClickableRegionA (upper left corner and lower right corner), then we're in ClickableRegionA. So given the example vectors I provided above, if we landed at vector <5,2,5>, we would be inside of ClickableRegionA. If we landed at vector <15,2,15>, we would not. (Sorry so much explaining, just trying to set up how the script operates in order to provide background for the problem.) Okay, so now say I move the entire presentation 180 degrees. The vectors for ClickableRegionA are no longer accurate, and I will have to reverse my > and < operators because llDetectedGrab is now returning decrementing values as it moves right and incrementing values as it moves left instead of the other way around. To complicate matters, I've been ignoring the Y axis because I'd like my pointer to only move horizontally and vertically. If I move the entire presentation to any rotation other due North or due South after defining ClickableRegionA, everything will be on a diagonal, meaning that the Y axis will change just for horizontal and vertical movements now. Sorry if this is way too much info for the forum -- I rely on the pooled intellectual horsepower and experience here to point out what I might be doing wrong.  (Next up: solving world hunger...) 
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
11-23-2006 09:54
From: Junie Ginsburg Okay, so now say I move the entire presentation 180 degrees. The vectors for ClickableRegionA are no longer accurate, and I will have to reverse my > and < operators because llDetectedGrab is now returning decrementing values as it moves right and incrementing values as it moves left instead of the other way around. Not sure (not in position to check in world at the moment) but i think it'd be easier for you to just align local coordinates of each zone to world coordinate system, by applying the prim rotation to them ( region_coordinate * llGetRot() ) ... and only then doing comparison with offset between the region prim and the pointer. In similar manner, when you're encoding region coordinates in the first place make sure to get rid of coordinate change introduced by rotation of board prim ( vector region_corner = (pointer_pos - board_pos) / board_rot ) doing it this way you should be able to get the correct zone no matter if the board prim is rotated 180 degree, at random angle or even upside down ^^;;
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
11-24-2006 02:22
Still haven't had a chance to try out your suggestions, Joannah, but wanted to post that there are two demos running in our village for anyone who is interested in checking them out from a blackbox perspective. http://slurl.com/secondlife/namhae/70/89/52/My demo shows textures stored locally in an object. My husband, Intolerable, has a demo that loads files from a webserver. He's currently working on adding a page to his demo that allows users to update the text that is displayed on the prim via chat. Any feedback is welcome, of course. 
|
|
Intolerable Ginsburg
Registered User
Join date: 19 Jul 2006
Posts: 35
|
11-24-2006 15:16
From: Junie Ginsburg My husband, Intolerable, has a demo that loads files from a webserver. He's currently working on adding a page to his demo that allows users to update the text that is displayed on the prim via chat. Actually, on one of the pages you should, if I get to it tonight, change text on a page via chat commands, or edit text and hopefully (depending on how lazy I am and how into playing with my new Treo I am) edit various elements of the same page via a php script. Its unfortunate that my demo is tied to the media stream, but with a little effort, someone might do more than the ugly, little 'website' I've created.
|
|
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
|
11-24-2006 17:11
I see what you are doing. Yeah that all makes sense to me, I was doing this for a HUD, which is why the camera angle was important.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river. - Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence. - James Nachtwey
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
11-24-2006 23:14
From: Joannah Cramer Not sure (not in position to check in world at the moment) but i think it'd be easier for you to just align local coordinates of each zone to world coordinate system, by applying the prim rotation to them ( region_coordinate * llGetRot() ) ... and only then doing comparison with offset between the region prim and the pointer. In similar manner, when you're encoding region coordinates in the first place make sure to get rid of coordinate change introduced by rotation of board prim ( vector region_corner = (pointer_pos - board_pos) / board_rot )
doing it this way you should be able to get the correct zone no matter if the board prim is rotated 180 degree, at random angle or even upside down ^^;; Joannah, this suggestion works perfectly for adjusting the static coordinates of defined regions - thanks so much for suggesting it. I'm still at a loss, however, about how to compensate for rotation when it comes to handling the actual movement of my pointer. I want it to move horizontally and vertically *only*, relative to the board behind it. This isn't a problem when it's facing due North or due South because I can simply add or subtract llDetectedGrab values to the current position of the pointer on the x and z axes. However, if the pointer and board are at a weird rotation, then suddenly everything is on the diagonal and the y axis is involved in horizontal movement. Any ideas how I could correct for that? I'm actively experimenting, but haven't come up with anything that works 100% of the time.
|
|
Feynt Mistral
Registered User
Join date: 24 Sep 2005
Posts: 551
|
11-24-2006 23:48
As it so happens my projective geometry class went over something very similar to this problem last week. Sadly we haven't gotten to futzing with quaternions (devil spawned constructs that they are!) so I'm not quite sure how to help out there. We'll be learning about (and loathing) them in a week or two so I'll bring up the question then if you haven't solved it already.
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
11-25-2006 08:45
From: Junie Ginsburg However, if the pointer and board are at a weird rotation, then suddenly everything is on the diagonal and the y axis is involved in horizontal movement.
Any ideas how I could correct for that? I'm actively experimenting, but haven't come up with anything that works 100% of the time. Two possible ways to tackle it... one would be to simply apply board rotation to the offset vector of your pointer ( pointer_pos += offset * board_rot, or something ) ... this makes your pointer move in the local coordinate system of your board the other -- there's a few functions which allow to extract X, Y and Z vectors out of rotation quaternion ( llRot2Fwd(), llRot2Up() etc) ... if you supply them with rotation of board, in return you'll get unit-long local X, Y and Z vectors of its coordinate system. This can be used to calculate movement of pointer relative to the board: the final vector would be something like: local_X * pointer_offset.x + local_Y * pointer_offset.y + local_Z * pointer_offset.z both are far from perfect, though, because prim grab function seems to operate in coordinate system defined by angle of user's camera ... so it may lead to situation where dragging your mouse to the left moves pointer to the right if board happens to be rotated 180 degrees in relation to camera, or something similar. a more elegant solution could be on-the-fly adjustment of position of your pointer so it always 'snaps' to local YZ plane of the board (or whatever, am figuring your board 'faces' the user with its X vector) ... this should be doable with simple projection of point onto plane: http://www.euclideanspace.com/maths/geometry/elements/plane/index.htm (near the end of page, the generalized abc plane is defined by the forward vector of local coordinate system of your board, give or add half of its 'thickness' perhaps) edit: note, the matrix thing may seem rather daunting but you can leverage vectors here to do most of the work for you... depending if the system is left- or right- handed, encode each of the rows/columns into separate vector, then use your original coordinates as input... something to effect of: // pseudocode vector abc = llRot2Fwd( board_rot );
vector x = < b2+c2, -ba, -ca >; vector y = <-ab, a2+c2, -cb >; vector z = <-ac, -bc, a2+b2 >;
vector world = llGetPos(); vector projected = x * world.x + y * world.y + z * world.z; projected += board_pos; // ?we want the plane to actually pass through board centre?
... or something to this effect, been a while since i used that so the actual order can be all wrong here *..*
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
11-26-2006 13:46
Wow, this is all SO daunting, but I'm determined to make this thing work. Thanks for the pointer to the "plane-snapping" math. It's going to take some work for me to wrap my brain around it, but will try out your pseudocode in my script. To answer the question you commented into your code, I *don't* want the plane to pass through the board center (vertical). The "pointer" prim should always move along the front face of the board, it should never "sink" into the board. Hopefully this makes sense.  I appreciate all of your suggestions!
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
11-26-2006 15:54
From: Junie Ginsburg To answer the question you commented into your code, I *don't* want the plane to pass through the board center (vertical). The "pointer" prim should always move along the front face of the board, it should never "sink" into the board. Hopefully this makes sense.  Aye, perfectly understandable... i think i worded that wrong in the comment, the += board_pos part is there so the plane you're snapping pointer to actually passes through the board rather than the centre of coordinate system (i.e. point 0, 0, 0 in world) ... so it probably should stay in there but modified further by vector that's aligned with 'forward' vector of coordinate system the board is using, and as long as half-thickness of the board... using the code snippet it'd be something like: projected += board_pos + (abc * board_size.x * 0.5); also, the 'world' vector in the code snippet should probably be (pointer_pos - board_pos) rather than just position of pointer... if just because the board_pos is added in the end. I'll need to find some time in world and actually test it, it's all theoretical so far >.< I know am doing terrible work explaining it, it's just one of these situations where you have a picture in mind of how the little arrows add together but good luck trying to put that in words... sorry ^^;;
|
|
Junie Ginsburg
Steampunk Merchant
Join date: 5 Aug 2006
Posts: 35
|
11-26-2006 17:08
Is your variable "projected" below the position where the pointer will be when snapped to the board? As in, after you do all of the math, would you be setting the position like llSetPos(projected)?
I've just incorporated the pseudocode into my script, and now I can't drag the pointer horizontally or vertically, it only flies away from the board on the y vector. But I haven't added in the projected += board_pos part of it yet, so maybe that's it...
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
11-26-2006 18:40
From: Junie Ginsburg Is your variable "projected" below the position where the pointer will be when snapped to the board? As in, after you do all of the math, would you be setting the position like llSetPos(projected)? Aye. I finally got around trying it, this is (very crude) code that seems to work: vector board_pos; vector plane_x; vector plane_y; vector plane_z;
default { state_entry() { llSensor( "board", "", PASSIVE, 20.0, PI ); } sensor( integer Contacts ) {
board_pos = llDetectedPos(0); rotation board_rot = llDetectedRot(0); vector abc = llRot2Fwd( board_rot ); float a2 = abc.x; a2 *= a2; float b2 = abc.y; b2 *= b2; float c2 = abc.z; c2 *= c2; float ab = abc.x * abc.y; float bc = abc.y * abc.z; float ac = abc.x * abc.z; plane_x = < b2 + c2, -1.0 * ab, -1.0 * ac >; plane_y = < -1.0 * ab, a2 + c2, -1.0 * bc >; plane_z = < -1.0 * ac, -1.0 * bc, a2 + b2 >; llSetRot( board_rot ); board_pos += abc * 0.25; // move the plane origin 25 cm 'in front' of board centre }
touch( integer Contacts ) { vector world = llGetPos() - board_pos + llDetectedGrab(0); vector projected = world.x * plane_x + world.y * plane_y + world.z * plane_z; projected += board_pos; llSetPos( projected ); } }
... it looks for object named "board" and does all the cached math, then aligns the pointer accordingly ... it only does it on startup though, so if you rotate the board after, the pointer prim script has to be reset so it looks for board again. All could be done in quite more elegant manner obviously but hey, seems to work ^^;
|
|
grumble Loudon
A Little bit a lion
Join date: 30 Nov 2005
Posts: 612
|
11-27-2006 04:54
I think you would get smoother response if you set the mouse physical and made it hover. The client can process inter-update moves and create a much easier to use mouse. Here is some math I cut from the image viewer posted earlier. It magically rotates the grab vector based on the prim's rotation by mutiplying the rotational inverse? vector GrabDir = llDetectedGrab(0); rotation primRot = llGetRot(); //Convert the drag to be in line with the prims rotation GrabDir = GrabDir * (<0,0,0,1> / primRot);
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
11-27-2006 08:43
From: grumble Loudon Here is some math I cut from the image viewer posted earlier. It magically rotates the grab vector based on the prim's rotation by mutiplying the rotational inverse? vector GrabDir = llDetectedGrab(0); rotation primRot = llGetRot(); //Convert the drag to be in line with the prims rotation GrabDir = GrabDir * (<0,0,0,1> / primRot);
I think you can use just (grabDir * primRot) instead because that'll do the same thing, rotate the grab offset by rotation of prim... the catch is, that doesn't actually do anything too useful -- what it does is, it affects the camera-based coordination system used to determine how mouse movement translates into grab vector. Meaning, if the board is rotated 90 degree then moving your mouse to the sides of screen will cause prim move closer/farther from the viewer. If the prim is rotated 180 degree then the sides become swapped -- moving mouse to left causes prim move to the right of the viewer, etc. ^^;;
|
|
grumble Loudon
A Little bit a lion
Join date: 30 Nov 2005
Posts: 612
|
11-28-2006 04:55
I don't understand the math but the "vector * (unity / vector)" does not do a divide and multiply in the normal way.
This math reverses the grab dir so that it is correct from the prims view even if the prim is rotated.
In this case however you need to use the grab dir to move the mouse based on the sim's view and then lock it's motion so that it stays on the surface of the screen.
I still thing that it would be better to use apply impulse on a physical object to keep it on the board prim and use the client's normal grab system.
to make it easier to use, modify the apply impulse code so that pushing it back into the board also moves it upwards.
I would explain more, but I am still learning as I make a vehicle that does not use the normal vehicle script.
|