Page 1 of 2

player body physic with newton

Posted: Thu Dec 23, 2004 10:16 pm
by Cairon
Hello !

I'm developing a multiplayer first person shooter game with irrlicht for graphics, newton for physics, and raknet for networking.

It's quite usable now, and i want to implement better player physics. before, i used the collision response animator for the gravity and collision (createCollisionResponseAnimator). now i want to handle the player as an NewtonBox with physics, so that f.e. my player will push the other physic objects away when i run against them.

I did this by creating the body like this:

Code: Select all

const float x = 10.0f, y = 10.0f, z = 10.0f;

NewtonCollision* collision = NewtonCreateBox(nWorld, x,y,z, NULL);
body = NewtonCreateBody(nWorld, collision);
NewtonBodySetMaterialGroupID (body, m_metalMaterial);
NewtonBodySetUserData(body, Game->camera);

float mass = 10.0f;
float Ixx = 0.7f * mass * (y * y + z * z) / 12.0f;
float Iyy = 0.7f * mass * (x * x + z * z) / 12.0f;
float Izz = 0.7f * mass * (x * x + y * y) / 12.0f;
NewtonBodySetMassMatrix (body, mass, Ixx, Iyy, Izz);

// Set callback functions for the body
NewtonBodySetTransformCallback(body, SetMeshTransformEventPlayer);
NewtonBodySetForceAndTorqueCallback(body, CubeApplyForceAndTorqueEvent);
NewtonBodySetAutoFreeze(body, 0);
// Set the position of the body
core::matrix4 mat;
mat.setTranslation(Game->camera->getPosition() * IrrToNewton);
NewtonBodySetMatrix(body, &mat.M[0]);
SetMeshTransformEventPlayer (body, &mat.M[0]);

My Callback function for handling the transform callback:

Code: Select all

void SetMeshTransformEventPlayer(const NewtonBody* body, const float* matrix)
{
	// copy the matrix into an irrlicht matrix4
	core::matrix4 mat;
	memcpy(mat.M, matrix, sizeof(float)*16);

	// Retreive the user data attached to the newton body
	scene::ISceneNode *tmp = (scene::ISceneNode *)NewtonBodyGetUserData(body);
	if (tmp)
	{
		// Position the node
		tmp->setPosition(mat.getTranslation() * NewtonToIrr);		// set position
	}
}
My first question: Is my attempt to implement this correct?
If not, how should i do this? I see some problems, f.e. the player body will be rotatet when it falls, can i set the rotation just on every TransformCallback ?

Now i want to move the box, according to the key input.

To test this, i tried:

Code: Select all

if ( App->Keys['W'] )
{
	core::vector3df v(10.0f,0.0f,0.0f);
	NewtonBodySetForce(Player->body, &v.X );
}
But this code seems to have no effect. Which function do I have to use?

NewtonBodyAddForce()
NewtonBodySetForce()
NewtonBodySetVelocity()

Another problem I don't know how to solve is, when i want to create my bullets f.e. of a m16 as models, and with physics. Do I have to create the NewtonBox or Sphere the normal way, and add a high velocity in the direction the bullet should fly to? Or should i use a normal flyanimator and update the physics position every frame?

I hope somebody can help me :-)
Thanks!!!!

BTW: I want to release a playable beta version in a few weeks, and I will release everything opensource.

Posted: Thu Dec 23, 2004 11:09 pm
by Spintz
2 things...

First off, after this line -

Code: Select all

NewtonBodySetAutoFreeze(body, 0);
Add the following line -

Code: Select all

NewtonWorldUnfreezeBody ( nWorld, body );
Secondly, you can only apply a force to a rigid body in the newton world with NewtonBodySetForce, NewotnBodyAddForce or other such functions in the NewtonApplyForceAndTorque callback. So what you need to do, is to set a force vector based on keys being pressed, and then apply that force vector, in the callback you registered with the NewtonBodySetForceAndTorqueCallback fucntion.

( look at the file C:\Program Files\NewtonSDK\doc\Newton.htm and it will tell you in the Remarks for those function something like - This function is only effective when called from NewtonApplyForceAndTorque callback )

Now, as far as whether to use a flyanimator or to implement Continuous Collision Mode for a body in the Newton world with NewtonBodySetContinuousCollisionMode, I cannot say. I would test them both and see which is better. My first impression would be to attempt to get the Physics engine to do the work for you.

