Jump to content

Can I Combine Offset Rotation and Regular Rotation?


BrownBoxStudio
 Share

Recommended Posts

I'm trying to create an amusment park ride, a scrambler. The ride has outer seats that spin locally while also spinning around a central axis. I figured out how to have an object spin around an offset axis, from the wiki, but I cannot figure out how to have a non-offset spin with the offset spin.

I am currently setting the rotation through a tier using llSetPrimitiveParams.

default
{
    state_entry()
    {
        llSetTimerEvent(.01);   
    }
    timer()
    {
        integer num;
        num +=20;
        if (num > 360)
            {
                num = 0;
            }    
        rotation vRotArc = llEuler2Rot( <0.0, 0.0, num> * DEG_TO_RAD );
        vector   vPosOffset     = <5.0, 0.0, 0.0>;
        
        llSetPrimitiveParams( [PRIM_POSITION, llGetPos() + (vPosOffset - vPosOffset * vRotArc) * llGetRot(), PRIM_ROTATION, vRotArc * llGetRot()]);        
    }

}

Is it possible to have a second non-offset rotation alongside the offset rotation? How can I do so? Also is there a faster way then using llSetPrimitiveParams and a timer? With smooth movement it is quiet slow. 

Link to comment
Share on other sites

That's a popular amusement park scripting project.  When people have done it in the past, they've usually managed the rotations with llTargetOmega, which is smooth and is quite fast enough for that sort of use.  You can actually run the spin on both the main axis and the child axes from a single script, using SLPPF and the PRIM_OMEGA option to set the spin parameters for each separately.

Link to comment
Share on other sites

I saw about llTargetOmega. I did get the rotation to work in the way you suggested but I ran into a problem with sit positions not rotating with the child prim. i thought using offset rotation might be easier. I saw something about linking the movement between the child prim and the avatar but wasn't sure where to go with that.  I'll have to look into that more. If anyone has a suggestion I would be more than willing to listen. 

Although if possible I would still like to figure out the way I'm trying now. Is there no way to go about rotation with an offset axis and speed up the script?

 

Link to comment
Share on other sites

Take a look at the teacup ride in the Hairy Hippo Park in Bay City (http://maps.secondlife.com/secondlife/Weston/124/87/26). It's a bit dated but still fun, a nice basic amusement park ride.  The cups are all sculpties, but you could do a better job with mesh cups now.  Each has multiple sit positions.  I'm pretty sure from the way that the ride starts and stops that it is using exactly the method I described.

Link to comment
Share on other sites

I'm not sure but I think the only way to rotate the avatars with llTargetOmega is if you make the seats separate objects from the rest of of the ride, have them rotating on their own axes (which will rotate the avatar, too) and also use llSetKeyframedMotion to make them orbit the central axis.

Turning things in a circle using keyframed motion is a bit tricky.  It's too late at night for me to try anything, but  irihapeti's script here  looks as if it should do the trick.

We have some real keyframed motion experts, like irihapeti, in this forum, and maybe one of them might comment.

 

 

Link to comment
Share on other sites

Thanks for the link to Hairy Hippo Park, I took a look. I'll see about looking into that but I am still hoping I can figure out something with offset rotation. There are some other rides I can think of where I may not be able to use llTargetomega; maybe they would still work but I need to test more.

Although what do I look into to get the sit target to spin with the child prim? The main reason I moved to another sollution is because I didn't know what next step to take.

 

Edit: I found another post talking about how to move a sit position with a child prim and the only way to do so is with llSetLinkPrimitiveParams but it confuses me.

 

https://community.secondlife.com/t5/LSL-Scripting/Move-a-sitting-avatar-with-a-child-prim/td-p/1700527

Link to comment
Share on other sites

I'm not an expert in KFM, but I do have some successful implementations of it on my island, (and a ton of failed projects stored in little boxes....)

Keyframed motion in a circle or an arc seems to suffer from a lack of precision in the moving_end event, but that becomes most noticeable when you are either doing a pendulum-type swing, or want an object to reach or stop at precise locations. Unlike a train with wagons following it around a circle, your fairground ride might not look too odd with some variation in the positions of each seat, so I think it's worth you trying it.

My suggestion would be to go along Innula's route, the seats being individual prims, and give each of them a pre-calculated look up table of position and rotation at various points on the circle around the central part of the ride. At each point, the look up table gives it the rotation and position deltas to apply in order to get to the next point. If necessary, the seat could also have the regional position and rotation it is supposed to have achieved at the end of the keyframed motion, and adjust it's position accordingly using SLPPF.

The same precalculated table will do for all seats, they just start and stop at a different point in the table. They can then have their own target omegas to do the axial-spin

Link to comment
Share on other sites

I know I suggested keyframed motion, but in the course of answering the question about what sit targets do, I got thinking about another way to tackle this problem...

You can't use llSitTarget to move avatars,or not in the way you want to.   When you set a sit target, it tells the server where to place the avatar on the object when first the avatar sits down.    If you then change the sit target while the avatar is seated, that has no effect until the next time someone sits on the object.

You can, however, use llSetLinkPrimitiveParamsFast to move a seated avatar, since for many purposes SL treats seated avatars as if they were ordinary child prims.   Link numbers for seated avatars start at n+1, where n is the number of regular prims in the object.   

Unfortunately, PRIM_TARGET_OMEGA doesn't work for seated avatars, so you have to use PRIM_ROT or (preferably, cos it's easier) PRIM_ROT_LOCAL to rotate seated avatars.

