Sign in to follow this  
Suen

Smooth rotation and movement of camera

Recommended Posts

Hello. I managed to implement an fps camera and to simplify it for myself at the moment I control everything by the keyboard. My camera is able to do yaw and pitch rotations (with pitch being limited to +- 90 degrees) and I'm able to move it up/down, left/right and forward/backward.

To do the rotation I store the camera target in spherical coordinates and modify these spherical coords and then convert them to cartesian coords. and re-calculate the camera matrix. To move the camera around I simply modify the camera position and re-calculate the camera matrix.

So far everything works as I want it. But whenever I rotate my camera (each time I press a button I change the latitude/longitude of my camera target by one unit/angle) or move my camera around I get "jerky movements". Basically they are nowhere close to being smooth rotations and movements. If I press and hold a button that keeps rotating the camera I merely want this rotation to be smooth and the same if I were to press and hold a button which moves the camera in some direction. What exactly should I be looking into to be able to achieve this?

Share this post


Link to post
Share on other sites
Anyone? Apparently if I were to control the rotation of my camera with mouse movement this would make the rotations more smooth but I can't see why it solves the problem while using keyboard buttons does not. So if anyone can explain that it would be nice.

Furthermore even if I were to implement mouse movement it doesn't solve the "jerky" movement if I move around the camera itself in the scene.

Share this post


Link to post
Share on other sites
Perhaps your problem is caused by the step sizes you are taking when accumulating the new angles to the camera orientation? To get smoother rotations, apply the rotation in smaller steps, or decouple the increments and maintain a "currently rendered" orientation and a "desired target" orientation, between which you interpolate smoothly.

Seeing some code samples would probably help here.

Share this post


Link to post
Share on other sites
What you are looking for is interpolation. Basically, you use an elapsed-time value to interpolate between the camera's previous position/orientation and the camera's next position/orientation, and calculate the view matrices from the interpolated values.

Consider that at time t=0, the camera is at (0,0). The camera moves at 1 unit per second. Say that at t=0 you receive a keystroke to move the camera, so you translate the camera by one unit. However, this translation occurs instantaneously. At t=0 the camera is at (0,0) and at t=1 the camera is suddenly at (0,1). It causes a visible jump.

The thing is, in that 1 second span of time, the screen refreshed 60 times or more (depending on frame rate). For 60 frames it drew the view from camera at (0,0), then suddenly switched to drawing the scene from camera at (0,1), causing a visual jump. What you could be doing instead, though, is tracking the frame time of when the scene is drawn, and using that frame time to interpolate between (0,0) and (0,1) so that intermediate frames drawn will show the camera at different locations in between (0,0) and (0,1)

You might read the old standby, [url="http://gafferongames.com/game-physics/fix-your-timestep/"]Fix Your Timestep![/url] or Javier Arevalo's [url="http://www.flipcode.com/archives/Main_Loop_with_Fixed_Time_Steps.shtml"]Main Loop with Fixed Time Steps[/url] at the old Flipcode site for more information on how this works.

Share this post


Link to post
Share on other sites
[quote name='clb' timestamp='1335899122' post='4936488']
Perhaps your problem is caused by the step sizes you are taking when accumulating the new angles to the camera orientation? To get smoother rotations, apply the rotation in smaller steps, or decouple the increments and maintain a "currently rendered" orientation and a "desired target" orientation, between which you interpolate smoothly.

Seeing some code samples would probably help here.
[/quote]

Wouldn't applying smaller steps to my rotation cause the camera to rotate slower though? Or am I misunderstanding something here? I'll post the necessary code here:

[b]Function to create camera matrix[/b]
[CODE]
void createCameraMatrix()
{
glm::vec3 camTargetCart = sphericalToCartesian(cameraTarget); //Convert the camera target's spherical coords. to cartesian.

glm::vec3 lookDir = glm::normalize(camTargetCart); //Look direction
glm::vec3 upDir = glm::normalize(glm::vec3(0.0f, 1.0f, 0.0f)); //Up direction of camera, aligned with world's y-axis at beginning.

glm::vec3 rightDir = glm::normalize(glm::cross(lookDir, upDir)); //Calculate remaining axis of camera
glm::vec3 perpUpDir = glm::cross(rightDir, lookDir); //Re-calculate up direction

//Create camera matrix
rotMat[0] = glm::vec4(rightDir, 0.0f);
rotMat[1] = glm::vec4(perpUpDir, 0.0f);
rotMat[2] = glm::vec4(-lookDir, 0.0f);

rotMat = glm::transpose(rotMat);

transMat[3] = glm::vec4(-cameraPos, 1.0f);

finalMatrix = rotMat * transMat;

//Give value to uniform in shader
glUseProgram(theProgram);
glUniformMatrix4fv(locCameraTest, 1, GL_FALSE, glm::value_ptr(finalMatrix));
glUseProgram(0);
}
[/CODE]