Hope I've helped. I've been working vigorously with many aspects of the Newton engine as of late, so ask away ( I may have some questions for you as well! ;) )



This will allow your object to move, or should. It also depends on how the materials are setup, how much gravity you have

Posted: Fri Dec 24, 2004 12:01 am
by Cairon
Thanks Spintz, based on your tipps i've changed my code:

(SetMeshTransformEventPlayer stayed the same)

Code: Select all

		NewtonBodySetTransformCallback(body, SetMeshTransformEventPlayer);
		NewtonBodySetForceAndTorqueCallback(body, CubeApplyForceAndTorqueEventPlayer);
		NewtonBodySetAutoFreeze(body, 0);
		NewtonWorldUnfreezeBody ( nWorld, body );

Code: Select all

void CubeApplyForceAndTorqueEventPlayer (const NewtonBody* body) 
{ 
   // add normal force	
   ApplyForceAndTorqueEventPlayer (body);

   // see if this body is under water // not yet
   // ApplyBouyancyForcePlayer (body);
 }

Code: Select all

void ApplyForceAndTorqueEventPlayer (const NewtonBody* body) 
{ 
	float mass; 
	float Ixx; 
	float Iyy; 
	float Izz; 

	scene::ICameraSceneNode *tmp = (scene::ICameraSceneNode *)NewtonBodyGetUserData(body);

	NewtonBodyGetMassMatrix (body, &mass, &Ixx, &Iyy, &Izz); 

	core::vector3df gravity_force (0.0f * IrrToNewton, NEWTON_GRAVITY * mass * IrrToNewton, 0.0f * IrrToNewton); 

	core::vector3df move_dir = tmp->getTarget();
	move_dir.normalize();

	core::vector3df move_force(0,0,0);

	f32 MoveSpeed = 100.0f;
	if ( App->Keys[0xA0] )
		MoveSpeed *= 2.0f;

	if (App->Keys['W'])
		move_force += move_dir * (f32) App->elapsedTime * MoveSpeed;
	if (App->Keys['S'])
		move_force -= move_dir * (f32) App->elapsedTime * MoveSpeed;

	core::vector3df strafevect = tmp->getTarget();
	strafevect = strafevect.crossProduct(core::vector3df(0,1.0f,0));
	strafevect.normalize();

	if (App->Keys['A'])
		move_force += strafevect * (f32) App->elapsedTime * MoveSpeed;
	if (App->Keys['D'])
		move_force -= strafevect * (f32) App->elapsedTime * MoveSpeed;

	move_force *= IrrToNewton;

	core::vector3df torque (0.0f * IrrToNewton, 0.0f * IrrToNewton, 0.0f * IrrToNewton);  

	NewtonBodyAddForce (body, &gravity_force.X); 
	NewtonBodyAddForce (body, &move_force.X); 
	NewtonBodyAddTorque (body, &torque.X); 
}
The problem is now, that still nothing happends when i try to move. Any ideas?

Posted: Fri Dec 24, 2004 12:06 am
by Cairon
could i do

Code: Select all

	core::vector3df torque (0.0f * IrrToNewton, 0.0f * IrrToNewton, 0.0f * IrrToNewton);  

	NewtonBodyAddForce (body, &gravity_force.X); 
	NewtonBodyAddForce (body, &move_force.X); 
	//NewtonBodyAddTorque (body, &torque.X); 

	NewtonBodySetTorque(body, &torque.X)
to fix the player box in rotation and torque?

Posted: Fri Dec 24, 2004 2:58 am
by Spintz
Ok, well a couple things, again... ;)

You apparently don't want the node to rotate ( you're applying a 0 vector to the torque ), just don't even call NewtonBodySetTorque.

To get the direction the camera is facing, you want to subtract the target from the camera's position and then get the normal - here's a function I use to recalculate my forward, up and right vectors after movement -

Code: Select all