So you could do something based on this (I know we don't normally write full scripts but I got interested in working out the problem, and I've not seen it done this way before).   It assumes that the root prim is located in the centre of the object, and that all the seats are single prims, with their geometric centre is at the centre of the visible prim (i.e. it's not a mesh or a sculptie with a funky bounding box).

In a real example, you would use a timer, obviously, rather than keep on touching the thing:

 

 
integer counter;integer max;integer primCount;list seats;rotation r5degrees; rotation r15degrees;default{    state_entry()    {        counter =1;        max = llGetNumberOfPrims()+1;        primCount = llGetObjectPrimCount(llGetKey());//        do{            if(llToLower(llGetLinkName(counter))=="seat"){ //if the prim is called "seat"                llLinkSitTarget(counter, <0.0,0.0,0.5>, ZERO_ROTATION); //set a sit target                seats+=[counter]; //and make a note of the link number            }        }         while(++counter<max);        r5degrees=llEuler2Rot(<0.0,0.0,5.0>*DEG_TO_RAD); //5 degrees on the z axis        r15degrees=llEuler2Rot(<0.0,0.0,15.0>*DEG_TO_RAD); //15 degrees on the z axis        //the two rotations were chosen arbitrarily     }        changed(integer change){                if(change & CHANGED_LINK){            if(llGetAgentSize(llGetLinkKey(llGetNumberOfPrims()))!=ZERO_VECTOR){            //no one is sitting on me            llResetScript();             //reset the script, in case we've added a new seat or something            }          }            }    touch_start(integer num_detected)    {        max = llGetNumberOfPrims()+1;           counter=2;             rotation rot =llList2Rot(llGetLinkPrimitiveParams(1, [PRIM_ROT_LOCAL]),0);        list params =[PRIM_LINK_TARGET,1,PRIM_ROT_LOCAL,(r5degrees)*rot];         //rotate the root prim, and thus the whole linkset, by 5 degrees        do{            if(~llListFindList(seats, [counter]) || llGetAgentSize(llGetLinkKey(counter))!=ZERO_VECTOR){            //if the link number is that of a seat or of a seated avatar            rot =llList2Rot(llGetLinkPrimitiveParams(counter, [PRIM_ROT_LOCAL]),0);             //calculate a 15 degree rotation             params+= [PRIM_LINK_TARGET,counter]+[PRIM_ROT_LOCAL]+[r15degrees*rot];            //and add link number plus rotation to the list             }        }        while (++counter<max);        llSetLinkPrimitiveParamsFast(LINK_SET, params);        //now rotate everything in the one frame    }}

 

Link to comment
Share on other sites

Your script is pretty awesome. There is still one thing I can't figure out, an issue I ran into with my own scripts. The sitting avatar only rotates around it's own axis while also facing inward. It needs an offset axis to rotate with the seats. Imagine the solar system with a sun, earth, and moon. The moon; the avatar, rotates around the earth; seats, which rotates around the sun; root prim. How do you get the moon to rotate around the earth when they both have the same root prim? 

I guess my main issue is not understanding what is going on with the rotation when you multiply position by rotation, or when you multiply a rotation by another rotation. I mean I can get the offset to work, and get a prim to spin freely with the offset but I don't know what is actually happening to make it work. Anyone care to explain?

I realize I have not actually read through the whole page on rotations and am going to do so before asking anymore questions.

Link to comment
Share on other sites

i just add some thoughts

i first go thru what we can do, which you already found out. I just say for others who might read
 
- we can OMEGA a prim and when sit on it then the avatar will rotate with the prim
 
- we can link prims and OMEGA the root prim, and also independently OMEGA the child prims. But when we sit on the child prim then the avatar rotates with the root prim
 
- we cant OMEGA the avatar itself either
 
- we also cant use OMEGA and KEYFRAME at the same time on the same prim. The OMEGA will stop while the KEYFRAME is running
 
- eta addo. we also cant KEYFRAME the child prim, independently of the root prim
 
+
 
so we have to rotate avatar the normal way (like Innula has mentioned)
 
the downside of the normal way is that we can get visual judder when we dont want to hammer the sim in our script 
 
so to help ameliorate this then a way it can be done is:
 
1) OMEGA the root prim
2) OMEGA the child prim (seat)
3) rotate the avatar as Innula showed in some largish step. Synching it as best we can with the child (seat) OMEGA
 
