arnellou Posted April 6, 2023 Share Posted April 6, 2023 (edited) Hello everyone, I have a question about my car door script. The door is not opening properly as it rotates 90 degrees and its pivot point or hinge is in the middle. I tried adjusting the x-axis of the vector open_position variable, but it seems to adjust its position first and then perform the rotation.. Please see the video here. Could someone please suggest a solution to fix the pivot/hinge and other issues? Any advice or tips would be greatly appreciated. Thank you in advance for your help. // config vector close_position = <-0.570, 0.190, 1.865>; rotation close_rotation = <0.0, 0.90, 0.0, -1.0>; vector open_position = <-0.770, 0.190, 1.865>; rotation open_rotation = <0.00, 0.00, 0.00, 0.00>; key close_sound = "876d3492-b8b8-7745-f5ae-dc5176ec6105"; key open_sound = "9a076511-9aa8-f50d-e34c-1396a07a66a9"; key lock_sound = "37e8d62e-ba8a-3faf-49cc-7cb9267686da"; key unlock_sound = "37e8d62e-ba8a-3faf-49cc-7cb9267686da"; key chime_sound = "b2aa9559-4e32-8321-09f8-86572fd5bc00"; float close_volume = 1.0; float open_volume = 1.0; float lock_volume = 0.5; float unlock_volume = 0.5; float chime_volume = 0.5; integer channel = 0; integer auto_close = 0; float movement_time = 1.0; integer preload_sounds = FALSE; integer sit_check = FALSE; list whitelist = [""]; string link_name = ""; vector link_close_position = <0.00000, 0.00000, 0.00000>; rotation link_close_rotation = <0.00000, 0.00000, 0.00000, 0.00000>; vector link_open_position = <0.00000, 0.00000, 0.00000>; rotation link_open_rotation = <0.00000, 0.00000, 0.00000, 0.00000>; // integer close_open; integer handle; integer link; vector link_time_position; rotation link_time_rotation; integer locked; integer sitting; float time; vector time_position; rotation time_rotation; // http://wiki.secondlife.com/wiki/Interpolation/Linear/Vector vector vLin(vector x, vector y, float t){ return x*(1-t) + y*t; } // http://wiki.secondlife.com/wiki/Slerp rotation slerp( rotation a, rotation b, float t ) { return llAxisAngle2Rot( llRot2Axis(b /= a), t * llRot2Angle(b)) * a; } close_or_open() { if(llGetLinkNumber() < 2) return; if(llGetLocalPos() != close_position) { llStopSound(); llResetTime(); while(llGetTime() < movement_time) { time = llGetTime() / movement_time; time_position = vLin(open_position, close_position, time); time_rotation = slerp(open_rotation, close_rotation, time); if(link != 0) { link_time_position = vLin(link_open_position, link_close_position, time); link_time_rotation = slerp(link_open_rotation, link_close_rotation, time); } llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POS_LOCAL, time_position, PRIM_ROT_LOCAL, time_rotation]); if(link != 0) llSetLinkPrimitiveParamsFast(link, [PRIM_POS_LOCAL, link_time_position, PRIM_ROT_LOCAL, link_time_rotation]); } llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POS_LOCAL, close_position, PRIM_ROT_LOCAL, close_rotation]); if(link != 0) llSetLinkPrimitiveParamsFast(link, [PRIM_POS_LOCAL, link_close_position, PRIM_ROT_LOCAL, link_close_rotation]); if(close_sound != "") llTriggerSound(close_sound, close_volume); } else { if(open_sound != "") llTriggerSound(open_sound, open_volume); llResetTime(); while(llGetTime() < movement_time) { time = llGetTime() / movement_time; time_position = vLin(close_position, open_position, time); time_rotation = slerp(close_rotation, open_rotation, time); if(link != 0) { link_time_position = vLin(link_close_position, link_open_position, time); link_time_rotation = slerp(link_close_rotation, link_open_rotation, time); } llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POS_LOCAL, time_position, PRIM_ROT_LOCAL, time_rotation]); if(link != 0) llSetLinkPrimitiveParamsFast(link, [PRIM_POS_LOCAL, link_time_position, PRIM_ROT_LOCAL, link_time_rotation]); } llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POS_LOCAL, open_position, PRIM_ROT_LOCAL, open_rotation]); if(link != 0) llSetLinkPrimitiveParamsFast(link, [PRIM_POS_LOCAL, link_open_position, PRIM_ROT_LOCAL, link_open_rotation]); if(auto_close != 0) llSetTimerEvent(auto_close); if(chime_sound != "") llLoopSound(chime_sound, chime_volume); } } find_link() { integer prims = llGetNumberOfPrims(); integer index; while(prims--) { string name = llGetLinkName(index); if(name == link_name) link = index; index++; } } default { state_entry() { llSetMemoryLimit(llGetUsedMemory() + 1024); handle = llListen(channel, "", llGetOwner(), ""); find_link(); if(preload_sounds == TRUE) { if(close_sound != "") llPreloadSound(close_sound); if(open_sound != "") llPreloadSound(open_sound); if(lock_sound != "") llPreloadSound(lock_sound); if(unlock_sound != "") llPreloadSound(unlock_sound); if(chime_sound != "") llPreloadSound(chime_sound); } } on_rez(integer start_param) { llResetScript(); } touch_start(integer total_number) { key id = llDetectedKey(0); string name = llGetUsername(id); if(id == llGetOwner()) { close_or_open(); return; } if(locked == FALSE) close_or_open(); else if(locked == TRUE) { integer find = llListFindList(whitelist, [name]); if(find != -1) close_or_open(); } } timer() { llSetTimerEvent(0.0); if(llGetLocalPos() != close_position) close_or_open(); } listen(integer channel, string name, key id, string message) { if(sit_check == TRUE) if(sitting == FALSE) return; message = llToLower(message); if(message == "close") if(llGetLocalPos() != close_position) close_or_open(); if(message == "open") if(llGetLocalPos() != open_position) close_or_open(); if(message == "unlock") { locked = FALSE; llTriggerSound(unlock_sound, unlock_volume); } if(message == "lock") { locked = TRUE; llTriggerSound(lock_sound, lock_volume); } } changed(integer change) { if(change & CHANGED_LINK) { find_link(); key av = llAvatarOnLinkSitTarget(LINK_ROOT); if(av == llGetOwner()) if(av) sitting = TRUE; else sitting = FALSE; } } } Edited April 6, 2023 by arnellou added sourcecode Link to comment Share on other sites More sharing options...
Rolig Loon Posted April 6, 2023 Share Posted April 6, 2023 This is a common problem with slow-rotating door scripts. I have tried several of the standard approaches myself. The best way to beat it is to redesign the mesh for the door, frankly. If the mesh is designed with its hinge point at the visual edge of the door, you don't have to go through the business of adjusting both the local position and the local rotation at every time step. Link to comment Share on other sites More sharing options...
animats Posted April 6, 2023 Share Posted April 6, 2023 Try using LlSetPrimitiveParams instead of LlSetPrimitiveParamsFast. You have an inner loop with no delays issuing commands with no rate limit. The system doesn't like that. It tends to result in out of order or dropped updates, or your script being throttled. LlSetPrimitiveParamsFast means "don't wait for completion". Here, you want these updates to happen at a fixed rate. LlSetPrimitiveParams has a 200ms delay, which is reasonable here. Link to comment Share on other sites More sharing options...
SeanMcDonald Posted April 7, 2023 Share Posted April 7, 2023 When building with prims, a trick I've used is to create a hinge prim, link it to the door, then rotate the hinge. Link to comment Share on other sites More sharing options...
animats Posted April 8, 2023 Share Posted April 8, 2023 15 hours ago, SeanMcDonald said: When building with prims, a trick I've used is to create a hinge prim, link it to the door, then rotate the hinge. That only works for standalone prims not attached to something else. You can do that for a house, but not a car. The permanent solution is a real hierarchy, where child prims can have children of their own. See this JIRA. LL gradually seems to be coming around to that, partly because they want to go with glTF import, and glTF assumes there's a hierarchy. Rosedale has said that not having a general prim hierarchy was his biggest design mistake. There's been discussion of adding pivot points to prims, but it turns out that a full hierarchy is easier to implement, because the viewers already understand a hierarchy. 1 Link to comment Share on other sites More sharing options...
SeanMcDonald Posted April 22, 2023 Share Posted April 22, 2023 On 4/8/2023 at 12:23 AM, animats said: That only works for standalone prims not attached to something else. You can do that for a house, but not a car. Maybe I have a strange case, then. It's a pair of scripts, but it works well enough for a vehicle I built several years ago. The door script: /* * Smooth Rotating Linked Door With Hinge * * By: Lyn Mimistrobell * Version: 1.1 * License: Do whatever you like with it, just don't blame me if you break it :) */ /* * Define the rotation in degrees, using the door prim's local coordinate * system */ vector ROTATION = <122.0, 0.0, 0.0>; /* * Define the position of the virtual hinge; usually this is half the door * prim's width and thickness */ vector HINGE_POSITION = <0.0, 0.05, -1.425>; /* * Define how fast the door opens, in seconds */ float SECONDS_TO_ROTATE = 1.0; /* * Define after how much time the door should close automatically, in seconds; * set to 0.0 to disable autolmatic closing */ float AUTO_CLOSE_TIME = 10.0; /* * Define a sound that plays when the door starts to open; set to NULL_KEY * for no sound. */ key SOUND_ON_OPEN = "e5e01091-9c1f-4f8c-8486-46d560ff664f"; /* * Define a sound that plays when the door has closed; set to NULL_KEY * for no sound. */ key SOUND_ON_CLOSE = "88d13f1f-85a8-49da-99f7-6fa2781b2229"; /* * Define the volume of the opening and closing sounds */ float SOUND_VOLUME = 1.0; /* * NORMALLY, THERE IS NO NEED TO CHANGE ANYTHING BELOW THIS COMMENT. IF YOU DO * YOU RISK BREAKING IT. */ integer gClosed; // Door state: TRUE = closed, FALSE = opened rotation gRotationClosed; // Initial rotation of the door (closed) vector gPositionClosed; // Initial position of the door (closed) vector gRotationPerSecond; // The amount to rotate each second doOpenOrClose() { /* * Only perform the rotation if the door isn't root or unlinked */ integer linkNumber = llGetLinkNumber(); if (linkNumber < 2) return; if (gClosed) { /* * Store the initial rotation and position so we can return to it. * * Rotating back purely by calculations can in the longer term cause the door * to be positioned incorrectly because of precision errors * * We determine this everytime before the door is being opened in case it was * moved, assuming the door was closed whilst being manipulated. */ gPositionClosed = llGetLocalPos(); gRotationClosed = llGetLocalRot(); /* * Play the opening sound and preload the closing sound */ if (SOUND_ON_OPEN) llPlaySound(SOUND_ON_OPEN, SOUND_VOLUME); } vector hingePosition = gPositionClosed + HINGE_POSITION * gRotationClosed; /* * Reset the timer and start moving */ llResetTime(); while (llGetTime() < SECONDS_TO_ROTATE) { float time = llGetTime(); if (! gClosed) /* * Invert the timer for closing direction */ time = SECONDS_TO_ROTATE - time; rotation rotationThisStep = llEuler2Rot(gRotationPerSecond * time) * gRotationClosed; vector positionThisStep = hingePosition - HINGE_POSITION * rotationThisStep; llSetLinkPrimitiveParamsFast(linkNumber, [PRIM_ROT_LOCAL, rotationThisStep, PRIM_POS_LOCAL, positionThisStep]); } /* * Set the new state */ gClosed = !gClosed; if (gClosed) { /* * Finalize the closing movement */ llSetLinkPrimitiveParamsFast(linkNumber, [PRIM_ROT_LOCAL, gRotationClosed, PRIM_POS_LOCAL, gPositionClosed]); /* * Play the closing sound and preload the opening sound */ if (SOUND_ON_CLOSE) llPlaySound(SOUND_ON_CLOSE, SOUND_VOLUME); if (SOUND_ON_OPEN) llPreloadSound(SOUND_ON_OPEN); } else { /* * Finalize the opening movement */ rotation rotationOpened = llEuler2Rot(ROTATION * DEG_TO_RAD) * gRotationClosed; vector positionOpened = hingePosition - HINGE_POSITION * rotationOpened; llSetLinkPrimitiveParamsFast(linkNumber, [PRIM_ROT_LOCAL, rotationOpened, PRIM_POS_LOCAL, positionOpened]); /* * Preload the closing sound */ if (SOUND_ON_CLOSE) llPreloadSound(SOUND_ON_CLOSE); /* * Set a timer to automatically close */ llSetTimerEvent(AUTO_CLOSE_TIME); } } default { state_entry() { /* * Assume the door is closed when the script is reset */ gClosed = TRUE; /* * These doesn't change unless the script is changed, calculate them once */ gRotationPerSecond = (ROTATION * DEG_TO_RAD / SECONDS_TO_ROTATE); /* * Preload the opening sound */ if (SOUND_ON_OPEN) llPreloadSound(SOUND_ON_OPEN); } touch_start(integer agentCount) { doOpenOrClose(); } timer() { llSetTimerEvent(0.0); /* * Close the door if it isn't already closed */ if (! gClosed) doOpenOrClose(); } } And the hinge script: default //initially closed { touch_start(integer num_detected) { state open; } } state open { state_entry() { rotation r1 = llEuler2Rot(<122.5,0.0,0.0> * DEG_TO_RAD); llSetRot(r1 * llGetRot()); } touch_start(integer num_detected) { state closed; } } state closed { state_entry() { rotation r2 = llEuler2Rot(<-122.5,0.0,0.0> * DEG_TO_RAD); llSetRot(r2 * llGetRot()); } touch_start(integer num_detected) { state open; } } If I were guessing, I'd say that this likely came my way from the Outworldz.com site. It only uses LSL functions, though, so it should work in SL as well as OS. Link to comment Share on other sites More sharing options...
animats Posted April 22, 2023 Share Posted April 22, 2023 Right, you have to set both position and rotation in the same operation to get rotation about something other than the center point. If you're willing to write your own kinematic chain math, that's not a big problem. Link to comment Share on other sites More sharing options...
Recommended Posts
Please take a moment to consider if this thread is worth bumping.
Please sign in to comment
You will be able to leave a comment after signing in
Sign In Now