Tutorial - SoftPixel Engine with Newton

From Newton Wiki
Revision as of 08:02, 10 June 2019 by WikiSysop (talk | contribs) (1 revision imported)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

There is an C++ example "SoftPixel Engine: Newton integration" you can download here:

http://softpixelengine.sourceforge.net/download/NewtonSDKIntegration.zip

Introduction

This tutorial will show you the interface between the SoftPixel Engine and NewtonGameDynamics. You won't find the whole code here so you have to download the package available at the above link.

Getting started

In the example we only have one global Newton member:

// Global Newton members:
NewtonWorld* nWorld = 0;

We initialize Newton in the constructor of our PhysicsSystem class:

PhysicsSystem::PhysicsSystem()
{
    // Create the physics environment
    nWorld = NewtonCreate();
    
    // Create a physics material (in this example only one)
    s32 MaterialID = NewtonMaterialGetDefaultGroupID(nWorld);
    
    // Configure material (friction, elasticity, softness)
    NewtonMaterialSetDefaultFriction  (nWorld, MaterialID, MaterialID, 0.8f, 0.4f);
    NewtonMaterialSetDefaultElasticity(nWorld, MaterialID, MaterialID, 0.3f      );
    NewtonMaterialSetDefaultSoftness  (nWorld, MaterialID, MaterialID, 0.05f     );
}

By the way our PhysicsSystem class only has a list of our PhysicsObjects but no more Newton members.

Next we should define the two needed callback functions: "SetMeshTransformEvent" and "ApplyForceAndTorqueEvent". These functions are called when the location of a physics object has changed (SetMeshTransformEvent) or the forces are needed to be applied (ApplyForceAndTorqueEvent) normally for gravity. To work with callbacks for gravity as well is a very good concept because in this way you can create levels were you have more than one gravity direction. Maybe you could create a magnetic field and only some objects are effected by it. But in this example we only have one gravity which points straight down.

static void _cdecl SetMeshTransformEvent(const NewtonBody* nBody, const f32* Matrix, s32 ThreadIndex)
{
    /**
     * The user data which is needed to be set after creation of a new Newton body
     * Return value of "NewtonBodyGetUserData" is a void pointer which is casted to our "PhysicsObject" class
     */
    PhysicsObject* Obj = (PhysicsObject*)NewtonBodyGetUserData(nBody);
    
    // Check if the pointer is valid and there is a 3d model (SoftPixel Engine's "scene::Entity" class)
    if (Obj && Obj->GetMesh())
    {
        // Set the object's transformation matrix computed by the physics engine
        dim::matrix4f Mat;
        memcpy(&Mat[0], Matrix, sizeof(f32)*16);
        
        // Reset scale because it's not consiered by the physics engine
        Mat.scale(Obj->GetMesh()->getScale());
        Obj->GetMesh()->setMatrix(Mat);
    }
}

static void _cdecl ApplyForceAndTorqueEvent(const NewtonBody* nBody, dFloat TimeStep, s32 ThreadIndex)
{
    // The same like in the function above
    PhysicsObject* Obj = (PhysicsObject*)NewtonBodyGetUserData(nBody);
    
    if (!Obj)
        return;
    
    /**
     * Get the mass information
     * "Ixx", "Iyy" and "Izz" is not used in this example but the
     * "NewtonBodyGetMassMatrix" functions always accepts these variables
     */
    f32 Mass;
    f32 Ixx, Iyy, Izz;
    
    NewtonBodyGetMassMatrix(nBody, &Mass, &Ixx, &Iyy, &Izz);
    
    /**
     * Set the gravity force (can point in any direction)
     * In this example the direction is (0 | -25 | 0)
     * When using the real gravity (g = 9.81) your scene's size must fit
     */
    const dim::vector3df Dir = InGameGravity * Mass;
    
    NewtonBodySetForce(nBody, &Dir.X);
}

No we can change over to the creation of our objects consisting of a 3d model (using the SoftPixel Engine) and a physics object (using Newton). The physics objects furthermore consists of a "Collision" (NewtonCollision) and a "Body" (NewtonBody). There are some pre defined collision types like "Cube", "Sphere", "Capsule" etc. But you also can use a polygon collision by building your own triangle by triangle. There is the possibility to create static and dynamic physics objecs, too. Dynamic objects are boxes for example which fall around the scene. Objects you can drag'n'drop or so. In our example we only have one static object: the world itself. The environment object which shall not fall down. So at first we will take a look at the function for creating the static objects:

void PhysicsObject::CreateStatic()
{
    // Temporary variables we need for the creation
    f32 vArray[9];
    u32 Indices[3];
    dim::vector3df Pos[3];
    
    // Index members
    u32 s, i, j;
    
    // Create collision tree and fill the triangle data
    nCollision_ = NewtonCreateTreeCollision(nWorld, 0);
    
    // Begin building the collision tree
    NewtonTreeCollisionBeginBuild(nCollision_);
    
    /**
     * Loop for each mesh's surface
     * Remember what a SoftPixel Engine's surface is for:
     * It's particular used to give a mesh multiple textures (not multi-texturing)
     */
    for (s = 0; s < Mesh_->getSurfaceCount(); ++s)
    {
        Mesh_->selectSurface(s);
        
        for (i = 0; i < Mesh_->getTriangleCount(s); ++i)
        {
            Mesh_->getTriangleIndices(i, Indices);
            
            for (j = 0; j < 3; ++j)
            {
                Pos[j] = Mesh_->getVertexCoord(Indices[j]);
                
                vArray[j*3+0] = Pos[j].X;
                vArray[j*3+1] = Pos[j].Y;
                vArray[j*3+2] = Pos[j].Z;
            }
            
            /**
             * Add a new face (triangle)
             * vArray holds 9 float values: 3 vectors with its 3 components X, Y and Z
             * "NewtonTreeCollisionAddFace" can also create a quad like face but we only use triangles here
             */
            NewtonTreeCollisionAddFace(nCollision_, 3, vArray, sizeof(dim::vector3df), 0);
        }
    }
    
    // Finish collision tree creation
    NewtonTreeCollisionEndBuild(nCollision_, 1);
    
    // Create the body (static objects need a body as well)
    nBody_ = NewtonCreateBody(nWorld, nCollision_);
    
    // Calculate the axis-alined bounding box (AABB)
    f32 BoxP0[3];
    f32 BoxP1[3];
    f32 pMat[4][4];
    
    NewtonBodyGetMatrix(nBody_, &pMat[0][0]);
    NewtonCollisionCalculateAABB(nCollision_, &pMat[0][0], &BoxP0[0], &BoxP1[0]);
}