void RecalcWorldVectors ( )
{
	// Compute new forward vector (line of sight vector)
	forwardVector = camera->getTarget ( ) - camera->getPosition ( );
	// Not concerned with the Y Axis for character moving on ground.  If doing a spaceship or flying object, you would be concerned with the Y value of the forward vector.
	m_forwardVector.Y = 0.0f;
	m_forwardVector.normalize ( );
	
	// Compute new right vector, WORLDUPVECTOR is ( 0.0f, 1.0f, 0.0f )
	rightVector = WORLDUPVECTOR.crossProduct ( forwardVector );
	rightVector.normalize ( );
	
	// Compute new camera up vector
	upVector = forwardVector.crossProduct ( rightVector );
	upVector.normalize ( );
}
Ummmm, what are the properties of your material?

As a side not, a more appropriate calculation for a box's moment of inertia is -
Ixx = 0.83f * mass * ( height * height + length * length );
Iyy = 0.83f * mass * ( width * width + length * length );
Izz = 0.83f * mass * ( height * heigt + width * width );
( I got these values from the book, Physics for Game Developers by David M. Bourg )

I can't think of anything else at this point, or don't see it, try and fix those things and let me know what happens. If you want, you can email me and I'll take a look at your whole code and be able to help better. I could also show you a bunch of code how I do it, here's an AVI of me using Newton in my, we'll call it a tech demo, it's not near what my game is going to be....

http://www.spintz.com/mygame/mygamedemo.avi

Just so you can see, I've got the newton stuff working pretty well. Only thing is, for my character movement, I don't use Forces, I just set the velocity in either the rightVector or forwardVector's direction. Still working on getting forces to create the type of movement I want. I'm looking into something about inverse kinematics or something, but setting the velocity seems to work just fine for now.

Posted: Sun Dec 26, 2004 9:23 pm
by Cairon
I've changed my code for using the NewtonBodySetVelocity function instead of using forces. The result is quite strange, it's nearly working, it does not move in the right direction. Maybe somebody knows what could be wrong, i don't know what I could do.

I run this in my frame update function:

Code: Select all

		core::vector3df move_dir = camera->getTarget() - camera->getPosition();
		move_dir.Y = 0.0f;
		move_dir.normalize();

		core::vector3df move_force(0,0,0);

		f32 MoveSpeed = 50.0f;
		if ( App->Keys[0xA0] ) // double speed on LSHIFT
			MoveSpeed *= 2.0f;

		if (App->Keys[Config->key_moveforward[0]] || App->Keys[Config->key_moveforward[1]])
			move_force += move_dir * (f32) App->elapsedTime * MoveSpeed;
		if (App->Keys[Config->key_movebackward[0]] || App->Keys[Config->key_movebackward[1]])
			move_force -= move_dir * (f32) App->elapsedTime * MoveSpeed;

		core::vector3df strafevect = camera->getTarget() - camera->getPosition();
		strafevect = strafevect.crossProduct(core::vector3df(0,1.0f,0));
		strafevect.Y = 0.0f;
		strafevect.normalize();

		if (App->Keys[Config->key_moveleft[0]] || App->Keys[Config->key_moveleft[1]])
			move_force += strafevect * (f32) App->elapsedTime * MoveSpeed;
		if (App->Keys[Config->key_moveright[0]] || App->Keys[Config->key_moveright[1]])
			move_force -= strafevect * (f32) App->elapsedTime * MoveSpeed;

		move_force *= IrrToNewton;
		
		NewtonBodySetVelocity(Player->body, &move_force.X);
I do now create my player model with this code:

Code: Select all

		const float x = 10.0f, y = 10.0f, z = 10.0f;

		NewtonCollision* collision = NewtonCreateBox(nWorld, x,y,z, NULL);
		body = NewtonCreateBody(nWorld, collision);
		NewtonBodySetMaterialGroupID (body, m_metalMaterial);
		NewtonBodySetUserData(body, Game->camera);

		// Set body mass & inertia matrix
		float mass = 100.0f;
		float Ixx = 0.83f * mass * (y * y + z * z);
		float Iyy = 0.83f * mass * (x * x + z * z);
		float Izz = 0.83f * mass * (x * x + y * y);
		NewtonBodySetMassMatrix (body, mass, Ixx, Iyy, Izz);

		// Set callback functions for the body
		NewtonBodySetTransformCallback(body, SetMeshTransformEventPlayer);
		NewtonBodySetForceAndTorqueCallback(body, CubeApplyForceAndTorqueEventPlayer);
		NewtonBodySetAutoFreeze(body, 0);
		NewtonWorldUnfreezeBody (nWorld, body);
		// Set the position of the body
		core::matrix4 mat;
		mat.setTranslation(Game->camera->getPosition() * IrrToNewton);
		NewtonBodySetMatrix(body, &mat.M[0]);
		SetMeshTransformEventPlayer (body, &mat.M[0]);