so altogether, the structure will move smoothly. and only the avatar will lag/judder a wee bit
 
bc this is a kinda wild-ish ride then can advantage the avatar judder to more simulate the RL condition
 
when a RL person is on this kinda ride then their body moves in response to the various pressure/forces applied
 
so we can make a ride animation that wibbles the avatar a little bit as it rides. The wibbling anim will help to disguise the judder/lag of the avatar as it step rotates, while also simulating the RL condition
  • Like 1
Link to comment
Share on other sites

This still comes back to the question, how can the avatar be rotated around the axis of the child prim instead of it's own axis? I've been reading the rotations page and trying to give an offset to a child prim, since the avatar acts like a prim, and it is not working very well. I've tried two methods so far on a two prim object.

 

This method using llGetPos and llGetRot has no visible effect on the child prim.

default{    touch_start(integer num_detected)    {        llSetTimerEvent(.01);    }    timer()    {        vector vPosOffset = <1.0, 0.0, 0.0>;        rotation vRotArc = llEuler2Rot( <0.0, 0.0, 5.0> * DEG_TO_RAD );        list params = llGetLinkPrimitiveParams(2, [PRIM_POSITION,PRIM_ROTATION ]);        vector childPos = llList2Vector(params,0);        rotation childRot = llList2Rot(params,1);        vPosOffset = vPosOffset * vRotArc;        llSetLinkPrimitiveParamsFast(2, [PRIM_POSITION, llGetPos() + (vPosOffset - vPosOffset * vRotArc) * llGetRot() ,PRIM_ROTATION, vRotArc * llGetRot()]);    }}

Using the local position and rotation of the child prim has results but nothing at all useable. It's just glitchy movements around a non-offset axis. 

default{    touch_start(integer num_detected)    {        llSetTimerEvent(.01);    }    timer()    {        vector vPosOffset = <1.0, 0.0, 0.0>;        rotation vRotArc = llEuler2Rot( <0.0, 0.0, 5.0> * DEG_TO_RAD );        list params = llGetLinkPrimitiveParams(2, [PRIM_POS_LOCAL,PRIM_ROT_LOCAL ]);        vector childPos = llList2Vector(params,0);        rotation childRot = llList2Rot(params,1);        vPosOffset = vPosOffset * vRotArc;        llSetLinkPrimitiveParamsFast(2, [PRIM_POSITION, childPos + (vPosOffset - vPosOffset * vRotArc) * childRot ,PRIM_ROTATION, vRotArc * childRot]);    }}

 

Basically what I was trying to do is to get the avatar, to stay in the same spot relative to the red arm. The red arm spins relative to the center arms but the avatar, it does spin thanks thanks to Inulla, only spins on it's own axis. It won't spin on any offsets I give it. 



 

Link to comment
Share on other sites

I'll try to work out a reply to your question later today if I get a chance, though RL is likely to keep me rather busy so it may not be till tomorrow or Monday that I can give it proper attention.

