Using Newton in the Quake3 engine

A place to discuss everything related to Newton Dynamics.

Moderators: Sascha Willems, walaber

Using Newton in the Quake3 engine

Postby Xycaleth » Wed Jul 01, 2009 7:40 am

I'm currently trying to integrate Newton into a Quake3 engine based game mod - in particular for a Jedi Academy mod. But I'm having problems with the movement of the rigid bodies, and I'm not entirely sure I'm loading the BSP map surfaces correctly :P

Here's my map loading code (sorry if it's a bit long :D):
Code: Select all
static qboolean LoadSurfaces ( byte* base, lump_t* surfaces, lump_t* vertices, lump_t* bmodels )
{
    dsurface_t* surf = (dsurface_t*)(base + surfaces->fileofs);
    drawVert_t* vert = (drawVert_t*)(base + vertices->fileofs);
    dmodel_t* model = (dmodel_t*)(base + bmodels->fileofs);
   
    NewtonCollision* collision = NULL;
   
    int numSurfaces, numModels;
    int surfaceCount = 0, vertexCount = 0;
    int i, j;
    int stride;
    int limit;
   
    numSurfaces = surfaces->filelen / sizeof (*surf);
    numModels = bmodels->filelen / sizeof (*model);
    --numModels;
   
    if ( numModels > 0 )
    {
        ++model;
    }

    world = NewtonCreate (NULL, NULL);
   
    collision = NewtonCreateTreeCollision (world);
    NewtonTreeCollisionBeginBuild (collision);
   
    stride = sizeof (drawVert_t);
   
    for ( i = 0; i < numSurfaces; ++i, ++surf )
    {
        // IsModelSurface checks if the surface belongs to a map model.
        // I don't want these to be added to the collision tree.
        if ( IsModelSurface (model, numModels, i) )
        {
            continue;
        }
       
        qboolean flip = qfalse;
   
        switch ( surf->surfaceType )
        {
            case MST_TRIANGLE_SOUP:
            case MST_PLANAR:
                vec3_t v[3];
                limit = surf->numVerts - 2;
                for ( j = limit - 1; j >= 0; --j )
                //for ( j = 0; j < limit; ++j )
                {                   
                    if ( flip )
                    {
                        VectorCopy (vert[surf->firstVert + j + 2].xyz, v[0]);
                        VectorCopy (vert[surf->firstVert + j + 1].xyz, v[1]);
                        VectorCopy (vert[surf->firstVert + j + 0].xyz, v[2]);
                    }
                    else
                    {
                        VectorCopy (vert[surf->firstVert + j + 0].xyz, v[0]);
                        VectorCopy (vert[surf->firstVert + j + 1].xyz, v[1]);
                        VectorCopy (vert[surf->firstVert + j + 2].xyz, v[2]);
                    }

                    NewtonTreeCollisionAddFace (collision, 3, &v[0][0], sizeof (vec3_t), 1);
                    flip = (qboolean)!flip;
                }

                vertexCount += surf->numVerts;
                ++surfaceCount;
            break;
           
            case MST_PATCH:
                // Haven't done this bit yet.
            break;
           
            case MST_FLARE:
                // This is a billboard. Don't need to worry about collisions with these.
            break;
           
            default:
            break;
        }
    }
       
    NewtonTreeCollisionEndBuild (collision, 1);
   
    G_Printf ("...%d surfaces loaded, %d vertices total\n", surfaceCount, vertexCount);
   
    worldBody = NewtonCreateBody (world, collision);
    NewtonReleaseCollision (world, collision);
   
    worldMatrix = GetIdentityMatrix();
    NewtonBodySetMatrix (worldBody, &worldMatrix[0][0]);
   
    vec3_t max;
    vec3_t min;
    NewtonCollisionCalculateAABB (collision, &worldMatrix[0][0], &min[0], &max[0]);
    NewtonSetWorldSize (world, &min[0], &max[0]);
   
    return qtrue;
}

