Ok, I started to refactore the demo to work in core 300, and I found some obvious problems in your demo. I am surprise it even work as well as it does.
The first problem is that the puck shape is a cylinder, however cylinder defined alone the x axis, that would be OK if you use the inertia matrix calculator. but you calculate yourself as if the axis was alone Y
The make a irregular body with and inertia matrix that does no math the collision shape, This is not wrong, the body is actually behaving as it should, but the visual representation do no match the physics.
The problem is that since the inertia is asymmetric the solver will distribute the forces and torques according to eth mass distributions, so after each step the sum of all torque will not be zero.
To solve that it is better the pass a offset matrix when you create the cylinder.
I did that and it work much better.
The second problem is the rounding errors, I was mention before, The cylinder has more contact the the minimum necessary to find a optimal solution.
I mention that we can remove contacts, to make it better but I also mention that you should align the friction tangent with the direction of the table.
I thought were doing that and it was still deviating to the side, but when I saw the contact call back you were only making the friction zero.
This is theoretically correct but In the Newton engine that is no who you make a collision frictionless.
if you look at function NewtonMaterialSetContactFrictionCoef you will see that the minimun friction value is clamped to 0.01, This In the Newton engine you cannot have a constraint with zero force,
The result of this is that there will be some residual side forces that will rotate push the puck to the side just a little in each step. Thsi samll force when acting for long time, is enought to make it deviate significantly from the trajectory.
Even with those ill setup, the deviation should not be as severe as I see in the demo. I check the contact call back I see that you do not align the tangent friction to the table direction.
I saw that you tryed to do it but it was not right, I simple added that call and now the deviation is less than one part in 1 thousand which is well inside of 32 bit float accuracy.
The deviation cannot be zero because as I mention before you are no making the motion over the table friction less, the tangent friction have a tiny values, this make the solver work with large number and small number and the and 32 bit float lose Precision.
If you want perfect frictionless motion you need to make the material between the table and the Puck frictionless using the function in the call back
NewtonMaterialSetContactFrictionState (material, 0, 0);
NewtonMaterialSetContactFrictionState (material, 0, 1);
This will remove the friction row for the solver. I did not do that but you should try and see how it works
Here is the code with the changes I mention please see if it works now.
- Code: Select all
/* Copyright (c) <2009> <Newton Game Dynamics>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely
*/
#include <toolbox_stdafx.h>
#include "../DemoEntityManager.h"
#include "../DemoCamera.h"
#include "PhysicsUtils.h"
#include "../DemoMesh.h"
#include "../toolBox/OpenGlUtil.h"
#define PHYSICS_WORLD_SCALE 50.0f // conversion from meters to world units
#define TABLE_WIDTH (0.508f * PHYSICS_WORLD_SCALE) // 20 inches
#define TABLE_LENGTH (5.08f * PHYSICS_WORLD_SCALE) // 200 inches (16' 8")
#define TABLE_HEIGHT (0.0762f * PHYSICS_WORLD_SCALE) // 3 inches
#define WEIGHT_DIAMETER (0.062f * PHYSICS_WORLD_SCALE)
#define WEIGHT_RADIUS (WEIGHT_DIAMETER * 0.5f)
#define WEIGHT_HEIGHT (0.025f * PHYSICS_WORLD_SCALE)
#define WEIGHT_MASS 0.360f // weight of a puck in Kg (mass doesn't need scaling)
#define CAMERA_X (-11.0f)
#define CAMERA_Y (0.26f * PHYSICS_WORLD_SCALE)
#define CAMERA_Z (-3.0f * PHYSICS_WORLD_SCALE)
// Material data
enum eSBMaterials
{
SBMaterial_WEIGHT,
SBMaterial_SURFACE,
SB_NUM_MATERIALS
};
NewtonBody* gPuckBody = NULL;
NewtonWorld* gWorld = NULL;
static void NewtonRigidBodySetForceCB(const NewtonBody* const body, float timestep, int threadIndex);
static void PhysicsNewton_CollisionPuckSurfaceCB(const NewtonJoint *pContactJoint,dFloat fTimeStep,int ThreadIndex);
static void RenderBodyContactsAndTangentDiretions (NewtonBody* const body, float length);
// create physics scene
void PuckSlide (DemoEntityManager* const scene)
{
NewtonWorld* const world = scene->GetNewton();
gWorld = world;
int materialGroupIDs[SB_NUM_MATERIALS];
// Create groups
for (int i = 0; i < SB_NUM_MATERIALS; i++)
{
materialGroupIDs[i] = NewtonMaterialCreateGroupID(world);
}
// Setup the material data
NewtonMaterialSetDefaultSoftness(world, materialGroupIDs[SBMaterial_WEIGHT], materialGroupIDs[SBMaterial_SURFACE], 0.15f);
NewtonMaterialSetDefaultElasticity(world, materialGroupIDs[SBMaterial_WEIGHT], materialGroupIDs[SBMaterial_SURFACE], 0.30f);
NewtonMaterialSetDefaultFriction(world, materialGroupIDs[SBMaterial_WEIGHT], materialGroupIDs[SBMaterial_SURFACE], 0.05f, 0.04f);
// setup callbacks for collisions between two material groups
NewtonMaterialSetCollisionCallback(world,materialGroupIDs[SBMaterial_WEIGHT],materialGroupIDs[SBMaterial_SURFACE],NULL,NULL,PhysicsNewton_CollisionPuckSurfaceCB);
///////
// Add table
{
dVector tableSize(TABLE_LENGTH, TABLE_HEIGHT, TABLE_WIDTH, 0.0f);
// create the shape and visual mesh as a common data to be re used
NewtonCollision* const collision = CreateConvexCollision (world, GetIdentityMatrix(), tableSize, _BOX_PRIMITIVE, materialGroupIDs[SBMaterial_SURFACE]);
DemoMesh* const geometry = new DemoMesh("cylinder_1", collision, "wood_3.tga", "wood_3.tga", "wood_3.tga");
dMatrix matrix = GetIdentityMatrix();
matrix.m_posit.m_x = 0.0f;
matrix.m_posit.m_z = 0.0f;
matrix.m_posit.m_y = 0.0f;
NewtonBody *tableBody = CreateSimpleSolid (scene, geometry, 0.0, matrix, collision, materialGroupIDs[SBMaterial_SURFACE]);
NewtonBodySetMassMatrix(tableBody, 0.0f, 1.0f, 1.0f, 1.0f);
NewtonBodySetMaterialGroupID(tableBody, materialGroupIDs[SBMaterial_SURFACE]);
NewtonBodySetContinuousCollisionMode(tableBody, 1);
// do not forget to release the assets
geometry->Release();
// NewtonDestroyCollision (collision);
}
///////
// Add puck
{
dVector puckSize(WEIGHT_DIAMETER, WEIGHT_HEIGHT, 0.0f, 0.0f);
// create the shape and visual mesh as a common data to be re used
dMatrix offsetMatrix = dRollMatrix(3.141592f/2.0f);
//NewtonCollision* const collision = CreateConvexCollision (world, GetIdentityMatrix(), puckSize, _CYLINDER_PRIMITIVE, materialGroupIDs[SBMaterial_WEIGHT], &offsetMatrix[0][0]);
NewtonCollision* const collision = NewtonCreateCylinder (world, puckSize.m_x * 0.5f, puckSize.m_y, materialGroupIDs[SBMaterial_WEIGHT], &offsetMatrix[0][0]);
DemoMesh* const geometry = new DemoMesh("cylinder_1", collision, "smilli.tga", "smilli.tga", "smilli.tga");
//dMatrix matrix = dRollMatrix(3.141592f/2.0f);
dMatrix matrix (GetIdentityMatrix());
matrix.m_posit.m_x = -TABLE_LENGTH*0.5f+WEIGHT_DIAMETER;
matrix.m_posit.m_z = -11.8f;
matrix.m_posit.m_y = 5.0f;
gPuckBody = CreateSimpleSolid (scene, geometry, WEIGHT_MASS, matrix, collision, materialGroupIDs[SBMaterial_WEIGHT]);
// Set moment of inertia
dVector I;
float Mass = WEIGHT_MASS;
float Radius = WEIGHT_RADIUS;
float Height = WEIGHT_HEIGHT;
I.m_x = I.m_z = Mass*(3.0f*Radius*Radius+Height*Height)/12.0f;
I.m_y = Mass*Radius*Radius/2.0f;
NewtonBodySetMassMatrix(gPuckBody,Mass, I.m_x, I.m_y, I.m_z);
NewtonBodySetMaterialGroupID(gPuckBody, materialGroupIDs[SBMaterial_WEIGHT]);
NewtonBodySetContinuousCollisionMode(gPuckBody, 1);
NewtonBodySetAutoSleep(gPuckBody, 1);
// Set callbacks
NewtonBodySetForceAndTorqueCallback(gPuckBody, NewtonRigidBodySetForceCB);
// do not forget to release the assets
geometry->Release();
// NewtonDestroyCollision (collision);
}
// place camera into position
dMatrix camMatrix (dPitchMatrix(20.0f * 3.1416f /180.0f));
dQuaternion rot (camMatrix);
dVector origin (CAMERA_Z, CAMERA_Y, CAMERA_X, 0.0f);
scene->SetCameraMatrix(rot, origin);
}
void NewtonRigidBodySetForceCB(const NewtonBody* const body, float timestep, int threadIndex)
{
float mass;
float Ixx;
float Iyy;
float Izz;
NewtonBodyGetMassMatrix(body, &mass, &Ixx, &Iyy, &Izz);
float force[3];
force[0] = 0.0f;
force[1] = mass * (-9.81f * PHYSICS_WORLD_SCALE);
force[2] = 0.0f;
NewtonBodySetForce(body, force);
}
static void PhysicsNewton_CollisionPuckSurfaceCB(const NewtonJoint *pContactJoint,dFloat fTimeStep,int ThreadIndex)
{
dVector Position;
// Get pointer to body
NewtonBody* body = NewtonJointGetBody0(pContactJoint);
float mass, Ixx, Iyy, Izz;
NewtonBodyGetMassMatrix(body, &mass, &Ixx, &Iyy, &Izz);
if (mass == 0.0f)
{
body = NewtonJointGetBody1(pContactJoint);
}
// Test to see if it is the friction calculation that is causing the side force
// With this the Puck must go straight because it will be frictionless, the net force should not change direction
dVector tableDir(0.0f, 0.0f, 1.0f, 0.0f);
for (void* contact = NewtonContactJointGetFirstContact (pContactJoint); contact; contact = NewtonContactJointGetNextContact (pContactJoint, contact))
{
NewtonMaterial* const material = NewtonContactGetMaterial (contact);
// align fiction tangent to the puck motion
NewtonMaterialContactRotateTangentDirections(material, &tableDir[0]);
// uncoimen thsi to see ho eth tange are rotated
//dVector dir0;
//dVector dir1;
//NewtonMaterialGetContactTangentDirections (material, body, &dir0[0], &dir1[0]);
NewtonMaterialSetContactFrictionCoef (material, 0.0f, 0.0f, 0);
NewtonMaterialSetContactFrictionCoef (material, 0.0f, 0.0f, 1);
}
}
void LaunchPuck()
{
static bool bLaunched = false;
if (bLaunched)
return;
if (gPuckBody)
{
bLaunched = true;
NewtonInvalidateCache (gWorld);
dVector zeros(0.0f, 0.0f, 0.0f, 0.0f);
NewtonBodySetVelocity(gPuckBody, &zeros.m_x);
NewtonBodySetOmega(gPuckBody, &zeros.m_x);
NewtonBodySetForce(gPuckBody, &zeros.m_x);
NewtonBodySetTorque(gPuckBody, &zeros.m_x);
dVector vel(171.299469f, 0.0f, 0.0f);
NewtonBodySetVelocity(gPuckBody, &vel.m_x);
}
}
void UpdateStuff(DemoEntityManager *scene)
{
if (gPuckBody)
{
int sleepState = NewtonBodyGetSleepState(gPuckBody);
if (sleepState)
{
LaunchPuck();
}
// Update the camera to follow puck
dMatrix rotMatrix (dPitchMatrix(20.0f * 3.1416f /180.0f));
dQuaternion rot (rotMatrix);
DemoEntity* demoEntity = (DemoEntity*)NewtonBodyGetUserData (gPuckBody);
float camZ = demoEntity->GetCurrentMatrix().m_posit.m_x - 30.0f;
DemoCamera* demoCamera = scene->GetCamera___();
dMatrix camMatrix = demoCamera->GetNextMatrix();
camMatrix.m_posit.m_x = camZ;
demoCamera->SetMatrix(*scene, rot, camMatrix.m_posit);
}
}
Later I will port this demo to core 300, so tha you can see how you cn amigrate to the new version, There I will try to do the ccontact reduction I mention before if it is needed, But I thonk it will no be nesssary
I hope this solve those problems.