gr_tut20_screenshot.jpg

ElasticPhysics.cs

#region Using directives

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using Microsoft.DirectX;
using Microsoft.DirectX.DirectSound;
using Microsoft.DirectX.Direct3D;

using JadEngine;
using JadEngine.Video;
using JadEngine.Core;
using JadEngine.Mathematics;
using JadEngine.Scene;
using JadEngine.Scene.MD5;
using JadEngine.Physics;
using JadEngine.Particles;
using JadEngine.Sound;
using JadEngine.Tools;
using JadEngine.Input;

#endregion

namespace Tutorial
{
	/// <sumary>
	/// This example will teach you to create materials and assign them elastic properties.
	/// Each time you hit a hinge, it loses 1 hit point. When it gets to 0, the joint is destroyed
	/// and the hinge falls down. The red hinge has angle's limits.
	/// </sumary>
	public class ElasticPhysics : JApplication
	{
		#region Structs

		/// <summary>
		/// TODO
		/// </summary>
		struct Contact
		{
			#region Fields

			/// <summary>
			/// TODO
			/// </summary>
			public JRigidBody Body0;

			/// <summary>
			/// TODO
			/// </summary>
			public JRigidBody Body1;

			#endregion
		};

		#endregion

		#region Fields

		/// <summary>
		/// Indicates if the help must be shown in the screen or not
		/// </summary>
		bool showHelp = true;

		/// <summary>
		/// TODO
		/// </summary>
		JMesh mesh;

		/// <summary>
		/// TODO
		/// </summary>
		JCollision collision;

		/// <summary>
		/// Elastic material
		/// </summary>
		JPhysicsMaterial elastic;

		/// <summary>
		/// Non-elastic material
		/// </summary>
		JPhysicsMaterial nonElastic;

		/// <summary>
		/// TODO
		/// </summary>
		JSample sample;

		/// <summary>
		/// TODO
		/// </summary>
		Contact contactValues;

		/// <summary>
		/// TODO
		/// </summary>
		MaterialSetCollisionBegin mscbCB;

		/// <summary>
		/// TODO
		/// </summary>
		MaterialSetCollisionProcess mscpCB;

		/// <summary>
		/// TODO
		/// </summary>
		MaterialSetCollisionEnd msceCB;

		#endregion

		#region Methods

		/// <summary>
		/// The engine calls this method after creating the device
		/// </summary>
		/// <param name="form">Form used as window</param>
		/// <returns>True if everything went right</returns>
		public override bool InitGame(Form form)
		{
			// Create the fonts and texture objects
			Jad.AfterCreateDevice();

			// Load default textures, scenes, shaders,...
			Jad.LoadCreateInHouse();

			#region Creation of the world

			// Activate Newton's physics system
			Jad.Physics.Create();

			CreateGround();

			CreateHinges();

			CreateSphereMeshAndMaterials();

			BuildMaterialCollision();

			#endregion

			// Create the sound, setting the sound priority and quality (for 3D sound)
			Jad.CreateSound(CooperativeLevel.Priority, JSoundQuality.High);

			// Create the 2D sample
			sample = Jad.Sound.Create("golpe.wav");

			// Put ambient light
			Jad.Scene.Lights.Ambient = new JColor(0.6f);

			Jad.Scene.Camera.Transform.Position = new Vector3(0, 1f, -6f);

			Jad.Scene.Camera.FarPlane = 50F;

			// When working with physics the best is set a fixed time
			Jad.Timer.FixedMode = true;
			Jad.Timer.FixedModeFPS = 1.0f / 60.0f;	// 60 fps

			// Set our skybox
			Jad.Scene.SkyBox = new JSkyBox("lobbycube");

			// Everything is set, start the engine
			Jad.Begin();

			// Enable bloom effect
			Jad.Scene.BloomEffect.Enabled = true;
			Jad.Scene.BloomEffect.HighPass.HighPassValue = 0.6f;
			Jad.Scene.BloomEffect.HighPass.Multiplier = 0.7f;

			// Create input class to manage the keyboard and the mouse
			Jad.CreateInput();

			return true;
		}

		/// <summary>
		/// Release the objects
		/// </summary>
		public override void End()
		{
			collision.Release();
		}