However, a couple of preliminary points.  

  • First, why does the avatar have to be offset from the centre of the seat?  It's far simpler if they're not.  
  • Second, if the avatar does have to be offset, presumably you're doing this by using the sit target.  What is the sit target for the child prim in your example?

ETA -- you posted your picture while I was writing this.  I think I understand now.  I'll have a play when I get a chance, unless someone else wants to try.

Link to comment
Share on other sites

  • First, why does the avatar have to be offset from the centre of the seat?  It's far simpler if they're not.  

Seems you got it from my image but to itterate the reason the sit has to be offset is because that is how the ride works. The ride has a base with 3 or 4 extended arms that hold a seat piece. The seat piece also has 3 or 4 arms each with seats on the end of those arms.Like my photo this is a real seat part of a scrambler which, as others have suggested I am making 1 mesh with the center being the pole:



 

I can't imagine it would be impossible to give a child prim an offset but so far none of my tests on child prims have worked. I don't know enough about rotations, even after reading the wiki, to know where to go next. 

The original plan I had, so that the avatar seating did not have to be moved, was to keep the seat part as a seperate object and rotate via an offset axis and then apply a second rotation on the original axis. Although the second rotation messes up the offset axis. I think that way might be a lot easier than figure out how to move all the sit positions.

So basically an earth rotating around it's axis while also rotating around the sun, except in this case the earth is not a child prim. I could get the earth to rotate around the sun but I can not get the earth to rotate around it's own axis and the sun at the same time. 

 May still run into the same problem since the sitting avatars will be child prims of the seperate object.

Link to comment
Share on other sites

need to use LOCAL

 

vector offset = <-0.3,0.1,0.9>; // adjust for avatardefault{        touch_end(integer num_detected)    {        list params = llGetLinkPrimitiveParams(2, [PRIM_POS_LOCAL, PRIM_ROT_LOCAL]);               vector pos = llList2Vector(params, 0);        rotation rot =  llList2Rot(params, 1);        llSetLinkPrimitiveParamsFast(3, [PRIM_POS_LOCAL, pos + offset, PRIM_ROT_LOCAL, rot]);     }    }

 

example:

Link to comment
Share on other sites

I did use local rotation in my tests. I just found a weird outcome, using local. The offset axis for a child prim works perfectly fine if the root prim is at a 90 degree interval. As soon as the root prim is at a different angle, even if the root prim is static, the child prim does not spin properly. Using the same code as I did above.

 

default{    touch_start(integer num_detected)    {        llSetTimerEvent(.01);    }    timer()    {        vector vPosOffset = <1.0, 0.0, 0.0>;        rotation vRotArc = llEuler2Rot( <0.0, 0.0, 5.0> * DEG_TO_RAD );        list params = llGetLinkPrimitiveParams(2, [PRIM_POS_LOCAL,PRIM_ROT_LOCAL ]);        vector childPos = llList2Vector(params,0);        rotation childRot = llList2Rot(params,1);        vPosOffset = vPosOffset * vRotArc;        llSetLinkPrimitiveParamsFast(2, [PRIM_POSITION, childPos + (vPosOffset - vPosOffset * vRotArc) * childRot * llGetRot() ,PRIM_ROTATION, vRotArc * childRot * llGetRot()]);    }}

I would think the child prim would still be able to calculate the same offset axis no matter what the root prim is at. Is this an issue with SL rotation or my program? I don't know anymore at this point.

Link to comment
Share on other sites

ok. understand

assumptions

there is a center pole with 4 arms. The center pole (root) rotates

at the end each arm there is a pole attached. this pole rotates independently of the center pole. attached to this pole is a fixed arrangement of 4 seats. Being fixed they rotate with their own pole

so if make out of mesh then is a root prim (with 4 arms) that rotates. There is a child prim (made out of mesh), pole + seats, linked to each arm

the seats with avatars on dont rotate independently of the pole they are attached to

ok

by linking the prims. We can:

rotate the center pole

this will rotate the arms

we can rotate the childs (seats) independently by rotating the child prim

+

when avatar sits on the seat. The structure rotates so all good. The seats rotate round their own poles. So all good

The avatar tho rotates with the root prim and not the child prim it is sitting on

am not sure why then, given what has been mentioned already and given the codes already given, why the avatar is not rotating as is should

  

 

 

 

   

