gr_tut21_screenshot.jpg

LoadingAndBuoyancy.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 show a loading screen. You will create an UpVector joint
	/// to have a rotation vector constraint. You will see an example of targeting the camera
	/// to an object and give impulse to a sphere. You will see the Buoyancy properties too.
	/// </sumary>
	public class LoadingAndBuoyancy : JApplication
	{
		#region Fields

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

		/// <summary>
		/// TODO
		/// </summary>
		JCameraFirstPerson camera;

		/// <summary>
		/// Air plane model
		/// </summary>
		JMeshObject airPlane;

		/// <summary>
		/// TODO
		/// </summary>
		JMeshObject wheel;

		/// <summary>
		/// TODO
		/// </summary>
		JRigidBody body;

		/// <summary>
		/// TODO
		/// </summary>
		Vector4 waterPlane;

		/// <summary>
		/// The water object
		/// </summary>
		JMeshObject water;

		/// <summary>
		/// TODO
		/// </summary>
		float[] cubeVertices = new float[] {
			-0.5f, 0, -0.5f,1,1,
			0.5f,0,0.5f,0,0,
			-0.5f,0,0.5f,1,0,
			0.5f,0,-0.5f,0,1,
			-0.5f,1,-0.5f,0,1,
			0.5f,1,0.5f,1,0,
			0.5f,1,-0.5f,1,1,
			-0.5f,1,0.5f,0,0,
			-0.5f,0,-0.5f,0,1,
			0.5f,1,-0.5f,1,0,
			0.5f,0,-0.5f,1,1,
			-0.5f,1,-0.5f,0,0,
			0.5f,0,-0.5f,0,1,
			0.5f,1,0.5f,1,0,
			0.5f,0,0.5f,1,1,
			0.5f,1,-0.5f,0,0,
			0.5f,0,0.5f,0,1,
			-0.5f,1,0.5f,1,0,
			-0.5f,0,0.5f,1,1,
			0.5f,1,0.5f,0,0,
			-0.5f,0,0.5f,0,1,
			-0.5f,1,-0.5f,1,0,
			-0.5f,0,-0.5f,1,1,
			-0.5f,1,0.5f,0,0 };

		/// <summary>
		/// TODO
		/// </summary>
		int[] cubeFaces = new int[] {
			0,1,2,
			1,0,3,
			//4,5,6,
			//5,4,7,
			8,9,10,
			9,8,11,
			12,13,14,
			13,12,15,
			16,17,18,
			17,16,19,
			20,21,22,
			21,20,23 };

		/// <summary>
		/// TODO
		/// </summary>
		float angleY;

		#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();

			ShowLoading();

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

			CreateGround();

			CreateWaterPlane();

			CreateWheel();

			LoadAirPlane();

			SetCameraTarget();

			// Create a directorial light 
			JDirectLight light = (JDirectLight) Jad.Scene.Lights.Create("direct01", JLightType.Directional);
			light.Transform.Angles = new Vector3((float) Math.PI / 2, 0, 0);
			light.Color = JColor.White;
			light.Multiplier = 1.0f;
			light.AffectSpecular = true;
			light.Attenuation.Type = JLightAttenuationType.None;
			light.CastShadows = true;

			// Set the shadow mode to stencil

			Jad.Scene.Shadows.ShadowMode = JShadowMode.StencilVolume;

			// Set the depth pass mode 
			Jad.Scene.Shadows.Stencil.DepthPass = true;

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

			// We need a really far far plane
			Jad.Scene.Camera.FarPlane = 650.0f;

			// 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");

			// We need render the skybox first because of transparency
			Jad.Scene.RenderSkyBoxFirst = true;

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

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

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

			return true;
		}

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

		/// <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 (Jad.Input.Keyboard[Key.O].Pressed)
				Jad.Scene.Shadows.Stencil.Debug = !Jad.Scene.Shadows.Stencil.Debug;

			if (Jad.Input.Keyboard[Key.L].Pressed)
				camera.UseInput = !camera.UseInput;

			if (Jad.Input.Keyboard[Key.T].Pressed)
				camera.OnlyTranslationOnParent = !camera.OnlyTranslationOnParent;

			// 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.NumPadPlus].Pressed)
				body.Mass++;

			if (Jad.Input.Keyboard[Key.NumPadMinus].Pressed && body.Mass > 1)
				body.Mass--;

			if (Jad.Input.Keyboard[Key.NumPad4].Pressed)
				body.Buoyancy.LinearViscosity -= 0.1f;

			if (Jad.Input.Keyboard[Key.NumPad6].Pressed)
				body.Buoyancy.LinearViscosity += 0.1f;

			Vector3 front, right;

			front = new Vector3(0, 0, 1);
			right = new Vector3(1, 0, 0);

			front *= Jad.At1Second(10f);
			right *= Jad.At1Second(10f);

			if (Jad.Input.Keyboard[Key.Up].Down)
				body.AddImpulse(front);

			if (Jad.Input.Keyboard[Key.Right].Down)
				body.AddImpulse(right);

			if (Jad.Input.Keyboard[Key.Down].Down)
				body.AddImpulse(-front);

			if (Jad.Input.Keyboard[Key.Left].Down)
				body.AddImpulse(-right);

			MoveAirPlane();

			if (wheel.Transform.Position.Y <= waterPlane.W)
			{
				wheel.Physics.RigidBody.Buoyancy.Enabled = true;

				// Controlled by the application
				wheel.Physics.RigidBody.AutoFreeze = false;
			}

			else
			{
				wheel.Physics.RigidBody.Buoyancy.Enabled = false;

				// Controlled by Newton
				wheel.Physics.RigidBody.AutoFreeze = true;
			}

			// 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();

				// 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);

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

				font.RenderLine("Press O to activate/deactivate debug on shadows", 0, JColor.Yellow);

				font.RenderLine("Use arrow keys to move the ball. " + wheel.Physics.RigidBody.Buoyancy.Enabled.ToString(), 0, JColor.Yellow);

				font.RenderLine("Only translation on camera (T): " + Jad.Scene.Camera.OnlyTranslationOnParent.ToString() + " Lock camera (L):" + (!camera.UseInput).ToString(), 0, JColor.Yellow);

				font.RenderLine("Mass (+,-): " + body.Mass.ToString() + " Velocity vector: " + JMath.Vector3ToString(body.Velocity), 0, JColor.Yellow);

				font.RenderLine("Viscosity(4,6): " + body.Buoyancy.LinearViscosity.ToString("N2"), 0, JColor.Yellow);

				Jad.Video.Fonts.End();
			}

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

		/// <summary>
		/// Shows the loading screen
		/// </summary>
		void ShowLoading()
		{
			JSprite sprite = new JSprite();
			sprite.Texture = Jad.Video.Textures.Create2D("loading", true);
			sprite.SetPositionSize((Jad.Video.Width - sprite.Texture.ImageInfo.Width) / 2, (Jad.Video.Height - sprite.Texture.ImageInfo.Height) / 2,
									sprite.Texture.ImageInfo.Width, sprite.Texture.ImageInfo.Height);

			int time = System.Environment.TickCount;
			int elapsed;

			// Force the windows form to show
			Jad.Form.Show();

			// Create a new font
			JFont font = Jad.Video.Fonts.Create("Arial40", 50, 0, FontWeight.Bold, 1, false, CharacterSet.Default,
				Precision.Default, FontQuality.Default, PitchAndFamily.FamilyDoNotCare | PitchAndFamily.DefaultPitch
				, "Arial");

			// Wait 3 seconds
			while ((elapsed = System.Environment.TickCount - time) < 3000)
			{
				Jad.Video.Device.BeginScene();

				Jad.Video.Device.Clear(ClearFlags.Target, JColor.Black.ToArgb(), 1f, 0);

				sprite.Render();

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

				// Convert to seconds
				elapsed /= 1000;

				elapsed = 3 - elapsed;

				font.RenderCentered(elapsed.ToString(), 0, JColor.RedColor);

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

				Jad.Video.Device.EndScene();

				Jad.Video.Device.Present();
			}
		}

		/// <summary>
		/// Loads the airplane model
		/// </summary>
		void LoadAirPlane()
		{
			Jad.Import.Load("airplane 2.x", 0.1f);

			airPlane = Jad.Import.MeshObjects[0];
		}

		/// <summary>
		/// TODO
		/// </summary>
		void CreateWheel()
		{
			float radius = 0.5f;

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

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

			wheel = meshObject;

			wheel.CastShadows = true;

			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;

			// Create a collision object

			JCollision collision = new JCollision();

			collision.CreateSphere(radius, radius, radius);

			// 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();

			collision.CalculateVolume();

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

			body.Mass = 30f;

			Matrix matrix = Matrix.Translation(new Vector3(0, 5, 4));

			body.Matrix = matrix;

			// This way the sphere only will rotate on the y axis.

			JUpVectorJoint joint = new JUpVectorJoint(new Vector3(0, 1, 0), body);

			collision.Release();
		}

		/// <summary>
		/// TODO
		/// </summary>
		void SetCameraTarget()
		{
			// Now the camera will be targeting the sphere
			camera = (JCameraFirstPerson) Jad.Scene.Camera;

			// Set the relative position
			// When you are the child node, the position means relativa position to the parent node
			camera.Transform.Position = new Vector3(0, 2, -6);

			// The wheel is the parent node of the camera
			wheel.SceneGraph.AddChild(Jad.Scene.Camera);

			// We don't want to be affected by rotation of the wheel
			camera.OnlyTranslationOnParent = true;
		}

		/// <summary>
		/// TODO
		/// </summary>
		/// <param name="width">TODO</param>
		/// <param name="height">TODO</param>
		/// <param name="depth">TODO</param>
		/// <param name="tu">TODO</param>
		/// <param name="tv">TODO</param>
		/// <returns><TODO/returns>
		JMesh CreateContainer(float width, float height, float depth, float tu, float tv)
		{
			Mesh temp = new Mesh(cubeFaces.Length / 3, 24, MeshFlags.Managed, CustomVertex.PositionTextured.Format, Jad.Video.Device);

			JMesh mesh = Jad.Video.Meshes.Create();

			mesh.VertexBuffer = new JVertexBuffer(24);

			mesh.VertexBuffer.Position = new Vector3[24];
			mesh.VertexBuffer.TexCoord = new Vector2[1, 24];

			// Copy the source array to scale the values

			float[] vertices = new float[cubeVertices.Length];

			cubeVertices.CopyTo(vertices, 0);

			for (int i = 0, c = 0; i < cubeVertices.Length; i += 5, c++)
			{
				mesh.VertexBuffer.Position[c].X = vertices[i] * width;
				mesh.VertexBuffer.Position[c].Y = vertices[i + 1] * height;
				mesh.VertexBuffer.Position[c].Z = vertices[i + 2] * depth;
				mesh.VertexBuffer.TexCoord[0, c].X = vertices[i + 3] * tu;
				mesh.VertexBuffer.TexCoord[0, c].Y = vertices[i + 4] * tu;
			}

			// Creates an 16 bits IndexBuffer
			mesh.IndexBuffer = new JIndexBuffer(false, cubeFaces.Length);

			// Even if we have created a 16 bits indexBuffer, we always specify the indexes in 32 bits. The engine manages everything internally.

			cubeFaces.CopyTo(mesh.IndexBuffer.Indices, 0);

			// We want to invert the faces

			mesh.IndexBuffer.InvertFaces();

			return mesh;
		}

		/// <summary>
		/// TODO
		/// </summary>
		void CreateWaterContainer()
		{
			float height = 130f;

			JMesh groundMesh = CreateContainer(water.Mesh.BoundingBox.Width, height, water.Mesh.BoundingBox.Depth, 20f, 20f);

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

			ground.Transform.Position = water.Transform.Position - new Vector3(0, height / 2, 0);

			ground.AffectLight = 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("cement_stucco_d", true);

			layer.DiffuseMap.Color = new JColor(1f);

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

		/// <summary>
		/// TODO
		/// </summary>
		void CreateWaterPlane()
		{
			// Create a rectangle to use as a floor

			float depth = 500f;

			// IMPORTANT
			// Newton sets by default +-100 m of World Size.
			// when you build a TreeCollision, Jad adjusts the world size.
			// But here we are creating a very very big plane , so we must
			// change the world size

			Jad.Physics.SetWorldSize(new Vector3(-1000, -1000, -1000), new Vector3(1000, 1000, 1000));

			JMesh mesh = JMesh.CreateRectangle(depth, depth, 150, 150, JCreateRectangle.XZ, false, 10f, 10f);

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

			//water.Hide = true;

			// Now we associate the mesh with the meshobject

			water.Mesh = mesh;

			water.Material = new JMaterial[1];
			water.Material[0] = Jad.Scene.Materials.Load("calm_water.material");

			float degress = Geometry.DegreeToRadian(10);

			float y = (depth / 2) * (float) Math.Sin(degress);
			water.Transform.Position = new Vector3(0, -y, depth / 2);

			// Now we need to create the absoluteTM

			water.TransformDown();

			waterPlane = new Vector4(0, 1, 0, water.Transform.Position.Y);

			JPhysics.BuoyancyPlane = waterPlane;

			CreateWaterContainer();
		}

		/// <summary>
		/// Creates the ground
		/// </summary>
		void CreateGround()
		{
			// Create a rectangle to use as a floor

			float depth = 300f;

			JMesh groundMesh = JMesh.CreateRectangle(5f, depth, 5, 150, JCreateRectangle.XZ, false, 1f, depth / 10f);

			// Add it some noise ...

			float noise = 0.5f;

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

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

			// Now we 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();

			material[0].RenderStates.CullMode = Cull.None;

			// 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 rotate a bit to make a litle of inclination

			float degress = Geometry.DegreeToRadian(10);

			ground.Transform.Angles = new Vector3(degress, 0, 0);
			float y = (depth / 2) * (float) Math.Sin(degress);
			ground.Transform.Position = new Vector3(0, -y, depth / 2);

			// Now we need to create the absoluteTM

			ground.TransformDown();

			// 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>
		/// Moves the airplane around the sphere
		/// </summary>
		void MoveAirPlane()
		{
			angleY += Jad.AtXSecond(4f, Geometry.DegreeToRadian(300));

			Vector3 pos = wheel.Transform.Position;

			pos.Y += 0.1f * (float) Math.Sin(Jad.Timer.Time * 4f);

			Matrix pivot = Matrix.Translation(new Vector3(1.5f, 0, 0));

			Matrix rotation = Matrix.RotationY(angleY);

			Matrix traslation = Matrix.Translation(pos);

			airPlane.Transform.LocalTM = pivot * rotation * traslation;
		}

		#endregion
	}
}

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

Comments

No comments yet.