Introduction

Author: Movania Muhammad Mobeen

Hello readers, In this article, we will learn how to port the 11th tutorial on Physics Dynamics, Acceleration and Force in OpenGL 3.3. We saw in the last tutorial how to use the SDL framework to handle our window management. We will put the greater flexibility offered by SDL to good use by doing realtime physics calculation. The OpenGL rendering code, the geometry management and shaders for the meshes are identical to the last tutorial so we would not discuss them again here. The new additions to this tutorial are at two places:

  • Modification of the main loop to enable realtime physics
  • Functions to apply dynamics on the object

Modification of the main loop to enable realtime physics

Timing is critical for any simulation software. Realizing this, we have added new variables to main.cpp:

	// FPS calculation and time based physics
	unsigned int fps_physics=0;
	unsigned int fps_rendering=0;
	unsigned int set_fps_physics=100;
	unsigned int remaining_time=0;
	unsigned long fps_count_physics=0;
	unsigned long fps_count_rendering=0;
	unsigned long last_ticks=0;
	int msecxdframe=1000/set_fps_physics;

These variables and their purpose are given in the following table:

New variables and their purpose

VariablePurpose
fps_physicsFor timing of physics calcualtion
fps_renderingFor rendering of fps on display
set_fps_physicsFor sustained physics performance
remaining_time Remaining time for time based physics
fps_count_physicsFPS for physics engine
fps_count_renderingFPS for rendering engine
last_ticksNumber of ticks since last frame so that delta time could be obtained
msecxdframeMilliseconds we want for each physics frame