Link to comment
Share on other sites

I had originally thought you wanted each seat to be rotating around its centre point which was itself rotating around the ride centre point.

Having seen what you are trying to do, I think I would go for a simpler option. Make each seat a root prim, and rotate them around a fixed centre. Give each seat a lookup table of position and rotation. Make one seat the master. It trasnmits to the others the index number in the lookup table it is at, each other seat then applies an offset to get it's own index and works out where it should be at. The four-arm spider can similarly listen to the master seat and adjust it's position accordingly, so it tracks the master seat.

This system means that you can sit two avatars on the root prim by having two child cushion prims with sit targets, the seated avatars will therefore rotate exactly as the root prim rotates, and there is no complicated messing around with local position and rotations, all you need to do is get all seats rotating around a fixed point, (for which there is a wiki example, and staying the same relative distance away from each other.

 

 

 

Link to comment
Share on other sites

@irihapeti 

This is because the avatar acts as a prim in itself so it will not inhernetly rotate with the child prim seats. I cannot figure out how to give it an offset rotation to follow the child prims axis.

@ Profaitchikenz Haiku 

What do you mean by "lookup table"? I think I've been trying to do something similar except instead of a lookup table, if I understand at all, I've been trying to use an offset axis to have the seat rotate around the base and then have the seat object rotate itself. Although I haven't been able to get the object to rotate around it's own center and the center of the base. It does some odd things when I have two rotations. The offset axis is set by the seats rotation so if I have the seat rotating on it's own then the offset axis is constantly changing positions which is not intended. The script I was using for the offset axis is:

default{    touch_start(integer num_detected)    {        llSetTimerEvent(.01);    }    timer()    {        vector vPosOffset = <1.0, 0.0, 0.0>;        rotation vRotArc = llEuler2Rot( <0.0, 0.0, 5.0> * DEG_TO_RAD );        //vPosOffset = vPosOffset * vRotArc;        llSetPrimitiveParams( [PRIM_POSITION, llGetPos() + (vPosOffset - vPosOffset * vRotArc) * llGetRot(), PRIM_ROTATION, vRotArc * llGetRot()] );    }}

 I would actually like to get the ride working with, the way you suggested and my original question, the seats being a different object. Doing so would make creation easier in the long run and allow me to use the knowledge in other rides. Except I don't know how to get a root axis rotation to work alongside an offset axis. Your suggestion seems different though using a lookup table I just do not know what a lookup table is. 

 

 

Link to comment
Share on other sites

Make up a strided list of the form [position, rotation, ... ] let's say with 72 pairs of entries, and at startup, read a notecard or call a function to calculate the positions and rotations, it's as simple as using x = centreX + radius*llCos(angle), y = centrey + radius*llSin(angle), and in this instance, starting from an initial rotation, multiply by a rotation of 5 degrees on the Z-axis, store that, multiply it again for the next entry....

Therefore when you consider index taking the values 0 to 142 in steps of 2, llList2Vector(lookups, index) and llList2Rot(lookups, index+1) will return the pre-calculated position and rotations for any angle in steps of 5 degrees.

If you are using llTargetOmega to spin the seat you do not actually want to rotate it, the only time you use the rotations is either if an enpty non-spinning seat ius to be shown, or when llTargetOmega stops and you want to rotate the seat to where it actually should be considering where round the circle it has stopped.

I have at the moment a one-prim sculpted bench which will seat up to three avatars orbiting slowly around a lamppost , the bench is also spinning around it's central Z-axis using llTargetOmega, and two avatars are sitting happily on it going round and around, although at least one of them is showing signs of motion-sickness.

 

I would post a script but my hat has become increasingly dented of late by reminders that this forum is not for posting complete scripts in :)

ETA added two more benches, spaced at 120 degrees from each other, each rotating round the lamppost, each spinning using llTargetOmega, one bench chats it's current angle to the other two, there's no lookup tables involved,  and the total script time for them is still only 0.06 milliseconds. It would probably halve if I switched them all to lookup tables, but as it is, it's low enough for the visual effect it provides

  • Like 1
Link to comment
Share on other sites

if was me then i would go down this path

 

// example basis for the spin in spin thingy// link 2 prims// stick this script in the root// sit on the child// Touch to start. touch again to stop // adjust time and slice to get how you wantinteger going;float slice;float time = 0.2;start(){    slice = 1.0 / 2 * time;        vector axis = <0.0,0.0,1.0>;    llTargetOmega(axis, 1.0, 1.0);        llSetTimerEvent(time);      going = TRUE;   }stop(){    going = FALSE;    llSetTimerEvent(0.0);    llTargetOmega(ZERO_VECTOR, 0.0, 0.0);}default{    state_entry()    {        stop();    }        touch_end(integer num_detected)    {        if (going) stop();        else start();    }        timer()    {        rotation r = llList2Rot(            llGetLinkPrimitiveParams(2, [PRIM_ROT_LOCAL]) , 0);                r = r * <0,0,-slice, -1.0-slice>;                llSetLinkPrimitiveParamsFast(2, [PRIM_ROT_LOCAL, r]); // 2 is the child        llSetLinkPrimitiveParamsFast(3, [PRIM_ROT_LOCAL, r]); // 3 is the avatar     }}

 

eta

is my recommend bc if go down any other path then it gets more involved

+

eta more

this is not the actual solution to the OP issue. Can be used tho as a basis to make a different kinda ride

the actual OP solution is what Prof posted above

 

Link to comment
Share on other sites


Profaitchikenz Haiku wrote:

 

I would post a script but my hat has become increasingly dented of late by reminders that this forum is not for posting complete scripts in
:)

I'm always posting scipts,  and I'd certainly find your solution -- which sounds a very good one -- easier to understand if I could see the script.

Seriously, I think that it's posting simple scripts, and thus depriving learners of the opportunity to study the fragments we post, that people complain about.   Something like this, which is pretty complex and has us all thinking (me, anyway) is rather different.

If you don't mind posting, I'd find it helpful to read through the whole thing.  I think I understand the method but trig was never my strong point and it was all more years ago than I like recall.

Link to comment
Share on other sites


Innula Zenovka wrote:


Profaitchikenz Haiku wrote:

 

I would post a script but my hat has become increasingly dented of late by reminders that this forum is not for posting complete scripts in
:)

 .... ]Seriously, I think that it's posting simple scripts, and thus depriving learners of the opportunity to study the fragments we post, that people complain about.   Something like this, which is pretty complex and has us all thinking (me, anyway) is rather different.