[b]Some examples of my functions that are called when a keystroke is received[/b]
[CODE]
void rotCamHorizontal(GLfloat angle)
{
cameraTarget.y = cameraTarget.y + angle; //Add angle units to camera target (in spherical coords.)
createCameraMatrix(); //Re-calculate camera matrix to account for modified camera target
}

void rotCamVertical(GLfloat angle)
{
cameraTarget.z = cameraTarget.z + angle; //Add angle units to camera target (in spherical coords.)
cameraTarget.z = glm::clamp(cameraTarget.z, -90.0f, 90.0f); //Clamp the pitch between +- 90 degrees
createCameraMatrix(); //Re-calculate camera matrix to account for modified camera target
}

void moveCamForward(GLfloat moveSpeed)
{
cameraPos = cameraPos+(sphericalToCartesian(cameraTarget)*moveSpeed);
createCameraMatrix();
}

void moveCamRight(GLfloat moveSpeed)
{
cameraPos.x = cameraPos.x + (rotMat[0].x*moveSpeed); //rotMat[0].x correspond to (row, column) = (1, 1)
cameraPos.y = cameraPos.y + (rotMat[1].x*moveSpeed); //rotMat[1].x correspond to (row, column) = (1, 2)
cameraPos.z = cameraPos.z + (rotMat[2].x*moveSpeed); //rotMat[2].x correspond to (row, column) = (1, 3)
createCameraMatrix();
}
[/CODE]

[b]Function where movement/rotation functions are called.[/b]
[CODE]
void keyboard(unsigned char key, int x, int y)
{
GLfloat angle = 1.0f;
GLfloat moveSpeed = 1.0f;

switch(key)
{
//Move forward
case 'w':
moveCamForward(moveSpeed);
break;

//Rotate right (yaw)
case 'd':
rotCamHorizontal(-angle);
break;

//Rotate up (pitch)
case 'q':
rotCamVertical(angle);
break;

//Strafe right
case 'x':
moveCamRight(moveSpeed);
break;
}
}
[/CODE]

I'd also like to point out that I only initialize the camera matrix once as my program is running (even when it is idle and doing nothing). The only time I re-calculate it is as shown above when either the camera position or camera target are changed. I'm not sure of this matters though. Also thanks FLeBlanc for the suggestion, will definitely look into it. But as requested the code is above for people to see, any suggestions are appreciated. Edited by Suen

Share this post


Link to post
Share on other sites
As suggested, look into interpolating your camera position and rotation. That is the way to achieve the smaller steps the first poster recommended, but without slowing the speed down.

Share this post


Link to post
Share on other sites
[quote name='JTippetts' timestamp='1335924690' post='4936632']
As suggested, look into interpolating your camera position and rotation. That is the way to achieve the smaller steps the first poster recommended, but without slowing the speed down.
[/quote]
Will do, haven't looked very much yet through the links posted by FLEBlanc (since it's pretty late over here) but I went through it a bit fast and it seems to describe how to do the interpolation.

From what I understand so far a linear interpolation would be sufficient for smooth camera movement while quaternion slerp would be sufficient for smooth camera rotation. Either way I will look more into this when I wake up. Thanks for the suggestions [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img] Edited by Suen

Share this post


Link to post
Share on other sites
Well having read into this (and thanks to the answers above) I understand the issue behind why the animation is stuttering but I'm still having somewhat of a hard time with it. I'm not really sure if I should go on with this in this section of the forum (since the code is OpenGL specific) but I'll give it a try.

I checked the link provided by FLeBlanc ([url="http://gafferongames.com/game-physics/fix-your-timestep/"]Fix Your Timestep![/url]) and did also take a look at the source code provided there. I pretty much did the same:

I created a function for interpolation. I then changed one of my camera movement functions, storing the old and new position and then using them, together with what I assume would be the elapsed time value, as arguments for the interpolation function. I then create the view matrix from the interpolated camera position. See this below


[CODE]
glm::vec3 interpolate(const glm::vec3 &start, const glm::vec3 &end, float alpha)
{
glm::vec3 interp;

interp.x = end.x*alpha + start.x*(1-alpha);
interp.y = end.y*alpha + start.y*(1-alpha);
interp.z = end.z*alpha + start.z*(1-alpha);

return interp;
}

void moveCamLeft(GLfloat moveSpeed)
{
glm::vec3 startPos(cameraPos);

glm::vec3 endPos;

endPos.x = cameraPos.x - rotMat[0].x;
endPos.y = cameraPos.y - rotMat[1].x;
endPos.z = cameraPos.z - rotMat[2].x;

cameraPos = interpolate(startPos, endPos, timeValue);
createCameraMatrix();
}
[/CODE]

Now as far as I've understood from the posts here and from the links provided is that if I want a smooth movement from one position to another I would need to draw all values between the two positions where each one of these values would correspond to a frame being drawn. For example if we have 60 fps then the scene would be drawn from 60 different positions and then when the next frame is drawn I am at the final value. To do this I understood it as using a time-based value for my interpolation (where the time-based value would vary from 0 to 1, 0 corresponding to the start and 1 to the end position when I interpolate). But I am still quiet confused...exactly what am I supposed to keep time of? The amount of time it takes to draw a frame? Something else? I am also confused to when I start to measure the time and exactly in what function I should do so.

I feel this should be quite easy to understand (conceptually it is). I'm probably making it harder than what it is actually. Edited by Suen

Share this post


Link to post
Share on other sites
You measure the time in your main loop. Look at the loop from "Fix Your Timesteps!" again:

[code]
double t = 0.0;
const double dt = 0.01;

double currentTime = hires_time_in_seconds();
double accumulator = 0.0;

State previous;
State current;

while ( !quit )
{
double newTime = time();
double frameTime = newTime - currentTime;
if ( frameTime > 0.25 )
frameTime = 0.25; // note: max frame time to avoid spiral of death
currentTime = newTime;

accumulator += frameTime;

while ( accumulator >= dt )
{
previousState = currentState;
integrate( currentState, t, dt );
t += dt;
accumulator -= dt;
}

const double alpha = accumulator / dt;

State state = currentState*alpha + previousState * ( 1.0 - alpha );

render( state );
}
[/code]

Here, [b]currentState[/b] and [b]previousState[/b] represent the set of transforms for the camera and all objects in the world at time t(n-1) and t(n), where n is the current logic step. [b]accumulator[/b] is used to accumulate the advancement of time, and is also used to track how far into the current logic step we are. Each iteration through the loop, the current time is compared to the last time stored from the last time through the loop, and the difference is applied to accumulator. Then the logic update portion iterates on accumulator; as long as accumulator is larger than the length of 1 logic step ([b]dt[/b], or 0.01 in this example) then a physics step is performed (objects are moved, etc...). As soon as accumulator drops below the value of [b]dt[/b], we know that we are "current" or caught-up on logic updates and can proceed to rendering. However, the value of accumulator will now tell us how far along into the next physics step we are, so we can use it as the timeValue to interpolate transforms.

Share this post


Link to post
Share on other sites
Using interpolation to smooth from the previous position to the intended position will slow the camera down. Perhaps what you are looking for is input-smoothing which is averaging the deltas and then applying the delta to the camera.

Time delta is necessary to convert a velocity-in-seconds value into a velocity-in-frames value. When using mouse input, I divide the mouse delta by dt to get the pixels-per-second value. Buffer that over 15 frames, and average out the last 15 frames worth of velocities. Then multiply that by dt again to turn it back into the value for the frame.

The side effect of this is that the camera will have a bit of inertia after stopping movement of the mouse, but that tends to feel better than a harsh stop anyway.

Share this post


Link to post
Share on other sites
Sorry for the slow reply, my network has been done since I wrote the last reply and it's barely being stable now but here goes.

