Page 1 of 2

Changing Players Orientation per tick in UpdateMove()

Posted: Mon Aug 01, 2016 5:12 am
by JackStone
I am currently working on a spherical terrain implementation in T3D, which is going well, but I have hit a snag with the players movement. I have implemented 3D gravity according to an old resource that I found, and this works great, it pulls the player object towards the center of the world.

In update move ,I am adding the 3D gravity by doing this:

Code: Select all

VectorF a = terpos - getPosition(); a.normalize(); VectorF acc = a;
The problem here is that even though the player *moves* towards the terrain center, they are not oriented towards the terrain center. IE, their feet don't face the ground, which means they can't walk on the terrain unless the are standing at or very close to the north pole.

I have done some research into this, but I have not made a whole lot of progress.

There is some code in updatemove() that looks promising, such as:

Code: Select all

// If we don't have a runSurface but we do have a contactNormal, // then we are standing on something that is too steep. // Deflect the force of gravity by the normal so we slide. // We could also try aligning it to the runSurface instead, // but this seems to work well. if ( !runSurface && !contactNormal.isZero() ) acc = ( acc - 2 * contactNormal * mDot( acc, contactNormal ) );
What I want to do, basically, is alight the players position with the contactNormal, is this correct? How would this be done?


Re: Changing Players Orientation per tick in UpdateMove()

Posted: Mon Aug 01, 2016 10:29 am
by irei1as
Sadly, inside the class Player there is a lot of code that implies that the orientation will be always feet towards -z direction (head goes to +z).

This awesome resource:
lets you rotate freely but it needs a few changes as it's a bit outdated (well, as you use a custom gravity you have to program changes, anyway).

Also, I heavily suggest you to make a copy of your player files before doing the code changes. You may need to go back to the previous player code if anything goes wrong.

Re: Changing Players Orientation per tick in UpdateMove()

Posted: Mon Aug 01, 2016 6:39 pm
by JackStone
Yes, I do see a lot of that code in there.

I have actually implemented that resource, and it does seem to be working. I had forgotten that, I just realised now after looking at that link.

It seems then that all I need to do is use the mOrient from that resource to change the orientation of the player in updatemove?

I'm not sure how to do that though, any ideas?

Re: Changing Players Orientation per tick in UpdateMove()

Posted: Mon Aug 01, 2016 10:59 pm
by JackStone
I have made some progress with this.

If I add this to updatemove():

Code: Select all

QuatF q = QuatF(0, 0, 1, 0); mOrient = q;
I can change the orientation of the player. I just need to know what to set mOrient to in order to face the center of the terrain.

Re: Changing Players Orientation per tick in UpdateMove()

Posted: Tue Aug 02, 2016 3:43 am
by TorqueFan
Not sure if it'd be valid for your implementation, but could you just grab the normal of the surface the player is standing on? Then you could adjust your player's mOrient value based off of that.

Re: Changing Players Orientation per tick in UpdateMove()

Posted: Tue Aug 02, 2016 4:34 am
by JackStone
This would be ideal, but I'm not sure how to grab that normal?

The other thing, is that I'm not sure how to align the player to face a particular normal. I am doing this at the moment:

Code: Select all

Point3F gravityvec = teraincenterpos - getPosition(); gravityvec.normalize(); QuatF q = QuatF(gravityvec); mOrient = q;
It seems to be close to working, but it doesn't work on all cases.

Re: Changing Players Orientation per tick in UpdateMove()

Posted: Wed Aug 03, 2016 12:59 am
by TorqueFan
Luckily for you, the updateMove() function in player.cpp already does this for you:

Code: Select all

VectorF contactNormal(0,0,0); bool jumpSurface = false, runSurface = false; if ( !isMounted() ) findContact( &runSurface, &jumpSurface, &contactNormal ); if ( jumpSurface ) mJumpSurfaceNormal = contactNormal;
This nets you a VectorF variable with the surface normal data for whatever your player is standing on.

You can follow the trail to the findContact() and _findContact() functions in the same player.cpp file.

Another approach that I often use is to get ahold of the normal data using a raycast. Raycasts will return the normal as part of the collision struct. This can even be done in script, although for something like updating player movement it's best kept here in the source. I give mention to this approach because, believe it or not, it is used already for footprints and footstep sounds.

Re: Changing Players Orientation per tick in UpdateMove()

Posted: Thu Aug 04, 2016 1:19 am
by JackStone
Thank, you this seems to have helped me a little. I had seen that code before, but hadn't used it, since I was working on a system using a vector from the players position to the terrain center.

I have replaced my spherical terrain with a simple cube for testing, and I can obtain a contact normal, of the form: "1,0,1" with the player standing on one of the faces.