[ .... ]

Exactly.  It's a loose sort of standard that we've followed since before most of the current crowd started here years ago. We typically post snippets and illustrative examples rather than posting full-blown scripts.  That's partly because we want to discourage an OP from coming here to get a free script, thus depriving some starving scripter of the chance to write the script for him and get paid.  Mostly, though, it's because this has traditionally been a forum for scripters -- a place to trade ideas and moan about things that aren't working quite right.  It sort of perverts the purpose if an OP who has little intention of learning to script comes here just to get someone else to do his work.  There's a very fuzzy line in there somewhere, and we each find it in our own way.  As Innula says, sometimes we get fascinated by an OP's challenge and end up posting a lot more than snippets and schematic ideas.  This thread is one of those. 

Link to comment
Share on other sites

agree what you and Innula are saying
 
so Prof if you want to then I think is ok. If is such that can be useful for a wide range of people then can stick in the Library, so people can find more easy in the future
 
+
 
about the other
 
if a OP is asking for something for which there is already examples on the wiki, library or a other post then I think is reasonable to point them to that and say for them to have a go at it themself. bc like you say is about helping people to grow as scripters
 
if they show no real interest in learning themself, which mostly comes when a person just wants to make a one-off thing, then see you later that person. Like if they just want a one-off (or a whole lifetime series of one-offs) then they can go and buy them
 
+
 
and then for me personal
 
then sometimes we get a quite complex issue for which there can be more than one algo/approach which makes us think, even quite experienced scripters, which when solved in one or more ways, can be quite helpful to everyone. Seeing the different approaches taken and the algos that come out of these. Which sometimes while not always the exact answer to the original issue, can give us some ideas of how to approach similarish problems elsewhere
 
sometimes also is not a complex issue as it might turn out, just a rare instance out of the mainstream, but we never knew that until we start to chat about it, and see some algos posted
 
issues which are interesting for us to try work out how it might be done
 
i quite like these interesting ones, mostly bc working on them, chatting about them, and learning off others by the approach they take, helps me to grow myself
Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...