My Callback functions are:

Code: Select all

void ApplyForceAndTorqueEventPlayer (const NewtonBody* body) 
{ 
	float mass; 
	float Ixx; 
	float Iyy; 
	float Izz; 

	scene::ICameraSceneNode *tmp = (scene::ICameraSceneNode *)NewtonBodyGetUserData(body);

	NewtonBodyGetMassMatrix (body, &mass, &Ixx, &Iyy, &Izz); 

	core::vector3df gravity_force (0.0f * IrrToNewton, 100.0f * NEWTON_GRAVITY * mass * IrrToNewton, 0.0f * IrrToNewton); 
	core::vector3df torque (0.0f * IrrToNewton, 0.0f * IrrToNewton, 0.0f * IrrToNewton);  

	NewtonBodyAddForce(body, &gravity_force.X); 
	NewtonBodyAddTorque(body, &torque.X);
}

void SetMeshTransformEventPlayer(const NewtonBody* body, const float* matrix)
{
	// copy the matrix into an irrlicht matrix4
	core::matrix4 mat;
	memcpy(mat.M, matrix, sizeof(float)*16);

	// Retreive the user data attached to the newton body
	scene::ICameraSceneNode *tmp = (scene::ICameraSceneNode *)NewtonBodyGetUserData(body);
	if (tmp)
	{
		// Position the node
		tmp->setPosition(mat.getTranslation() * NewtonToIrr);		// set position
	}
}
I'm sorry for posting so much code :-)
I think I've done it principly right, or is it wrong to set the velocity of the player every frame?

Merry Chrismas !

Posted: Mon Dec 27, 2004 1:56 pm
by Electron
You should not be directly setting the velocity of newton regularly for anything. If you look on the Newton forums julio stresses this all the time. It introdces unlimited force into the world because it forces the body to keep moving at a set speed even if something representing a concrete wall were in its path. This can cause nasty explosions anf huge forces in weird directions. NEVER USE SET VELOCITY REGULARLY. (sorry for shouting :wink: ).

A couple other things: There is no point in calling NewtonBodyAddTorque if you are passing a 0 vector. There is also no point in multiplying 0.0f by your scale factor. IMHO it just makes things look more cluttered.

What I do for movement:
1) Player (and other beings) have a variable vector3df movevec. In my key handling code I set movevec to (0,0,1),(0,0,-1),(1,0,0),(-1,0,0) depending on which movement key was pressed. (0,0,0) if a movement key was let up.
2) in the force and torque callback I scale movevec by a movement speed constant and rotate it by my yaw angle.
3) I then set my force. I use NewtonBodySetForce to get greater control over exactly what force is being applied. I ignore torque because I finally decided the FPS camera gives much smoother rotation than trying to control the torque ever would.

Posted: Mon Dec 27, 2004 2:13 pm
by Spintz
Your strafevect should be set like so :

Code: Select all

core::vector3df WORLDUPVECTOR ( 0.0f, 1.0f, 0.0f );
strafevect = WORLDUPVECTOR.crossProduct ( move_dir );
strafevect.normalize ( );
Also, calling NewtonBodySetVelocity ( ... ), has no effect, unless called in the ForceAndTorqueEvent.

As far as the not using SetVelocity. This is true, if you want as real as possible Physics. In games/simulations you need to do whatever is necessary to get the behaviors you want. Julio gets very pissy when people want to use the Newton for things other than as real as possible physics. I'll tell you what, you show me how to realistically simulate humanoid movement, walking/running with applying forces and I'll use it. I haven't been able to do it yet, and when I ask over on the Newton forums, I get the old "just play with the material settings and force settings". It may be possible, but no one knows how to do it. The only samples I've seen with realistic physics movement in Newton, using forces, are vehicles and/or boxes.

I've got it !

Posted: Tue Dec 28, 2004 12:31 am
by Cairon
hi!