The main loop is modified by adding new variables for storing the current, last and elapsed time:

	void MainLoop(void)
	{
	   int i; // Counters
	   unsigned long l_start_time; // Start time for time based physics
	   unsigned long l_elapsed_time; // Elapsed time for time based physics
	   unsigned int l_frame_time; // Frame time for time based physics

Next, the frameworks events are handled:

	   // Events
	   FrameworkEvents(); // Process incoming events

This ensures that the application remains responsive to the user's input as well as the common window events and it does not stall. Next, the current time is obtained (using Framework_GetTicks function) and the display function (RenderDisplay) is called. Then, the elapsed time is calculated. This also gives us the time taken for the current frame (l_frame_time) and the remaining time (remaining_time) for physics calculation:

	   // Elapsed time calculation
	   l_elapsed_time=Framework_GetTicks()-l_start_time+remaining_time; // Elapsed time (we add also the previous remaining time)
	   l_frame_time=l_elapsed_time / msecxdframe; // Frames quantity we must cycle for physics
	   remaining_time=l_elapsed_time % msecxdframe; // Get the remaining time because we are working with integer values

Next, the physics loop is run which loops until all of the current frame's render time is used. It increases the current physics fps. Then, it loops through each object and applies drag and dynamics forces on it:

	// Physics
	while (l_frame_time-->0) // Now do physics as many times as we need to match the elapsed time of the rendering phase
	{
	   fps_count_physics++; // Increase physics FPS counter

	   for(i=0;i< obj_qty;i++) //
	   {
	      ObjDrag(&object[i]); // Add Drag
	      ObjDynamics(&object[i],(float)1.0/(float)set_fps_physics); // Do dynamics
	   }
	}

Finally, the fps for display and physics calculation are recalculated and the counters are reset for the next frame:

	// Rendering and Physics FPS calculation
	if ((Framework_GetTicks()-last_ticks)>=1000) // Every second
	{
	   last_ticks = Framework_GetTicks(); // Save the current ticks to catch the next second
	   // Assings the current FPS count to the global variables
	   fps_physics=fps_count_physics;
	   fps_rendering=fps_count_rendering;
	   // Clear the local counters
	   fps_count_physics = 0;
	   fps_count_rendering = 0;
	}

Note that all of the functions calculating the physics like ObjDrag, ObjDynamics etc. are based on physical laws and we are not discussing them here since this information is well desribed in elementary physics text.

Handling of the object's axially aligned bounding box (AABB)

When the mesh is loaded, the object's axially aligned bounding box (AABB) is calculated in the ObjCalcBSphere function. This function is called from ObjLoadFromIni function at initialization. The AABB is stored as an object attribute along with the object's other attributes like the object's transformation matrix. After the aabb has been calculated, we create space to store the aabb so that we may render them later. As before, VAOs come to our rescue. We create a new VAO id for each object and store it in a vector called vaoAABBIDs since we may not know the actual number of objects. Similarly we create vboAABBIDs for storing the aabb vertex positions:

	//allocate space for the vaos and vbos
	vaoIDs.resize(obj_qty);
	vboIDs.resize(obj_qty*4);
	vaoAABBIDs.resize(obj_qty);
	vboAABBIDs.resize(obj_qty);

Once we have successfully generated our vectors, we call the InitVAO function. This function as before loads the vertex positions, texture coordinates and normals into the VBO. In addition, now it also binds the new AABB vao (vaoAABBIDs) and pushes the AABB vertex positions. Note that these vertex positions are in object space:

	//now attach the AABB vao and dump the aabb vertices
	glBindVertexArray(vaoAABBIDs[i]);
	glBindBuffer (GL_ARRAY_BUFFER, vboAABBIDs[i]);
	glBufferData (GL_ARRAY_BUFFER, sizeof(GLfloat)*3*8, &object[i].aabb, GL_STATIC_DRAW);
	glEnableVertexAttribArray(aabb_shader["vVertex"]);
	glVertexAttribPointer (aabb_shader["vVertex"], 3, GL_FLOAT, GL_FALSE,stride,0);
	glBindVertexArray(0);

Note that we have a new shader (discussed later) for our AABB since we do not want to texture the AABB vertices and are only interested to color them with a constant color.

AABB shaders

The vertex shader for the AABB is exactly the same as the previous tutorials and it should be easy to undertand now:

	//Vertex shader
	#version 330
	in vec3 vVertex;
	uniform mat4 MVP;
	void main()
	{
	   gl_Position = MVP*vec4(vVertex,1);
	}

The fragment shader has been simplified as follows:

	//Fragment shader
	#version 330
	uniform vec4 Color;
	out vec4 vFragColor;
	void main(void)
	{
	   vFragColor = Color;
	}

We pass a new uniform (Color) which is passed when the shader is used. This way we can modify the color from the client code to assign a different color as we need.

AABB shader loading

As earlier, we create a new GLSLShader object (aabb_shader) and then we load the shader using the aabb_shader.LoadFromFile function as follows:

	aabb_shader.LoadFromFile(GL_VERTEX_SHADER, "shaders/aabb_shader.vert");
	aabb_shader.LoadFromFile(GL_FRAGMENT_SHADER, "shaders/aabb_shader.frag");

Once the aabb_shader is successfully compiled and linked, we set the shader attributes and uniforms:

	aabb_shader.CreateAndLinkProgram();
	aabb_shader.Use();
	   aabb_shader.AddAttribute("vVertex");
	   aabb_shader.AddUniform("MVP");
	   aabb_shader.AddUniform("Color");
	aabb_shader.UnUse();

In the rendering code, after the ith mesh is rendered, its AABB is rendered as follows:

	//draw the aabb points
	aabb_shader.Use();
	   #ifdef USE_GLM
	      glUniformMatrix4fv(aabb_shader("MVP"), 1, GL_FALSE, glm::value_ptr(MVP));
	   #else
	      glUniformMatrix4fv(aabb_shader("MVP"), 1, GL_FALSE, &MVP[0][0]);
	   #endif

	   glUniform4fv(aabb_shader("Color"),1, white);
	   glBindVertexArray(vaoAABBIDs[i]);
	      glDrawArrays(GL_POINTS, 0, 8);
	   glBindVertexArray(0);
	aabb_shader.UnUse();

The first few lines use the aabb_shader and then set the combined modelview projection matrix based on the current math library being used. Then, the AABB shader's color uniform is set to a white color (defined earlier as GLfloat white[4]={1,1,1,1}). Next, the AABB's VAO is bound and then 8 GL_POINTS are rendered. Finally, the VAO is unbound and the shader is un used.

Choosing between GLUT framework or SDL framework

The code in this and the subsequent tutorials has two frameworks to choose from. The glut framework that we developed in the last tutorial and the SDL framework that we developed in this tutorial. To use GLUT framework, you need to define the preprocessor FRAMEWORK_GLUT in your compiler setting. In Visual Studio 2008, you can go to Project -> Properties -> Configuration Properties-> C/C++ ->Preprocessor and add FRAMEWORK_GLUT to the preprocessor definitions. To enable SDL framework, define FRAMEWORK_SDL. Note that you can only use one framework at a time and not both. In addition, if you want to use the GLM matrix library, add the preprocessor USE_GLM otherwise the spacesimulator.net's matrix library is used. Running the code gives us the following output. You may press the 'w','a','s','d' keys along with several other keys to transform the camera and see the result:

Tut physics opengl3d3.png

SOURCE CODE

The Source Code of this lesson can be downloaded from the Tutorials Main Page