		/// <summary>
		/// This method is called every frame and it´s where the input
		/// events and the game action must be placed
		/// </summary>
		/// <returns>True to continue, false to exit the application</returns>
		public override bool Update()
		{
			// Exit the app?
			if (Jad.Input.Keyboard[Key.Escape].Down) return false;

			if (Jad.Input.Keyboard[Key.F1].Pressed) showHelp = !showHelp;

			// If G key was pressed we activate / deactivate the gizmos
			// The diference between KeyTouch and KeyPressed is that KeyTouch only returns
			// true if the key was pressed for the first time and not when it´s down as in
			// KeyPressed

			// KeyTouch is like normal writting, returning true only once for every key press

			if (Jad.Input.Keyboard[Key.G].Pressed)
				Jad.Scene.ShowGizmos = !Jad.Scene.ShowGizmos;

			// If they P key was pressed you can see / hide Newton collision objects
			if (Jad.Input.Keyboard[Key.P].Pressed)
				Jad.Physics.Debug = !Jad.Physics.Debug;

			if (Jad.Input.Keyboard[Key.Space].Pressed)
				AddSphere(true);

			if (Jad.Input.Keyboard[Key.Return].Pressed)
				AddSphere(false);

			// Everything was right, continue
			return true;
		}

		/// <summary>
		/// Render method
		/// </summary>
		public override void Render()
		{
			JView backbuffer = Jad.Video.Views[0];

			// Render the Jad.Scene using the scene camera
			backbuffer.Render();

			if (showHelp)
			{
				// Get access to the 1st font. The engine creates a font by default
				JFont font = Jad.Video.Fonts[0];

				// Start fonts rendering
				Jad.Video.Fonts.Begin();

				RenderHits(font);

				// Write text in the first line, position x=0, yellow color
				font.RenderLine(Jad.Version + ". Press ESC to exit. Press G to Show/Hide Gizmos. F1 show/hide text", 0, JColor.Yellow);

				// Write text in the second line, position x=0, white color
				font.RenderLine(Jad.Video.Views.CurrentView.Stats.Fps + " FPS", 0, JColor.White);

				font.RenderLine("Press P to show Physic collisions", 0, JColor.Yellow);

				font.RenderLine("Press SPACE to add an elastic sphere", 0, JColor.Yellow);

				font.RenderLine("Press RETURN to add a non elastic sphere", 0, JColor.Yellow);

				// End fonts rendering
				Jad.Video.Fonts.End();
			}

			// Show the backbuffer
			backbuffer.Present();
		}

		/// <summary>
		/// This callback is called when the two aabb boxes of the collisiong object overlap
		/// </summary>
		/// <param name="material">TODO</param>
		/// <param name="body0">TODO</param>
		/// <param name="body1">TODO</param>
		/// <returns>TODO</returns>
		int GenericContactBegin(int material, int body0, int body1)
		{
			contactValues.Body0 = (JRigidBody) JNewtonWrapper.BodyGetUserData(body0);
			contactValues.Body1 = (JRigidBody) JNewtonWrapper.BodyGetUserData(body1);

			// Return one the tell Newton the application wants to proccess this contact
			return 1;
		}

		/// <summary>
		/// This callback is called for every contact between the two bodies
		/// </summary>
		/// <param name="material">TODO</param>
		/// <param name="contact">TODO</param>
		/// <returns>TODO</returns>
		int GenericContactProcess(int material, int contact)
		{
			Object userData = (Object) JNewtonWrapper.MaterialGetMaterialPairUserData(material);

			if (contactValues.Body0.Node != null && contactValues.Body1.Node != null)
			{
				MyMeshObject hinge = null;

				if (contactValues.Body0.Node is MyMeshObject) hinge = (MyMeshObject) contactValues.Body0.Node;
				if (contactValues.Body1.Node is MyMeshObject) hinge = (MyMeshObject) contactValues.Body1.Node;

				if (hinge != null)
				{
					if (hinge.Hits > 0)
					{
						JColor color = hinge.Material[0].Layers[0].DiffuseMap.Color;

						// Set the object a little darker

						hinge.Material[0].Layers[0].DiffuseMap.Color -= 0.1f;

						color -= 0.1f;

						//hinge.Material[0].Layers[0].DiffuseMap.Color = color;

						// Play the hit sound
						sample.Play(false);

						// If we hit a lot, destroy the joint, and the piece will fall down
						if (--hinge.Hits <= 0)
							hinge.Physics.RigidBody.Joint.Destroy();
					}
				}
			}

			return 1;
		}

		/// <summary>
		/// This function is called after all contacts for these pairs are proccesed
		/// </summary>
		/// <param name="material">TODO</param>
		void GenericContactEnd(int material)
		{
		}

		/// <summary>
		/// TODO
		/// </summary>
		void BuildMaterialCollision()
		{
			mscbCB = new MaterialSetCollisionBegin(GenericContactBegin);
			mscpCB = new MaterialSetCollisionProcess(GenericContactProcess);
			msceCB = new MaterialSetCollisionEnd(GenericContactEnd);

			// We set the collision between the pair of materials that are interesting for us

			JPhysicsMaterial.SetCollisionDelegate(Jad.Physics.DefaultPhysicsMaterial, elastic, null, mscbCB, mscpCB, msceCB);
			JPhysicsMaterial.SetCollisionDelegate(Jad.Physics.DefaultPhysicsMaterial, nonElastic, null, mscbCB, mscpCB, msceCB);
		}

