Tutorial 105: - Basic Materials

From Newton Wiki
Jump to: navigation, search

Template:Languages
This tutorial introduces how to use basic materials

Select project Tutorial_105_BasicMaterials as startup project in Visual Studio and open the file CreateScene in the editor and find the function CreateScene:

void CreateScene (NewtonWorld* world, SceneManager* sceneManager)
{
	Entity* floor;
	Entity* smilly;
	Entity* frowny;
	NewtonBody* floorBody;
	NewtonBody* smillyBody;
	NewtonBody* frownyBody;
	NewtonCollision* shape;

	// Create the material for this scene
	CreateMaterials (world, sceneManager);

	// Create a large body to be the floor
	floor = sceneManager->CreateEntity();
	floor->LoadMesh ("FloorBox.dat");

	// add static floor object
	shape = CreateNewtonBox (world, floor, 0);
	floorBody = CreateRigidBody (world, floor, shape, 0.0f);
	NewtonReleaseCollision (world, shape);

	// set the Transformation Matrix for this rigid body
	dMatrix matrix (floor->m_curRotation, floor->m_curPosition);
	NewtonBodySetMatrix (floorBody, &matrix[0][0]);

	// now we will use the properties of this body to set a proper world size.
	dVector minBox;
	dVector maxBox;
	NewtonCollisionCalculateAABB (shape, &matrix[0][0], &minBox[0], &maxBox[0]);

	// add some extra padding
	minBox.m_x -=  50.0f;
	minBox.m_y -= 500.0f;
	minBox.m_z -=  50.0f;

	maxBox.m_x +=  50.0f;
	maxBox.m_y += 500.0f;
	maxBox.m_z +=  50.0f;

	// set the new world size
	NewtonSetWorldSize (world, &minBox[0], &maxBox[0]);

	// assign an Material ID to this body
	NewtonBodySetMaterialGroupID (floorBody, g_floorMaterial);



	// add some visual entities.
	dFloat y0 = FindFloor (world, 0.0f, 0.0f) + 10.0f;
	for (int i = 0; i < 5; i ++) {
		smilly = sceneManager->CreateEntity();
		smilly->LoadMesh ("Smilly.dat");
		smilly->m_curPosition.m_y = y0;
		y0 += 2.0f;
		smilly->m_prevPosition = smilly->m_curPosition;

		// add a body with a box shape
		shape = CreateNewtonBox (world, smilly, 0);
		smillyBody = CreateRigidBody (world, smilly, shape, 10.0f);
		NewtonReleaseCollision (world, shape);

		// assign an Material ID to this body
		NewtonBodySetMaterialGroupID (smillyBody, g_metalMaterial);
	}


	// add some visual entities.
	y0 = FindFloor (world, 0.0f, 0.4f) + 10.5f;
	for (int i = 0; i < 5; i ++) {
		frowny = sceneManager->CreateEntity();
		frowny->LoadMesh ("Frowny.dat");
		frowny->m_curPosition.m_z = 0.4f;
		frowny->m_curPosition.m_y = y0;
		y0 += 2.0f;
		frowny->m_prevPosition = frowny->m_curPosition;

		// add a body with a Convex hull shape
		shape = CreateNewtonConvex (world, frowny, 0);
		frownyBody = CreateRigidBody (world, frowny, shape, 10.0f);
		NewtonReleaseCollision (world, shape);

		// assign an Material ID to this body
		NewtonBodySetMaterialGroupID (frownyBody, g_woodMaterial);
	}
}

This function creates a standard physics scene with one large static rigid body and few smaller dynamic rigid bodies bouncing around. The difference between this scene and the scenes in previous tutorials is that in this scene, each time a rigid body is in contact with another rigid body in the scene, we want to generate a different event.

For this tutorial we will represent different events by playing a different impact sound. To do this, the material graph must first be configured by calling the function CreateMaterials:

void CreateMaterials (NewtonWorld* world, SceneManager* sceneManager)
{
	SoundManager* sndManager;
		
	sndManager = sceneManager->GetSoundManager();

	// create the Material IDs, 
	g_floorMaterial = NewtonMaterialCreateGroupID (world);
	g_woodMaterial = NewtonMaterialCreateGroupID (world);
	g_metalMaterial = NewtonMaterialCreateGroupID (world);

	// load the sound effects 
	woodOnFloor.m_sound = sndManager->LoadSound ("boxHit.wav");
	woodOnFloor.m_manager = sndManager;

	metalOnFloor.m_sound = sndManager->LoadSound ("metal.wav");
	metalOnFloor.m_manager = sndManager;

	woodOnMetal.m_sound = sndManager->LoadSound ("metalBox.wav");
	woodOnMetal.m_manager = sndManager;
	
	metalOnMetal.m_sound = sndManager->LoadSound ("metalMetal.wav");
	metalOnMetal.m_manager = sndManager;

	woodOnWood.m_sound = sndManager->LoadSound ("boxBox.wav");
	woodOnWood.m_manager = sndManager;


	//configure the Material interactions
	NewtonMaterialSetCollisionCallback (world, g_woodMaterial, g_floorMaterial, &woodOnFloor, NULL,  GenericContactProcess);
	NewtonMaterialSetCollisionCallback (world, g_metalMaterial, g_floorMaterial, &metalOnFloor, NULL,  GenericContactProcess);
	NewtonMaterialSetCollisionCallback (world, g_metalMaterial, g_woodMaterial, &woodOnMetal, NULL,  GenericContactProcess);

	NewtonMaterialSetCollisionCallback (world, g_woodMaterial, g_woodMaterial, &woodOnWood, NULL,  GenericContactProcess);
	NewtonMaterialSetCollisionCallback (world, g_metalMaterial, g_metalMaterial, &metalOnMetal, NULL,  GenericContactProcess);
}

The material system in Newton is an undirected graph were the nodes of the graphs represent the material and the edges of the graph store the physical properties between two material interactions.

To configure the Material Graph, the application for shave to create all of the material types that will be part of the Physics Scene. This is accomplished by calling the function NewtonMaterialCreateGroupID: each time this function is called a new ID is returned and the application needs to specify how this new material will interact with all other previously created materials. As you can see, the number of interactions increases with the square of the number of materials. Therefore it is the job of the application to minimize the number of materials.

To minimize the amount of work needed to set all on the material interactions for each newly created material, the engine uses the parameters of the default material to initialize all of the material interactions of the new material. When new materials are added, they will use the default behaviors already set, however new behaviors can be specified to override the defaults.

Material interactions are specified by calling functions such as NewtonMaterialSetCollisionCallback, NewtonMaterialSetDefaultSoftness, NewtonMaterialSetDefaultFriction. A complete list can be found here: Newton SDK API reference#Category:Physics_Material_Section

As you can see in this demo we only use NewtonMaterialSetCollisionCallback as we know all other interactions are set to the default interaction.

NewtonMaterialSetDefaultSoftness stores the pointer to a data structure that store some application specific data to be processed when the two materials collide. A callback function can also be stored to process the collision of the two materials:

Here is the callback function used in this tutorial:

static void GenericContactProcess (const NewtonJoint* contactJoint, dFloat timestep, int threadIndex)
{
	dFloat contactBestSpeed;
	SoundEffect* bestSound;
	dVector contactPosit;

	bestSound = NULL;
	contactBestSpeed = 0.5f;
        // iterate obe all ccontact of this collision and find the contact
        // tha most dominate the collision, this is the contct with thr strongest impact velocity
	for (void* contact = NewtonContactJointGetFirstContact (contactJoint); contact; contact = NewtonContactJointGetNextContact (contactJoint, contact)) {
		dFloat contactNormalSpeed;
		NewtonMaterial* material;

		// get the material for this contact;
		material = NewtonContactGetMaterial (contact);

		contactNormalSpeed = NewtonMaterialGetContactNormalSpeed (material);
		if (contactNormalSpeed > contactBestSpeed){
                        // the contact is stroget teh teh previus, save it and save teh local Data for futher prossesing
			contactBestSpeed = contactNormalSpeed;
			dVector normal;
			contactBestSpeed = contactNormalSpeed;
			NewtonMaterialGetContactPositionAndNormal (material, &contactPosit[0], &normal[0]);
			bestSound = (SoundEffect *)NewtonMaterialGetMaterialPairUserData (material);
		}
	}

	// now that we found teh most dominat contact
	if (bestSound) {
		dFloat volume;
		dFloat dist2;

                // we will play a sound to signal there was a collision.
		dVector eyePoint (GetCameraEyePoint() - contactPosit);
		dist2 = eyePoint % eyePoint;
		if (dist2 < (MAX_SOUND_DISTANCE * MAX_SOUND_DISTANCE)) {
                        // the sound volume is proportional to the distance from the eyepoint to the collision 
			volume = 1.0f;
			if (dist2 > (MIN_SOUND_DISTANCE * MIN_SOUND_DISTANCE)) {
				volume = 1.0f - (dSqrt (dist2) - MIN_SOUND_DISTANCE) / (MAX_SOUND_DISTANCE -  MIN_SOUND_DISTANCE);
			}
			bestSound->m_manager->Play(bestSound->m_sound, volume, 0);
		}
	}
}

With this we finish the implementation of a very basic sound system using Newton.

Later we will implement a more advanced material system where well use collision shape IDs to handle collisions with sub material shape stored with the rigid body. For now, this system can only control materials at the rigid body level, although it is possible to handle sub materiasl by writing a more complex contact callback. I suggest reading the advanced material system before you decide to do this.