I then add the rigid bodies like so:
Code: Select all
static void PhysicsEntityAdd ( gentity_t* ent )
{
    NewtonBody* body = NULL;
    NewtonCollision* collision = NULL;
    vec3_t size;
   
    VectorSubtract (ent->r.maxs, ent->r.mins, size);

    collision = NewtonCreateBox (world, size[0], size[1], size[2], NULL);
    body = NewtonCreateBody (world, collision);
    NewtonReleaseCollision (world, collision);
   
    NewtonBodySetUserData (body, (void*)ent);
    NewtonBodySetDestructorCallback (body, PhysicsEntityDie);
    NewtonBodySetContinuousCollisionMode (body, 1);
    NewtonBodySetCentreOfMass (body, &ent->s.origin[0]);
    NewtonBodySetForceAndTorqueCallback (body, PhysicsEntityThink);
    NewtonBodySetTransformCallback (body, PhysicsEntitySetTransform);
   
    vec3_t inertia;
    inertia[0] = (ent->mass / 12.0f) * (size[1] * size[1] + size[2] * size[2]);
    inertia[1] = (ent->mass / 12.0f) * (size[0] * size[0] + size[2] * size[2]);
    inertia[2] = (ent->mass / 12.0f) * (size[1] * size[1] + size[0] * size[0]);
   
    NewtonBodySetMassMatrix (body, ent->mass, inertia[0], inertia[1], inertia[2]);

    dMatrix matrix;
    matrix.m_posit.m_x = ent->s.origin[0];
    matrix.m_posit.m_y = ent->s.origin[1];
    matrix.m_posit.m_z = ent->s.origin[2];

    NewtonBodySetMatrix (body, &matrix[0][0]);
}

PhysicsEntityThink is defined as:
Code: Select all
static void PhysicsEntityThink ( const NewtonBody* body, dFloat timestep, int threadIndex)
{
    vec3_t force, inertia;
    float mass;
   
    NewtonBodyGetMassMatrix (body, &mass, &inertia[0], &inertia[1], &inertia[2]);
    force[0] = 0.0f;
    force[1] = 0.0f;
    force[2] = mass * -100.0f;
   
    NewtonBodyAddForce (body, &force[0]);
}

And PhysicsEntitySetTransform as defined as:
Code: Select all
static void PhysicsEntitySetTransform ( const NewtonBody* body, const dFloat* matrix, int threadIndex )
{
    gentity_t *ent = NULL;
    vec3_t newPosition;
    vec3_t angles;
   
    ent = (gentity_t*)NewtonBodyGetUserData (body);
    VectorCopy (ent->r.currentOrigin, oldPosition);
   
    newPosition[0] = matrix[12];
    newPosition[1] = matrix[13];
    newPosition[2] = matrix[14];
   
    NewtonGetEulerAngle (&matrix[0], &angles[0]);
   
    trap_UnlinkEntity (ent);
    G_SetOrigin (ent, newPosition);
    G_SetAngles (ent, angles);
    trap_LinkEntity (ent);
}

Just to clear any confusion, Quake3 has the Y and Z axes swapped (Y is forward, Z is up). The problem is the rigid bodies don't behave as I would expect them to. In the simplest case, I have a box resting on the ground, but it slides around sideways. I'm thinking the problem is caused by the swapped Y and Z coordinates in Q3. I read in this topic that I need to set the initial matrices of something to fit Newton's coordinate system:
Paril wrote:Maybe the problem is the orientation when the object is create, it can look good but maybe the initial rotation is not the same in all side.
I talk about the direction and up and right, when you create the visual object you really need to have the same dir,up,right that newton using.
Do you have try to play with the offset matrix when you create your newton object ? maybe it can fix your problem.

But do I set the matrix of the collision or the rigid body or both? And my matrix maths isn't particular good, so could someone please tell me what is the initial matrix I need to use?

Thanks!
Xycaleth
 
Posts: 13
Joined: Mon Jun 29, 2009 6:41 am

Re: Using Newton in the Quake3 engine

Postby JernejL » Wed Jul 01, 2009 8:49 am

collision should be around collision object origin, what you move and set location for is the bodies, not collisions.

Code: Select all
  NewtonBodySetCentreOfMass (body, &ent->s.origin[0]);


This is wrong - comment it out, leave default centre of mass for now, centre of mass is NOT object or collision location and might be whats causing your problems.
Help improving the Newton Game Dynamics WIKI
User avatar
JernejL
 
Posts: 1587
Joined: Mon Dec 06, 2004 2:00 pm
Location: Slovenia

Re: Using Newton in the Quake3 engine

Postby Xycaleth » Wed Jul 01, 2009 9:44 am

Ah, that explains a lot! :P And it also fixes the problems I've been having :D Thanks a lot :)
Xycaleth
 
Posts: 13
Joined: Mon Jun 29, 2009 6:41 am

Re: Using Newton in the Quake3 engine

Postby Xycaleth » Wed Jul 01, 2009 12:27 pm