In the function above "Mesh_" is a member of "PhysicsObject" and set in the constructor. In the example a Quake III Arena level is loaded by the "SceneManager::loadScene" function (BSP3 format). No we can change over to the next function "CreateDynamic":

void PhysicsObject::CreateDynamic(const EPhysicsObjectTypes Type, const dim::vector3df &Scale)
{
    /**
     * Create the special physics object type
     * The first function with return value "NewtonCollision" is the function which give us the pre defined collision models
     */
    switch (Type)
    {
        case PHYSICS_CUBE:
        {
            nCollision_ = NewtonCreateBox(nWorld, Scale.X, Scale.Y, Scale.Z, 0, 0);
            Mesh_       = spSmngr->createModel(scene::ENTITY_CUBE);
        }
        break;
        
        case PHYSICS_CONE:
        {
            nCollision_ = NewtonCreateCone(nWorld, Scale.Y/2, Scale.X, 0, 0);
            Mesh_       = spSmngr->createModel(scene::ENTITY_CONE);
            Mesh_->meshTurn(dim::vector3df(0, 0, -90));
        }
        break;
        
        case PHYSICS_CYLINDER:
        {
            nCollision_ = NewtonCreateCylinder(nWorld, Scale.Y/2, Scale.X*1.5, 0, 0);
            Mesh_       = spSmngr->createModel(scene::ENTITY_CYLINDER);
            Mesh_->meshTurn(dim::vector3df(0, 0, 90));
            Mesh_->meshTransform(dim::vector3df(1.5, 1, 1));
        }
        break;
        
        case PHYSICS_SPHERE:
        {
            nCollision_ = NewtonCreateSphere(nWorld, Scale.X/2, Scale.Y/2, Scale.Z/2, 0, 0);
            Mesh_       = spSmngr->createModel(scene::ENTITY_SPHERE);
        }
        break;
        
        case PHYSICS_POLYGON:
        {
            /**
             * This is similiar to the creation of a static object.
             * We have a 3d model (Mesh_) again and in this case we only need to fill a container (pVertices)
             * and transmit it to "Newton" with the function "NewtonCreateConvexHull"
             */
            const s32 VertexCount = Mesh_->getVertexCount();
            
            f32* pVertices = new f32[VertexCount*3];
            
            u32 s, i, j = 0;
            
            for (s = 0; s < Mesh_->getSurfaceCount(); ++s)
            {
                Mesh_->selectSurface(s);
                
                for (i = 0; i < Mesh_->getVertexCount(s); ++i)
                {
                    pVertices[j++] = Mesh_->getVertexCoord(i).X;
                    pVertices[j++] = Mesh_->getVertexCoord(i).Y;
                    pVertices[j++] = Mesh_->getVertexCoord(i).Z;
                }
            }
            
            nCollision_ = NewtonCreateConvexHull(nWorld, VertexCount, pVertices, sizeof(f32)*3, 0.0f, 0, 0);
            
            delete [] pVertices;
        }
        break;
    }
    
    // Add a texture to the standard primitive objects
    if (Type != PHYSICS_POLYGON)
        Mesh_->addTexture(CrateTex[math::Rnd(2)]);
    
    // Create and configure the physics body
    nBody_ = NewtonCreateBody(nWorld, nCollision_);
    
    Mesh_->meshTransform(Scale);
    
    // Mass and user data
    static const f32 DefaultMass = 35.0f;
    
    NewtonBodySetUserData(nBody_, this);
    NewtonBodySetMassMatrix(nBody_, DefaultMass, 15.0f, 15.0f, 15.0f);
    
    // No auto freeze/sleep
    NewtonBodySetFreezeState(nBody_, 0);
    NewtonBodySetAutoSleep(nBody_, 0);
    
    // Newton callback procedures we defined at the beginning
    NewtonBodySetTransformCallback(nBody_, SetMeshTransformEvent);
    NewtonBodySetForceAndTorqueCallback(nBody_, ApplyForceAndTorqueEvent);
}

That were the most important functions to be explained.

Forces, Velocity, Impulse

There are some functions in the PhysicsObject class we could mention:

void PhysicsObject::SetVelocity(const dim::vector3df &Velocity)
{
    // Set the velocity (array with 3 float components)
    NewtonBodySetVelocity(nBody_, &Velocity.X);
}
void PhysicsObject::AddVelocity(const dim::vector3df &Velocity)
{
    SetVelocity(GetVelocity() + Velocity);
}
dim::vector3df PhysicsObject::GetVelocity() const
{
    // Get the velocity (array with 3 float components)
    dim::vector3df Velocity;
    NewtonBodyGetVelocity(nBody_, &Velocity.X);
    return Velocity;
}

to be continued ...

I hope that was helpful and you all can use the SoftPixel Engine with Newton :-)