I've got it, (at last) working with forces. The problem i'm confronted now, is to choose the past primitive type for the player body. The sphere works best for me, but still my movement is not perfect, because it works only if i move forward. Strafing doesnt work at all, it moves very strangly, and moving backwards is to fast and i get a strange 'pinball effect'.

For everybody who wants who doesn't get it working, here is what i did:

In my player structure, i saved a pointer to the NewtonBody* body, and to the water body for swimming, and for a vector3df for the movement vector.

If got a player material, i'm experimenting with the different paramters, maybe someone knows better for this kind of material.

Code: Select all

int m_playerMaterial = NewtonMaterialCreateGroupID(nWorld);
	NewtonMaterialSetDefaultFriction   (nWorld, m_playerMaterial, m_playerMaterial, 10.0f, 10.0f);
	NewtonMaterialSetDefaultElasticity (nWorld, m_playerMaterial, m_playerMaterial, 0.0f);
	NewtonMaterialSetDefaultSoftness   (nWorld, m_playerMaterial, m_playerMaterial, 0.0f);
	NewtonMaterialSetCollisionCallback (nWorld, m_playerMaterial, m_playerMaterial, &player_metal, BeginContact, ProcesContact, FinishContact);
Here i'm applying my forces:

Code: Select all

void ApplyForceAndTorqueEventPlayer (const NewtonBody* body) 
{ 
	scene::ICameraSceneNode *tmp = (scene::ICameraSceneNode *)NewtonBodyGetUserData(body);

	core::vector3df gravity_force (0.0f * IrrToNewton, 10.0f * NEWTON_GRAVITY * IrrToNewton, 0.0f * IrrToNewton); 
	core::vector3df jump_force (0.0f, -NEWTON_GRAVITY * 2300.0f * IrrToNewton, 0);

	core::vector3df zero(0.0f,0.0f,0.0f);

	if ( Game->Player->doJump )
	{
		NewtonBodyAddForce(body, &jump_force.X);
		Game->Player->doJump = false;
	}
	if ( Game->Player->move_vec != zero)
	{
		NewtonBodyAddForce(body, &Game->Player->move_vec.X); 
	}
	//NewtonBodySetOmega(body, &zero.X);	
	//NewtonBodySetTorque(body, &zero.X);

	core::matrix4 mat;
	NewtonBodyGetMatrix(body, &mat.M[0]);
	mat.setRotationDegrees(zero);
	NewtonBodySetMatrix(body, &mat.M[0]);

	NewtonBodyAddForce(body, &gravity_force.X);
	
	NewtonBodySetAutoFreeze (body, 0);
	NewtonWorldUnfreezeBody (nWorld, body); 
}
Here's how i'm updating my movement vector. I've debugged the resulting values, and they seam to be correct.

Code: Select all

core::vector3df move_dir = camera->getTarget() - camera->getPosition();
move_dir.Y = 0.0f;
move_dir.normalize();
Game->Player->move_vec.set(0,0,0);
f32 MoveSpeed = 300.0f;
if ( App->Keys[0xA0] )
	MoveSpeed *= 2.0f;
if (App->Keys[Config->key_moveforward[0]] || App->Keys[Config->key_moveforward[1]])
	Game->Player->move_vec += move_dir * (f32) App->elapsedTime * MoveSpeed;
if (App->Keys[Config->key_movebackward[0]] || App->Keys[Config->key_movebackward[1]])
	Game->Player->move_vec -= move_dir * (f32) App->elapsedTime * MoveSpeed;

core::vector3df upvector = core::vector3df(0.0f, 1.0f, 0.0f);
core::vector3df strafevect = upvector.crossProduct(camera->getTarget() - camera->getPosition());
strafevect.Y = 0.0f;
strafevect.normalize();

if (App->Keys[Config->key_moveleft[0]] || App->Keys[Config->key_moveleft[1]])
	Game->Player->move_vec += strafevect * (f32) App->elapsedTime * MoveSpeed;
if (App->Keys[Config->key_moveright[0]] || App->Keys[Config->key_moveright[1]])
	Game->Player->move_vec -= strafevect * (f32) App->elapsedTime * MoveSpeed;

I'm creating my Player body like this:

Code: Select all

const float x = 0.75f, y = 1.0f, z = 0.75f;