		/// <summary>
		/// Creates the ground
		/// </summary>
		void CreateGround()
		{
			// Create a rectangle to use as a floor
			JMesh groundMesh = JMesh.CreateRectangle(30f, 30f, 50, 50, JCreateRectangle.XZ, false, 1f, 1f);

			// Add it some noise ...
			float noise = 0.5f;

			groundMesh.Noise(new Vector3(0, noise, 0));

			JMeshObject ground = Jad.Scene.MeshObjects.Create("ground", false);

			// Now associate the mesh with the meshobject
			ground.Mesh = groundMesh;

			// Create a materials array of 1 element
			JMaterial[] material = new JMaterial[1];

			material[0] = Jad.Scene.Materials.Create();

			// Create the layer
			JMaterialLayer layer = new JMaterialLayer();

			// Add it to the material
			material[0].AddLayer(layer);

			layer.DiffuseMap.Texture = Jad.Video.Textures.Create2D("Pared_Difusse", true);

			// Set the material we have just created to our ground
			ground.Material = material;

			// Now we build the collision tree. This case is formed only by the floor

			List<JMeshObject> meshesForTree = new List<JMeshObject>();

			meshesForTree.Add(ground);

			// Build the collision tree from the list
			JRigidBody body = Jad.Physics.BuildCollisionTree(meshesForTree, false);
		}

		/// <summary>
		/// Creates the hinges
		/// </summary>
		void CreateHinges()
		{
			int i = 0;

			for (int x = 0; x < 5; x++)
				for (int y = 0; y < 5; y++)
				{
					float px = (float) x * 1.5f;
					float py = 1 + (float) y * 1.5f;

					CreateHinge(new Vector3(px, py, 0), i);

					i++;

					if (i > 2) i = 0;
				}
		}

		/// <summary>
		/// Creates a hinge
		/// </summary>
		/// <param name="position">Position of the hinge</param>
		/// <param name="hingeType">Hinge type</param>
		void CreateHinge(Vector3 position, int hingeType)
		{
			Vector3 size = new Vector3(1, 1, 0.1f);

			JMesh mesh = JMesh.CreateCube(size);

			// Crete my own meshobject

			MyMeshObject meshObject = new MyMeshObject("hinge");

			// Add it to the scene graph, and the list

			Jad.Scene.MeshObjects.Add(meshObject);

			// Now associate the mesh with the meshobject

			meshObject.Mesh = mesh;

			// Create a materials array of 1 element
			JMaterial[] material = new JMaterial[1];

			material[0] = Jad.Scene.Materials.Create();

			// Create the layer
			JMaterialLayer layer = new JMaterialLayer();

			// Add it to the material
			material[0].AddLayer(layer);

			layer.DiffuseMap.Texture = Jad.Video.Textures.Create2D("wood", true);

			// Set the material we have just created to our ground
			meshObject.Material = material;

			JCollision collision = new JCollision();

			collision.CreateBox(ref size);

			// The inertia values are very important for realistic physics.
			// Newton can compute now the inertia values for you.
			// If you do not compute inertia, the engine use the default
			// values of 1,1,1

			collision.ComputeInertia();

			JRigidBody body = Jad.Physics.RigidBodies.Create(collision, meshObject);

			body.Mass = 1f;

			Matrix matrix = Matrix.Translation(position);

			body.Matrix = matrix;

			// We change the pivot point and the diffuse color to have different hinges

			if (hingeType == 1)
			{
				position.Y += size.Y / 2;
				layer.DiffuseMap.Color = JColor.RedColor;
			}

			if (hingeType == 2)
			{
				position.Y -= size.Y / 2;
				layer.DiffuseMap.Color = JColor.BlueColor;
			}

			JHingeJoint joint = new JHingeJoint(position, new Vector3(1, 0, 0), body, null);

			// Set limits to the hinge joints when the pivot point
			// is up
			if (hingeType == 1)
			{
				// Set the angle limit when the hinge goes back
				joint.MinAngleLimit = Geometry.DegreeToRadian(80);
				// Set the angle limit when the hinge goes front
				joint.MaxAngleLimit = Geometry.DegreeToRadian(45);

				joint.UseAngleLimit = true;
			}
		}