Thanks for the suggestion DigitalFragment, I've never heard of that approach but it seems to make sense. However I'm lost now on what way to approach. Like you I thought at first that a smooth interpolation between the prev. position and the intended one would slow down the camera but as suggested above (and as well in a few other places I've been looking into) this does not seem to be the so. So...while I am actually still confused and have several questions to ask (about both yours and the suggested one by FLeBlanc) I would appreciate it if I could opinions on what approach I should take.

Finally I want to say that I "solved" my problem. I was playing around with being able to register multiple keyboard inputs in GLUT. Basically I have two callback functions which are registered whenever a button is pressed/not pressed. I then have a third function which describes what each keyboard button I want to use do (here is where I call my camera movement/rotation functions). I finally call this third function in my display method (the method responsible for doing all rendering stuff), basically it's called for each loop (each time a new frame is drawn). This results in both smooth translation and rotation as I wanted. Compare to what I had before; I had a keyboard function (see above) which, instead of getting called every frame, only got called when I pushed a button. The result was that when the next frame is drawn after the keyboard input, with a modified view matrix, I would see the movement of my camera, but with this effect of "stuttering" which I wanted to solve.

Now here's the thing.......I don't have the slightest bit of clue as to why this second approach I took work. So no I really haven't solved the problem. I have a working solution without really understanding what's going on behind the scenes. Or have I perhaps confused everyone with what I intended to do? Well now that I think of it it's not really a working solution at all. Right now it's working fine because my FPS locked down to something around 60 FPS due to VSync being turned on, however the refresh rates obviously vary from one PC to another so if I use this solution in a PC with better/worse performance (or with VSync turned off) I would end up getting slower/faster movement. I'm still just curious to why it works...for 60 FPS at least.

Anyhow I'm still at step one so before I go further what is the recommended way to go for? Smooth interpolation or what DigitalFragment suggested? Would smooth interpolation slow the camera down as DigitalFragment said and as I thought from the beginning?

Share this post


Link to post
Share on other sites
Smooth interpolation doesn't exactly slow the camera down any except for the fact that its always falls short of the target:
If you want to rotate from 30 degrees to 40 degrees in one frame, but are interpolating, then you aren't going to hit 40, you are going to hit anywhere > 30 and < 40.
On the next frame, if you have stopped rotating, then the interpolation doesn't continue to 40, as you will want to interpolate from the previous value, to a value no different to the previous value. It does work to solve the issue, but it results in 'sudden termination' when you stop the rotation.

The other trick causes smoothing both when the rotation starts and when the rotation ends. As such, it causes the same lag initially, but while continuing to hold the rotate button, that lag disappears.

I'd suggest trying both and seeing which feels better for your input system. It largely depends on what sort of game you are making, the choice here is really a design issue not an implementation one.

Share this post


Link to post
Share on other sites
Since we've gone a bit into interpolation already I'll go with it first but I definitely want to try out the way Digitalfragment suggested, hopefully it won't be hard to implement.

FLeBlanc, I do have several questions I want to ask about the particular implementation you posted there from "Fix Your Timesteps!". I've read through it about 3-4 times now and still have some problems grasping exactly everything going on there (I do understand it a bit better now but still mostly only understand some bits here and there). However before that I think I really need to clarify (for my own sake) about what exactly is going wrong in my implementation or else I won't understand why the methods suggested by you guys work. Obviously you and more have explained what's going on but I feel slightly shaky about it still. I'll try to simplify my explanation.

I have a callback function called 'display' which is where I draw all my stuff (I assume THIS is what is usually referred to as my game logic, please correct me if I'm wrong about it here). Now normally this function, due to the nature of GLUT, would only be called during certain events (resizing the window, minimizing it etc.) but what I've done is to put a command at the very end of the function which will call the function again. This essentially means that I'm calling the display function X number of times per second, in other words X frames are being drawn each second. Since I have VSYNC turned on the value of X ends up being locked down to something around 60 (assume 60 for simplicity). So 60 frames per second are drawn, 60 FPS. So far so fine, nothing special here.

Now I have a callback function which is called whenever a keyboard button I've specified is pushed. In my implementation this moves/rotates the camera a certain amount of (x,y,z) units. This is where the movement starts looking rough as mentioned before and it's from here I get slightly confused. What EXACTLY is happening the very moment I push the button? Is the call/registration of my keystroke put in some queue which is registered after certain amount of time? For example is this what happens (assuming we have 60 FPS):

Display //Render scene at t=0 ms
Display //Render scene at t=16 ms
Display //Render scene at t=32 ms
.....
Display //Render scene at t=1000 ms
Keystroke //Make change to my camera at t=1000+x ms

Or is something else happening? Again what exactly is happening the very moment I push it?

I'm going to explain it the way I understood it according to FLeBlanc's first reply; I push the button at t=0 but by the time this push has registered a second has elapsed (how long does it take to register a keystroke so we get the new change for the camera? is it strictly a second?). During this second 60 frames were drawn from the original viewpoint of the camera. Then after that comes whatever changes the keystroke did and the next frame is drawn from the new viewpoint of the camera. This cause a sudden change, a jump as described by FLeBlanc in the first reply, because none of the intermediate values between the original and new camera viewpoint are shown. Please correct me here if I am thinking wrong about what's going on when I push a button.

Now comes the next thing I am not quite getting. As I said in an earlier post I made three functions. Two of these are callback functions which basically modify a flag to know whether a key is being pressed or not. The third function has the code that updates my camera position/orientation and this third function is called in my display function, in other words it is called 60 times per second as well. The only thing different here is that while it is called nothing happens if no key is pressed. If I press and hold a key then once the keystroke has been registered the third function described above updates my camera position/orientation EACH frame by (x,y,z) units. Since I am ALSO drawing the updated camera each frame that is the reason to why I get a smooth motion. I know this would be frame-dependent but disregard that for a moment. Am I thinking correct of this? Please confirm this as well

This is quite a long post and I apologize about that but I really feel that if I can't properly grasp the problem I'm having then I'm just going to have a harder time understanding why a suggested method works.

Share this post


Link to post
Share on other sites
I suspect that what might be going wrong in your code is key repeat. It's been years and years since I bothered with Glut, but if I remember correctly by default it repeats keys. This means that it will send a sequence of keydown/keyup callbacks timed according to the timing of the system key repeat rate. Typically with games, you want to use [b]glutIgnoreKeyRepeat[/b] so that keys won't be repeated and the callback for [b]glutKeyboardFunc[/b] will store state for the key in a table. The callback for [b]glutKeyboardUpFunc[/b] will clear that state. Then, in your update function, you query the state of this key table and move the camera accordingly.

Also, typically, your [b]display[/b] function, where you "draw your stuff" is not what is meant by your game logic. Game logic is all the moving of bullets, the walking of enemies, the physics updates, etc.... A function called [b]display[/b] should be just that: something that draws things.

In the fixed timestep loops that FLeblanc and others are talking about, you will allow [b]display[/b] to be called in the outer, or main, part of the loop. You can see that in the above posted loop code as the function [b]render[/b]. All it does is draw the scene, using the interpolated transformation state, as many times per second as available time will allow once the logic updating is done. The actual physics and logic updating, though, take place in the function [b]integrate[/b] which is nested inside an inner loop that is there to make sure that physics updates take priority over screen rendering if and only iff the simulation "falls behind". Otherwise, the advancement of [b]accumulator[/b] and associated conditionals exist merely to ensure that [b]integrate[/b] is called in a timely and precise fashion.

Now, it is important to note that Glut implements its own internal loop, implemented in [b]glutMainLoop[/b]. If you wish to continue using Glut, you would be well advised to seek out FAQs or other sources of info specific to Glut, since you would need to work within Glut's own loop and framework of callbacks in order to implement the presented fixed time stepping, perhaps doing something like using [b]glutTimerFunc[/b] to time the calling of your physics and logic update, and using [b]glutIdleFunc[/b] to call [b]glutPostRedisplay[/b]. Note that I don't use Glut anymore and never used it for any serious projects even when I did use it, so this might be completely wrong information.

(by the way, mentioning that you were using Glut would have helped immensely, and probably would have led to a solution much sooner; something like that would be considered important information for us to know.) Edited by JTippetts

Share this post


Link to post
Share on other sites
JTippets, thanks for the suggestions and sorry for the late reply (have been busy these days though I've also been taking a closer look at the code discussed here. I've been wondering one thing today with regard to my problem. I'm starting to kind of get the whole picture about this (code-wise, though most of you have explained it pretty well here, I'm still trying to grasp my head around it). I'd like to actually post my understanding of it just to see if I got it right but I'm in a bit of a hurry at the moment. Instead I just want to ask a really short question for the time being

As you and the rest of the people in this thread knows I only want to control my camera but I want it to be frame-independent. I just want to move the camera which is quite simple compared to having performing some kind of more advanced physical simulation. So I thought of it and...wouldn't it be enough to actually calculate the time it takes from updating one frame to the next and take that time and multiply it by some camera speed? I do understand that this is not the best solution because this could give us different deltatimes for each frame update and generally a physical simulation might not accept a wide range of dt's. What you want instead in that case is a fixed dt to make things easier and more predictable for the physical aspects of the prorgram.

But my purpose is rather simple, only moving/rotating the camera from pos/orient. 1 to pos/orient. 2 and no more. What I thought I could do is what I did earlier, make a call to my function which controls the game logic for each frame. If a key is pressed this function will perform some update and this update would be based on the difference in time between two frames. Basically the code would be something like this:

[CODE]
cameraPos.x = cameraPos.x + (rotMat[0].x*moveSpeed*timeInterval);
cameraPos.y = cameraPos.y + (rotMat[1].x*moveSpeed*timeInterval);
cameraPos.z = cameraPos.z + (rotMat[2].x*moveSpeed*timeInterval);
[/CODE]

where timeInterval = currentTime - prevTime and moveSpeed is some camera speed. rotMat[0/1/2] is to describe in what direction to move the camera by using the camera's local axes. Wouldn't this be sufficient for my purpose?

Share this post


Link to post
Share on other sites
For simple camera motion, it is fine to just use dt*speed, rather than a fixed step. Where a fixed step [i]really[/i] becomes important is a) physics simulations, that can become unstable with high values of dt, and b) multiplayer, where slight mathematical error occurring on each machine can cause a de-sync.