Sorry for the double post, but I have another problem now :( Sometimes my mod will crash when NewtonUpdate is called - the exact error from the Visual Studio debugger is:
Unhandled exception at 0x0776f0a0 (Newton.dll) in jamp.exe: 0xC0000005: Access violation reading location 0x00000000.

So it looks like Newton's trying to access a null pointer? I looked at the call stack and it crashes in the function dgRedBackNode::Unlink of the Newton code.
Xycaleth
 
Posts: 13
Joined: Mon Jun 29, 2009 6:41 am

Re: Using Newton in the Quake3 engine

Postby JernejL » Wed Jul 01, 2009 12:53 pm

is this using newton2 or 1.6? also, are all your pointers send, for callbacks, userdata etc.. properly?
Help improving the Newton Game Dynamics WIKI
User avatar
JernejL
 
Posts: 1587
Joined: Mon Dec 06, 2004 2:00 pm
Location: Slovenia

Re: Using Newton in the Quake3 engine

Postby Xycaleth » Wed Jul 01, 2009 1:25 pm

I'm using Newton 2.0 and I'm certain that I'm sending all my pointers correctly.
Xycaleth
 
Posts: 13
Joined: Mon Jun 29, 2009 6:41 am

Re: Using Newton in the Quake3 engine

Postby Julio Jerez » Wed Jul 01, 2009 2:28 pm

how many bodies do you have in yor simulation?
I would try with teh level and one body, You most have a body with a Invalid pointed.
The kind of error have have never happne in teh RedBlackTree in more that n20 year I am using it.
Does the Stack show more funtions? can you place them maybe I cna tell wht is wrokn.

Also if you world using the z, up and the y as fron the funtion NewtonGetEuler Amgle will no work for you, but we can woprries aboput taht Later,
First we most see why the engine is crashing.
Julio Jerez
Moderator
Moderator
 
Posts: 12426
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Using Newton in the Quake3 engine

Postby Xycaleth » Wed Jul 01, 2009 3:03 pm

I had about 20 bodies in the simulation. I'm now thinking it has something to do with the way I'm loading in the map surfaces though, because sometimes the bodies will just move straight through them as if they weren't there - in most cases the bodies collide correctly with the map surfaces but sometimes they pass through the surfaces :shock: I noticed it would crash as soon as a body left the world.

I've just tried another simulation with only 1 body, and it crashed again as soon as the body left the world. Here's the full call stack:
Newton.dll!dgRedBackNode::Unlink(dgRedBackNode * * head=0x02615fcc) Line 406
Newton.dll!dgRedBackNode::Remove(dgRedBackNode * * head=0x02615fcc) Line 493
Newton.dll!dgBroadPhaseCollision::UpdateBodyBroadphase(dgBody * body=0x0b3bef60, int threadIndex=0) Line 836
Newton.dll!dgBody::UpdateCollisionMatrix(float timestep=0.016666668, int threadIndex=0) Line 342
Newton.dll!dgBroadPhaseApplyExternalForce::ThreadExecute() Line 981
Newton.dll!dgBroadPhaseCollision::UpdateContactsBroadPhaseBegin(float timestep=0.016666668, bool collisioUpdateOnly=false) Line 1101
Newton.dll!dgBroadPhaseCollision::UpdateContacts(float timestep=0.016666668, bool collisioUpdate=false) Line 1444
Newton.dll!dgWorld::Update(float timestep=0.016666668) Line 695
Newton.dll!Newton::UpdatePhysics(float timestep=0.016666668) Line 109
Newton.dll!NewtonUpdate(const NewtonWorld * newtonWorld=0x3c888889, float timestep=0.016666668) Line 668 + 0xf bytes
jampgamex86.dll!UpdatePhysicsEntities()


Also, I've changed the way I'm loading the map surfaces. This seems more correct than how I was previously doing it (you'll need to look at the original function for the code above and below this code):
Code: Select all
case MST_TRIANGLE_SOUP:
            case MST_PLANAR:
                vec3_t v[3];
                limit = surf->numVerts - 2;
                for ( j = 0; j < limit; ++j )
                {                   
                    if ( !flip )
                    {
                        VectorCopy (vert[surf->firstVert + j + 2].xyz, v[0]);
                        VectorCopy (vert[surf->firstVert + j + 1].xyz, v[1]);
                        VectorCopy (vert[surf->firstVert + j + 0].xyz, v[2]);
                    }
                    else
                    {
                        VectorCopy (vert[surf->firstVert + j + 2].xyz, v[0]);
                        VectorCopy (vert[surf->firstVert + j + 0].xyz, v[1]);
                        VectorCopy (vert[surf->firstVert + j + 1].xyz, v[2]);
                    }
                   
                    NewtonTreeCollisionAddFace (collision, 3, &v[0][0], sizeof (vec3_t), NewtonMaterialGetDefaultGroupID (world));
                    flip = (qboolean)!flip;
                }

                vertexCount += surf->numVerts;
                ++surfaceCount;
            break;


EDIT: I'm still wondering if I need to set an initial rotation matrix for the collision boxes. I tried creating a rectangular body but I'm noticing the collisions don't happen properly against it. As if some of the box is missing :?
Xycaleth
 
Posts: 13
Joined: Mon Jun 29, 2009 6:41 am

Re: Using Newton in the Quake3 engine

Postby JernejL » Wed Jul 01, 2009 3:14 pm

make sure that your trimesh face winding is correct, trimesh is only one-sided.
Help improving the Newton Game Dynamics WIKI
User avatar
JernejL
 
Posts: 1587
Joined: Mon Dec 06, 2004 2:00 pm
Location: Slovenia

Re: Using Newton in the Quake3 engine

Postby Xycaleth » Wed Jul 01, 2009 4:51 pm

OKAY! I've sorted the surface loading now. I didn't realize I had to use the index list that accompanies each surface :D I've used that now, and all the surfaces are loaded correctly now so all the collisions happen correctly. I'm still not sure how to set the initial collision box matrix though.
Xycaleth
 
Posts: 13
Joined: Mon Jun 29, 2009 6:41 am

Re: Using Newton in the Quake3 engine

Postby Julio Jerez » Wed Jul 01, 2009 5:08 pm

Ah good. Now that you have your level the next step in to crate the entities in a proper way.
This is your original function
Code: Select all
static void PhysicsEntityAdd ( gentity_t* ent )
{
    NewtonBody* body = NULL;
    NewtonCollision* collision = NULL;
    vec3_t size;
   
    VectorSubtract (ent->r.maxs, ent->r.mins, size);

    collision = NewtonCreateBox (world, size[0], size[1], size[2], NULL);
    body = NewtonCreateBody (world, collision);
    NewtonReleaseCollision (world, collision);
   
    NewtonBodySetUserData (body, (void*)ent);
    NewtonBodySetDestructorCallback (body, PhysicsEntityDie);
    NewtonBodySetContinuousCollisionMode (body, 1);
    NewtonBodySetCentreOfMass (body, &ent->s.origin[0]);
    NewtonBodySetForceAndTorqueCallback (body, PhysicsEntityThink);
    NewtonBodySetTransformCallback (body, PhysicsEntitySetTransform);
   
    vec3_t inertia;
    inertia[0] = (ent->mass / 12.0f) * (size[1] * size[1] + size[2] * size[2]);
    inertia[1] = (ent->mass / 12.0f) * (size[0] * size[0] + size[2] * size[2]);
    inertia[2] = (ent->mass / 12.0f) * (size[1] * size[1] + size[0] * size[0]);
    NewtonBodySetMassMatrix (body, ent->mass, inertia[0], inertia[1], inertia[2]);

    dMatrix matrix;
    matrix.m_posit.m_x = ent->s.origin[0];
    matrix.m_posit.m_y = ent->s.origin[1];
    matrix.m_posit.m_z = ent->s.origin[2];

    NewtonBodySetMatrix (body, &matrix[0][0]);
}


I will make some modifications
Code: Select all
static void PhysicsEntityAdd ( gentity_t* ent )
{
    NewtonBody* body = NULL;
    NewtonCollision* collision = NULL;
    vec3_t size;
   
   
   // here you can try using thr convex hull shape, but the Box will do too
    VectorSubtract (ent->r.maxs, ent->r.mins, size);

   // if the pivot point of your body is set on the floor, the you need to use an offest matrix
    dmatrix offset (GetIdentityMtaruix());

   // this will align the collision Box with the shape of your mesh
    offset.m_posit.m_z = (ent->r.maxs[2] -  ent->r.mins[2]) * 0.5f;

    collision = NewtonCreateBox (world, size[0], size[1], size[2], &offset[0][0]);
    body = NewtonCreateBody (world, collision);
    NewtonReleaseCollision (world, collision);
   
    NewtonBodySetUserData (body, (void*)ent);
    NewtonBodySetDestructorCallback (body, PhysicsEntityDie);

// try without this first
    NewtonBodySetContinuousCollisionMode (body, 1);
//  NewtonBodySetCentreOfMass (body, &ent->s.origin[0]);
    NewtonBodySetForceAndTorqueCallback (body, PhysicsEntityThink);
    NewtonBodySetTransformCallback (body, PhysicsEntitySetTransform);

      // use the funtion to calculat eteh center of mass and teh inetria for you.
//    vec3_t inertia;
//    inertia[0] = (ent->mass / 12.0f) * (size[1] * size[1] + size[2] * size[2]);
//    inertia[1] = (ent->mass / 12.0f) * (size[0] * size[0] + size[2] * size[2]);
//    inertia[2] = (ent->mass / 12.0f) * (size[1] * size[1] + size[0] * size[0]);

        vec3_t inertia;
        vec3_t centerOfMass;
        NewtonConvexCollisionCalculateInertialMatrix (collision, &inertia[0], &centerOfMass[0]);   
       NewtonBodySetCentreOfMass (body, &centerOfMass[0]);

    inertia[0] *= ent->mass;
    inertia[1] *= ent->mass;
    inertia[2] *= ent->mass;
    NewtonBodySetMassMatrix (body, ent->mass, inertia[0], inertia[1], inertia[2]);


    dMatrix matrix (GetIdentityMatrix());
    matrix.m_posit.m_x = ent->s.origin[0];
    matrix.m_posit.m_y = ent->s.origin[1];
    matrix.m_posit.m_z = ent->s.origin[2];
    NewtonBodySetMatrix (body, &matrix[0][0]);
}
Julio Jerez
Moderator
Moderator
 
Posts: 12426
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Using Newton in the Quake3 engine

Postby JernejL » Thu Jul 02, 2009 3:49 am

Xycaleth wrote:OKAY! I've sorted the surface loading now. I didn't realize I had to use the index list that accompanies each surface :D I've used that now, and all the surfaces are loaded correctly now so all the collisions happen correctly. I'm still not sure how to set the initial collision box matrix though.


collision box? you mean newton collision offset or newton body location matrix? please be specific :?
Help improving the Newton Game Dynamics WIKI
User avatar
JernejL
 
Posts: 1587
Joined: Mon Dec 06, 2004 2:00 pm
Location: Slovenia

Re: Using Newton in the Quake3 engine

Postby Xycaleth » Thu Jul 02, 2009 7:11 am

Thanks for the code changes Julio :) Now how can I get usable Euler angles for the Q3 engine? You said that I can't use NewtonGetEulerAngle function because of the swapped Y and Z axes.
Delfi wrote:collision box? you mean newton collision offset or newton body location matrix? please be specific :?

Sorry, I meant the Newton collision offset. To quote my original post:
Xycaleth wrote:I read in this topic that I need to set the initial matrices of something to fit Newton's coordinate system:
Paril wrote:Maybe the problem is the orientation when the object is create, it can look good but maybe the initial rotation is not the same in all side.
I talk about the direction and up and right, when you create the visual object you really need to have the same dir,up,right that newton using.
Do you have try to play with the offset matrix when you create your newton object ? maybe it can fix your problem.

Is this true or can I just leave the offset matrix NULL?
Xycaleth
 
Posts: 13
Joined: Mon Jun 29, 2009 6:41 am

Re: Using Newton in the Quake3 engine

Postby JernejL » Thu Jul 02, 2009 7:58 am

You should only move the newton collision offset matrix when you want the collision shape to have a offsetted, that is, if your shapes that you create aren't centered around the object origin.

If quake engine entity origins work anything like half-life(1)'s origin then you don't need to do any adjustment as the brushes are set to have origin in the origin point.
Help improving the Newton Game Dynamics WIKI
User avatar
JernejL
 
Posts: 1587
Joined: Mon Dec 06, 2004 2:00 pm
Location: Slovenia

Re: Using Newton in the Quake3 engine

Postby Stucuk » Thu Jul 02, 2009 1:51 pm

Delfi wrote:You should only move the newton collision offset matrix when you want the collision shape to have a offsetted, that is, if your shapes that you create aren't centered around the object origin.

If quake engine entity origins work anything like half-life(1)'s origin then you don't need to do any adjustment as the brushes are set to have origin in the origin point.


Entities are literaly centered(Well there Origin is on (0,0,0)) around (0,0,0). So as Delfi said you don't need to set the collision's offset . The only thing you need to do is to move the object to wherever in the level it should show up.

This is the same in all Q3 based engines (RTCW, Elite Force, Soldier Of Fortune 2, Jedi Knight Academy, ETC).
User avatar
Stucuk
 
Posts: 801
Joined: Sat Mar 12, 2005 3:54 pm
Location: Scotland

Next

Return to General Discussion

Who is online

Users browsing this forum: No registered users and 3 guests

cron