NewtonCollision* collision = NewtonCreateSphere(nWorld, x,y,z, NULL);
body = NewtonCreateBody(nWorld, collision);
NewtonBodySetMaterialGroupID (body, m_playerMaterial);
NewtonBodySetUserData(body, Game->camera);

// Set body mass & inertia matrix
float mass = 50.0f;
float Ixx = 0.83f * mass * (y * y + z * z);
float Iyy = 0.83f * mass * (x * x + z * z);
float Izz = 0.83f * mass * (x * x + y * y);
NewtonBodySetMassMatrix (body, mass, Ixx, Iyy, Izz);

// Set callback functions for the body
NewtonBodySetTransformCallback(body, SetMeshTransformEventPlayer);
NewtonBodySetForceAndTorqueCallback(body, CubeApplyForceAndTorqueEventPlayer);
NewtonBodySetAutoFreeze(body, 0);
NewtonWorldUnfreezeBody (nWorld, body);
// Set the position of the body
core::matrix4 mat;
mat.setTranslation(Game->camera->getPosition() * IrrToNewton);
NewtonBodySetMatrix(body, &mat.M[0]);
SetMeshTransformEventPlayer (body, &mat.M[0]);

Posted: Tue Dec 28, 2004 1:29 pm
by Electron
why on earth do you turn off AutoFreexe and Unfreeze the body every single time you apply force. Unless you are freezing the body elsewhere in your code you can simply turn off auto freeze and unfreeze the body once. It will stay active forever.

Posted: Tue Dec 28, 2004 2:20 pm
by Spintz
Yeah, couple things, the maximum material value in Newton is 2.0 I believe, and it's been said that a good value to use for friction is around 1.0.

Also, yes, you do not need to keep unfreezing the body. If you set autofreeze off, then you control the freezing. If you wish to do so, you might want to freeze/unfreeze the body, but you do not have to keep setting the autofreeze value.

As far as the moments of inertia. They are different for a sphere. The MOI of a (solid) sphere is defined as 2/5 * Mass * Radius^2. So you need your radius in each X, Y and Z direction and then plug that in for MOIx, MOIy and MOIz respectively.

Also, you cannot call - SetMeshTransformEventPlayer (body, &mat.M[0]); - when you create the rigid body. Well you can, but that callback can only affect the newton world, if it's called by the newton world.

Everything else looks good.

Posted: Sat Jan 01, 2005 3:49 am
by Cairon
Hi everybody!

I just want to make a short update of what i have done until now, for those who want to implement it themself.

First,

core::matrix4 mat;
NewtonBodyGetMatrix(body, &mat.M[0]);
mat.setRotationDegrees(zero);
NewtonBodySetMatrix(body, &mat.M[0]);

that wasn't very good. The correct way to fix the rotation of the sphere, is to add a UpVector constraint. here's what to do:

core::vector3df up(0,1.0f, 0);
NewtonConstraintCreateUpVector(nWorld, &up.X, body);

>>Sprintz: Also, calling NewtonBodySetVelocity ( ... ), has no effect, unless called in the ForceAndTorqueEvent.

Thats simply not true :P

>>Sprintz: Also, you cannot call - SetMeshTransformEventPlayer (body, &mat.M[0]); - when you create the rigid body. Well you can, but that callback can only affect the newton world, if it's called by the newton world.

Well, the MeshTransformEvent does not change the newton world, its changing the irrlicht representation. I used this, because it was that way in the mercior tutorial on newton physics, which was my basecode for newton.

I'll create a project announcement in the forum, there you can download the sourcecode if you are interested in it.

http://irrlicht.sourceforge.net/phpBB2/ ... 8720#28720

If some people would like to have a tutorial on that topic, i would create one if there is some feedback.

Posted: Sun Jan 02, 2005 5:23 pm
by Ronin
I really would love to have a tutorial on implementing real looking physics with newton into irrlicht. And I'm sure, there are many other out there, which do also... :)

Posted: Sun Jan 02, 2005 5:36 pm
by Markk
WOW !!! great project !!! are you going to a section of tutorials about physics and multiplayer in your site ???

your project could be the best example for all people, thing in it

Posted: Sun Jan 02, 2005 5:36 pm
by Cairon
I would like to write one, could you, or anybody interested please post what you want to have explained in such a tutorial?