However, I now need to convert this VectorF into a QuatF.

I tried just setting the quatf to the contactnormal, which compiled, but doesn't work. There is obviously a trick to this that I am not getting.

Re: Changing Players Orientation per tick in UpdateMove()

Posted: Thu Aug 04, 2016 2:53 am
by JackStone
Ok, so I think I have reduced this to a simple test case.

When the player is standing on top of a cube, the contact normal is 0,0,1, and the quaternion should be QuatF(Point3F(0,0,0)) for the to be correctly rotated.

When the stand on the side of the cube, facing the negative x axis, the contact normal is (-1,0,0), which it should be. The quaternion here, for correct facing, should be: QuatF(Point3F(0,1.57,0)).

Which is basically a 90 degree rotation along y. The question is, how do I mathematically derive that quaternion from the contact normal and the player position, etc, alone?

Re: Changing Players Orientation per tick in UpdateMove()

Posted: Thu Aug 04, 2016 9:35 am
by irei1as
Quaternion math is not the easiest thing. You shouldn't think of them as a fancy vector as quaternions "kinda" have 4 dimensions (direction and a rotation in that direction). They're a transformation so the up vector is defined by something like "Quat.mul(0,0,1)".

Well, just let me share the code I used when I was playing with the "mOrient-resource". Feel free to ask anything if the comments are not enough.

In the .h file inside the class (I used it in "public:") add:

Code: Select all

void doLocalRotateX(const F32& rotX , QuatF* qchanged ); void doLocalRotateZ(const F32& rotZ , QuatF* qchanged ); void doQuatOrientation( const Point3F &neworient );
In the .cpp file add at the end (for example):

Code: Select all

void Player::doLocalRotateX(const F32& rotX, QuatF* qchanged ) { QuatF tempq(qchanged->x,qchanged->y,qchanged->z,qchanged->w); if(!mIsZero(rotX)) { Point3F localX; tempq.mulP(Point3F(1.0,0.0,0.0),&localX); QuatF qx(localX,rotX); tempq *= qx; } tempq.normalize(); qchanged->set(tempq.x,tempq.y,tempq.z,tempq.w); } void Player::doLocalRotateZ(const F32& rotZ, QuatF* qchanged ) { QuatF tempq(qchanged->x,qchanged->y,qchanged->z,qchanged->w); if(!mIsZero(rotZ)) { Point3F localZ; tempq.mulP(Point3F(0.0,0.0,1.0),&localZ); QuatF qz(localZ,rotZ); tempq *= qz; } tempq.normalize(); qchanged->set(tempq.x,tempq.y,tempq.z,tempq.w); } void Player::doQuatOrientation( const Point3F &neworient ) { //we will not modify mOrient untill the end QuatF tempmOrient = mOrient; tempmOrient.normalize(); tempmOrient.inverse(); //we're going global->local so we need the inverse of mOrient Point3F vect=neworient; //we need to normalize neworient vect.normalize(); Point3F localvect; tempmOrient.mulP(vect,&localvect); localvect.normalize(); //this normalize may not be needed... but just in case tempmOrient = mOrient; //we need back normalized mOrient to do the transformations tempmOrient.normalize(); //localvect is our wanted neworient in local coordinates //x and y values are the projections of localvect in the z=0 plane //to know that angle (that is the rotation on the z axis) we can use then the arctangent F32 rotZ = mAtan2(localvect.x,localvect.y); doLocalRotateZ(rotZ,&tempmOrient); //we rotate around our local z axis so we face our destination (y axis aligned) //so we only need a final rotation with our local x axis //rotation on x axis is similar to z but the plane is x=0 F32 rotX = mAtan2(mSqrt(localvect.x*localvect.x + localvect.y*localvect.y),localvect.z); doLocalRotateX(rotX,&tempmOrient); //now we undo the initial rotZ //This way the initial local-z rotation is similar... I think doLocalRotateZ((-1)*rotZ,&tempmOrient); //finally we transform mOrient mOrient.set(tempmOrient.x,tempmOrient.y,tempmOrient.z,tempmOrient.w); }
(Change "Player::" with the name of your particular class, of course.)

Then in order to align the mOrient to a "vectorUp" you just do in the c++ code:

Code: Select all

where vectorUp is the new orientation.

You can see I normalize() a lot... maybe too much. Feel free to remove all of the normalize() you think are not needed.

Note I use up as vector. If you're using gravity(going down) then remember to use gravity.neg() or you'll "walk" on your head.

As final note let me share that using the normal as up vector is not a very good idea if you use the default camera of player. Well, it's all right if the terrain is a plane but if it's kind of bumpy like the terrain of the full template then the camera movements are unpleasant when you run.