		/// <summary>
		/// TODO
		/// </summary>
		void CreateSphereMeshAndMaterials()
		{
			float radius = 0.25f;

			mesh = JMesh.CreateSphere(radius, 20, 20);

			// In Newton, we must define first a "colisionable" object setting the
			// shape of the object.
			// As the collision object is the same for all the object, we create it here
			// and reuse it

			collision = new JCollision();

			// It´ll have the same size as the one done to create the mesh
			// BECAUSE IN NEWTON, AS IN HADDD, THE DISTANCE MEASURE UNIT IS THE METER
			collision.CreateSphere(radius, radius, radius);

			// Create a material with high elasticity

			elastic = new JPhysicsMaterial();

			// Set the Elasticity between the default material ( used for the ground )
			// and the elastic material
			elastic.SetDefaultElasticity(Jad.Physics.DefaultPhysicsMaterial.Material, 0.95f);

			// Now a non so elastic material
			nonElastic = new JPhysicsMaterial();
			nonElastic.SetDefaultElasticity(Jad.Physics.DefaultPhysicsMaterial.Material, 0.1f);

			// Set the tunneling values. See above for an explanation of tunneling
			Jad.Physics.DefaultPhysicsMaterial.SetContinuousCollisionMode(elastic.Material, JContinuousCollisionMode.TakeCareWithTunneling);
			Jad.Physics.DefaultPhysicsMaterial.SetContinuousCollisionMode(nonElastic.Material, JContinuousCollisionMode.TakeCareWithTunneling);
		}

		/// <summary>
		/// Adds a sphere to the scene
		/// </summary>
		/// <param name="useElasticMaterial">Indicates if the spehre uses the elastic material or not</param>
		void AddSphere(bool useElasticMaterial)
		{
			// Put the cubes so that they are stacked and centered
			Vector3 pos = Jad.Scene.Camera.Transform.Position;

			string name;

			if (useElasticMaterial) name = "elastic"; else name = "non elastic";

			JMeshObject meshObject = Jad.Scene.MeshObjects.Create(name, false);

			// Associate the mesh with the meshobject

			meshObject.Mesh = mesh;

			// Create a materials array of 1 element
			JMaterial[] material = new JMaterial[1];

			material[0] = Jad.Scene.Materials.Create();

			// Create the layer
			JMaterialLayer layer = new JMaterialLayer();

			// Add it to the material
			material[0].AddLayer(layer);

			// Set the material we have just created to our JMeshObject
			meshObject.Material = material;

			// Create a Rigid body. Rigid bodies are the objects that have
			// the colision and physic properties set. We associate the collision
			// object we just created with the JMeshObject
			JRigidBody body = Jad.Physics.RigidBodies.Create(collision, meshObject);

			// Set the mass
			body.Mass = 2f;

			// Tunnelling is the effect that happens, for example, when a bullet hits a wall.
			// The bullet has a very high velocity, so in a frame he hasn´t hit the wall yet, but in the next one, he has gone through it, so no collision is detected. 
			// Version 1.51 of Newton can handle automatically this type of things.
			// Note: You must set the coontinous mode to the pair of materials too

			body.ContinuousCollisionMode = JContinuousCollisionMode.TakeCareWithTunneling;

			if (useElasticMaterial)
			{
				body.SetMaterialGroupID(elastic.Material);
				layer.DiffuseMap.Color = JColor.Yellow;
			}

			else
			{
				body.SetMaterialGroupID(nonElastic.Material);
				layer.DiffuseMap.Color = JColor.GreenColor;
			}

			Matrix matrix = Matrix.Translation(pos);

			body.Matrix = matrix;

			// Add impulse using the look vector
			body.AddImpulse(Jad.Scene.Camera.Look * 10f);
		}

		/// <summary>
		/// Show the hits remaining to break the joint
		/// </summary>
		/// <param name="font">Font to render the text</param>
		void RenderHits(JFont font)
		{
			// The meshes
			for (int i = 0; i < Jad.Scene.MeshObjectsToRender.Count; i++)
			{
				JMeshObject meshObject = Jad.Scene.MeshObjectsToRender[i];

				int x, y;

				if (meshObject is MyMeshObject)
				{
					MyMeshObject hinge = (MyMeshObject) meshObject;

					Vector3 screen = JMath.Projection(Vector3.Empty, hinge.Transform.WorldTM);
					x = (int) screen.X;
					y = (int) screen.Y;
					if (screen.Z >= 0 && screen.Z <= 1)
						font.Render(hinge.Hits.ToString(), x, y, JColor.White);
				}
			}
		}

		#endregion
	}

	/// <summary>
	/// Example mesh object class for the tutorial
	/// </summary>
	public class MyMeshObject : JMeshObject
	{
		#region Fields

		/// <summary>
		/// Number of hits the object can recieve
		/// </summary>
		public float Hits;

		#endregion

		#region Constructors

		/// <summary>
		/// Default constructor
		/// </summary>
		/// <param name="name">Name of the object</param>
		public MyMeshObject(string name)
			: base(name, false, Jad.Scene.MeshObjects)
		{
			Hits = 10;
		}

		#endregion
	}
}

Last edited Mar 8, 2007 at 7:50 PM by Vicente, version 4

Comments

No comments yet.