Share this post


Link to post
Share on other sites
[quote name='JTippetts' timestamp='1336434308' post='4938219']
For simple camera motion, it is fine to just use dt*speed, rather than a fixed step. Where a fixed step [i]really[/i] becomes important is a) physics simulations, that can become unstable with high values of dt, and b) multiplayer, where slight mathematical error occurring on each machine can cause a de-sync.
[/quote]
Yes this is what I suspected as well. I just tried implementing the code I posted in my previous reply. I tried it out with enabling/disabling Vsync and it seems to work fine. One thing I've been wondering after looking through several resources is that people say dt*speed is enough for a frame-independent movement (well in case of a simple purpose). My corresponding code, from what I understand, would simply be this (assuming one coordinate to keep this short):

[CODE]
cameraPos.x = cameraPos.x + (rotMat[0].x*timeInterval);
[/CODE]

rotMat[0].x would be the actual displacement while timeInterval is the time it takes to draw one frame as mentioned before. But running it like that was way too slow so I had to add in a weighting factor (moveSpeed in prev reply). Am I understanding something wrong here?

[quote]I suspect that what might be going wrong in your code is key repeat. It's been years and years since I bothered with Glut, but if I remember correctly by default it repeats keys. This means that it will send a sequence of keydown/keyup callbacks timed according to the timing of the system key repeat rate. Typically with games, you want to use glutIgnoreKeyRepeat so that keys won't be repeated and the callback for glutKeyboardFunc will store state for the key in a table. The callback for glutKeyboardUpFunc will clear that state. Then, in your update function, you query the state of this key table and move the camera accordingly.[/quote]

I checked some sources and read about this as well. Supposedly in glut (or freeglut to be more precise) you press the button and keep holding it you should have a repeating pattern; your keydown/keyup callbacks are repeatedly called as you wrote. Out of curiosity I wanted to confirm this so I simply placed code for printing out a message in both callback methods to see if those messages would be shown repeatedly as I held some defined key down. The result was that the keydown callback was called repeatedly, the keyup callback wasn't called until I released the button. Quite different from what I expected and have read about the callback methods repeating. Well as you said you haven't used GLUT for years so you might not care for this but I thought it was interesting to note anyway [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img] Edited by Suen

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this