Lets learn "How to build your first 3D Game !!"

 

3D Game Development Basics

In this tutorial, we will talk about Game development basics, hardwares which accelerates High order Game environment calculations, and at last some of my projects. This tutorial, assumes you know about OpenGL but if you have not worry not we try to explain every thing that we encounter. if you have queries feel free to leave a comment.

What is Game Development?

Game Development is different from Game Theory: Game Theory is an aspect of Artificial Intelligence and DataStruture Design, whereas Game Development Games are developed as a creative outlet and to generate profit. Development is normally funded by a publisher.

But let get some more basics as we proceed. If you want you can skip this subpart as most of this is concerned more about parallelism concepts rather than plain programming. Taking how our computer is able to to create so many 3d stuff so quickly. Well That 3D stuff is quite a larger product of combined effort of our processors and Graphical processing Unit. Processors read the game initials to decide what and how it's going to render as 3D. Then the Processed intermediate settings are transferred to GPU which has a high tendency for matrix related calculations, which game is all about, and this GPU calculates next feasible frame of our game based on input and processors out.
If you want to know Details about graphic cards see this
https://www.cs.virginia.edu/~gfx/papers/pdfs/59_HowThingsWork.pdf 
Our Aim:

  • Building a Boxing Game with 2 players in a ring.
  • The Players should be able to move around
  • Multiplayer game with the 2 players capable to play
  • Provide some measure to judge our setup.
Something like this:

You can take this one step ahead:


Tool setup:
Java:
Installing Java on Windows/Linux
Eclipse:
Installing Eclipse on Windows/Linux
OpenGL (JOGL version)
  1. get jogamp-all-platforms.7z, glugen-javadoc.7z, jogl-javadocs.7z from this site 
  2. go to eclipse - windows - preference - java - Build Path - User libraries
  3. Add New and give name 'Jogl' and click on 'Add External Jars'
  4. select jogl-all.jar, glugen-rt.jar, gluegen-rt-native-*, jogl-native-* (* - depending on platform for which you are developing) from folder jogamp-all-platform/jar.
  5. Now add location of there Native Libraries to this folder (jogamp-all-platform/jar).
  6. You can add there source code(optional) and click ok. More detailed steps can be found here.
SketchUP (or blender):

Make sure you are able to run this basic program in your pc setup:

package sample;
import java.awt.DisplayMode;


import javax.swing.JFrame;

import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.glu.GLU;
import com.jogamp.opengl.util.FPSAnimator;

public class Cube implements GLEventListener {

   public static DisplayMode dm, dm_old;
   private GLU glu = new GLU();
   private float rquad = 0.0f;
      
   @Override
   public void display( GLAutoDrawable drawable ) {
 
      final GL2 gl = drawable.getGL().getGL2(); //the GL object which does the rendering
      gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT ); //set paramters for background
      gl.glLoadIdentity(); // set identity matrix for our GL object
      gl.glTranslatef( 0f, 0f, -5.0f ); // simple translate the 3D world away from screen by 5 units

      // Rotate The Cube On X, Y & Z
      gl.glRotatef(rquad, 1.0f, 1.0f, 1.0f); // Now rotate about x,y,z axis by 1 unit
 
      //giving different colors to different sides
      gl.glBegin(GL2.GL_QUADS); // Start Drawing The Cube
      gl.glColor3f(1f,0f,0f); //red color
      gl.glVertex3f(1.0f, 1.0f, -1.0f); // Top Right Of The Quad (Top)
      gl.glVertex3f( -1.0f, 1.0f, -1.0f); // Top Left Of The Quad (Top)
      gl.glVertex3f( -1.0f, 1.0f, 1.0f ); // Bottom Left Of The Quad (Top)
      gl.glVertex3f( 1.0f, 1.0f, 1.0f ); // Bottom Right Of The Quad (Top)
  
      gl.glColor3f( 0f,1f,0f ); //green color
      gl.glVertex3f( 1.0f, -1.0f, 1.0f ); // Top Right Of The Quad
      gl.glVertex3f( -1.0f, -1.0f, 1.0f ); // Top Left Of The Quad
      gl.glVertex3f( -1.0f, -1.0f, -1.0f ); // Bottom Left Of The Quad
      gl.glVertex3f( 1.0f, -1.0f, -1.0f ); // Bottom Right Of The Quad 

      gl.glColor3f( 0f,0f,1f ); //blue color
      gl.glVertex3f( 1.0f, 1.0f, 1.0f ); // Top Right Of The Quad (Front)
      gl.glVertex3f( -1.0f, 1.0f, 1.0f ); // Top Left Of The Quad (Front)
      gl.glVertex3f( -1.0f, -1.0f, 1.0f ); // Bottom Left Of The Quad
      gl.glVertex3f( 1.0f, -1.0f, 1.0f ); // Bottom Right Of The Quad 

      gl.glColor3f( 1f,1f,0f ); //yellow (red + green)
      gl.glVertex3f( 1.0f, -1.0f, -1.0f ); // Bottom Left Of The Quad
      gl.glVertex3f( -1.0f, -1.0f, -1.0f ); // Bottom Right Of The Quad
      gl.glVertex3f( -1.0f, 1.0f, -1.0f ); // Top Right Of The Quad (Back)
      gl.glVertex3f( 1.0f, 1.0f, -1.0f ); // Top Left Of The Quad (Back)

      gl.glColor3f( 1f,0f,1f ); //purple (red + green)
      gl.glVertex3f( -1.0f, 1.0f, 1.0f ); // Top Right Of The Quad (Left)
      gl.glVertex3f( -1.0f, 1.0f, -1.0f ); // Top Left Of The Quad (Left)
      gl.glVertex3f( -1.0f, -1.0f, -1.0f ); // Bottom Left Of The Quad
      gl.glVertex3f( -1.0f, -1.0f, 1.0f ); // Bottom Right Of The Quad 

      gl.glColor3f( 0f,1f, 1f ); //sky blue (blue +green)
      gl.glVertex3f( 1.0f, 1.0f, -1.0f ); // Top Right Of The Quad (Right)
      gl.glVertex3f( 1.0f, 1.0f, 1.0f ); // Top Left Of The Quad
      gl.glVertex3f( 1.0f, -1.0f, 1.0f ); // Bottom Left Of The Quad
      gl.glVertex3f( 1.0f, -1.0f, -1.0f ); // Bottom Right Of The Quad
      gl.glEnd(); // Done Drawing The Quad
      gl.glFlush();
  
      rquad -= 0.15f;
   }
   
   @Override
   public void dispose( GLAutoDrawable drawable ) {
      // TODO Auto-generated method stub
   }
   
   @Override
   public void init( GLAutoDrawable drawable ) {
 // GL inilisations
      final GL2 gl = drawable.getGL().getGL2();
      gl.glShadeModel( GL2.GL_SMOOTH ); // Shading parameters
      gl.glClearColor( 0f, 0f, 0f, 0f ); 
      gl.glClearDepth( 1.0f );
      gl.glEnable( GL2.GL_DEPTH_TEST );
      gl.glDepthFunc( GL2.GL_LEQUAL );
      gl.glHint( GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST );
   }
      
   @Override
   public void reshape( GLAutoDrawable drawable, int x, int y, int width, int height ) {
 
      // TODO Auto-generated method stub
      final GL2 gl = drawable.getGL().getGL2();
      if( height <= 0 )
         height = 1;
   
      final float h = ( float ) width / ( float ) height;
      gl.glViewport( 0, 0, width, height ); 
      gl.glMatrixMode( GL2.GL_PROJECTION );
      gl.glLoadIdentity();
  
      glu.gluPerspective( 45.0f, h, 1.0, 20.0 );
      gl.glMatrixMode( GL2.GL_MODELVIEW );
      gl.glLoadIdentity();
   }
      
   public static void main( String[] args ) {
 
      final GLProfile profile = GLProfile.get( GLProfile.GL2 );
      GLCapabilities capabilities = new GLCapabilities( profile );
      
      // The canvas
      final GLCanvas glcanvas = new GLCanvas( capabilities );
      Cube cube = new Cube();
  
      glcanvas.addGLEventListener( cube );
      glcanvas.setSize( 400, 400 );
  
      final JFrame frame = new JFrame ( " Multicolored cube" );
      frame.getContentPane().add( glcanvas );
      frame.setSize( frame.getContentPane().getPreferredSize() );
      frame.setVisible( true );
      final FPSAnimator animator = new FPSAnimator(glcanvas, 300,true);
  
      animator.start();
   }
}

After this you may see things working as:


Now look briefly through the code, and you can see some basic rendering parameters and a basic rendering theory which is explained here.

Things that you can notice in this program
  • You only need vertices to define shape of object.
  • Any three vertices can define a planar surface
  • Colouring can be explicitly defined with the help of faces.
  • Other things that matter most are textures and normals of surface which is covered in code only(if you want you can see that part yourself)
Our target is to make a simple boxing game( as it was one of my project ). This project requires 
  • 2 boxer 3D object (same for both model)
  • 1 ring 3D object
You need to follow these basic procedures:
  1. First separate the moving parts of the model and engineer them to have there moving moving axis aligned with one particular axis in all the case. It will be much better if you have one of the positions fixed at origin, with dimensions of that part, calculated.
  2. Now we want to use these models into our software, for this we are going to use OBJ-MTL format.
  3. for more info, use these links, OBJ , MTL. If you look briefly into this OBJ format you can see how a 3D structure is represented in ASCII format. There are vertices and edges definition with colors corresponding to that particular face in mtl format file. We need a script which parses this file into proper matrices of VERTEX, EDGES, COLORS, INDEXES etc, so that we can render these into our OPENGL programs. A Fair parser has been modeled here.
  4. For 3D models you can use this site, It has various 3D models available freely for use. Now when you have your 3D file open it Sketchup or Blender and export it into OBJ format, with 3 vertices as one face, Notice that our code doesn't support texture but you can modify it to use textures and lightings.
  5. Now when our parser are ready we need to use these tuple to draw in our Game. Each tuple (EDGES, VERTICES etc) corresponding to different static parts of game (chest of boxer, head, hands ring). We need a script which can render these tuple at a given POSITION at a given ANGLE. This step is important. If you know where we are going, we just need to draw those static parts of game at different place, at different time and VOILA you have your game.
  6. At the end you need another script which converts you keyboard actions and convert them into there corresponding sequence of render placing all the said things at right positions.
The code for Parser is given for reference:

package parser;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;

import com.jogamp.opengl.GL2;

public class Experimental {
 private final int NUM_FACE_VERTICES = 3;
 private final int NUM_VERTEX_COORDS = 3;
 private final int NUM_TEX_COORDS = 2;
 private final FloatBuffer mVertexBuffer;
 private final FloatBuffer mColorBuffer;
 private final IntBuffer mIndexBuffer;
 private FileInputStream fis;
 private FileInputStream fis1;
 private TheThree light_source;

 public Experimental(String s) {
  setLight_source(new TheThree(1, 1, 1));

  // read in all the lines and put in their respective arraylists of
  // strings
  // reason I do this is to get a count of the faces to be used to
  // initialize the
  // float arrays
  ArrayList<String> vertexes = new ArrayList<String>();
  ArrayList<String> textures = new ArrayList<String>();
  ArrayList<String> faces = new ArrayList<String>();
  HashMap<String, Color> hm = new HashMap<String, Color>();
  try {
   fis = new FileInputStream(new File("res\\raw\\objs\\" + s + ".obj"));
   fis1 = new FileInputStream(new File("res\\raw\\mtls\\" + s + ".mtl"));
  } catch (FileNotFoundException e1) {
   // TODO Auto-generated catch block
   e1.printStackTrace();
  }
  BufferedReader br = new BufferedReader(new InputStreamReader(fis));
  BufferedReader br1 = new BufferedReader(new InputStreamReader(fis1));
  int colorindex = 0, face = 0;
  float r, g, b, a;
  String line, c, sim = null;
  Color tf = null;
  try {
   while ((line = br1.readLine()) != null) {
    if (line.startsWith("newmtl ")) {
     c = line.substring(7);
     while ((line = br1.readLine()) != null) {
      if (line.startsWith("Kd "))
       break;
     }
     if (line.startsWith("Kd ")) {
      System.out.println(line.substring(3));
      String[] col = line.substring(3).split(" ");
      r = Float.parseFloat(col[0]);
      g = Float.parseFloat(col[1]);
      b = Float.parseFloat(col[2]);
      a = 1.0f;
      Color p = new Color(r, g, b, a);
      // TheFour tf2 = new TheFour(r, g, b, a);
      System.out.println("!!!!" + p.getRed() + " "
        + p.getGreen() + " " + p.getBlue());
      hm.put(c, p);

      System.out.println("!pk!" + hm.get(c).getRed() + " "
        + hm.get(c).getGreen() + " "
        + hm.get(c).getBlue());
     }
    }
   }
  } catch (IOException e1) {
   // TODO Auto-generated catch block
   e1.printStackTrace();
  }
  try {
   while ((line = br.readLine()) != null) {
    // System.out.println(line);
    // do not read in the leading v, vt or f
    if (line.startsWith("v "))
     vertexes.add(line.substring(2));
    if (line.startsWith("vt "))
     textures.add(line.substring(3));
    if (line.startsWith("f ") || line.startsWith("usemtl ")) {
     if (line.startsWith("f ")) {
      face++;
      faces.add(line.substring(2));
     } else {
      faces.add("!" + line.substring(7));
     }

    }

   }
  } catch (IOException e) {
   e.printStackTrace();
  }

  for (String sd : faces) {
   System.out.println("!" + sd);
  }

  faces.size();
  // holding arrays for the vertices, texture coords and indexes
  float[] vCoords = new float[face * NUM_FACE_VERTICES
    * NUM_VERTEX_COORDS];
  float[] tCoords = new float[face * NUM_FACE_VERTICES * NUM_TEX_COORDS];
  int[] iCoords = new int[face * NUM_FACE_VERTICES];
  float[] iColors = new float[face * NUM_FACE_VERTICES * 4];

  int vertexIndex = 0;
  int faceIndex = 0;
  int textureIndex = 0;
  // for each face
  for (String i : faces) {
   // for each face component
   if (i.startsWith("!")) {
    sim = i.substring(1);
    tf = hm.get(sim);
    System.out.println("@" + sim + "@");
   } else {
    for (String j : i.split(" ")) {

     iCoords[faceIndex] = faceIndex++;
     // iColors[colorindex++] = tf.getRed() / 255.0f;
     // iColors[colorindex++] = tf.getGreen() / 255.0f;
     // iColors[colorindex++] = tf.getBlue() / 255.0f;
     // iColors[colorindex++] = tf.getAlpha() / 255.0f;
     System.out.println("!@!" + tf.getRed() + " "
       + tf.getGreen() + " " + tf.getBlue());
     String[] faceComponent = j.split("/");

     String vertex = vertexes.get(Integer
       .parseInt(faceComponent[0]) - 1);
     String texture = textures.get(Integer
       .parseInt(faceComponent[1]) - 1);
     String vertexComp[] = vertex.split(" ");
     String textureComp[] = texture.split(" ");

     for (String v : vertexComp) {
      vCoords[vertexIndex++] = Float.parseFloat(v);

     }

     for (String t : textureComp) {
      tCoords[textureIndex++] = Float.parseFloat(t);
     }
    }
    float z1 = vCoords[vertexIndex - 1], y1 = vCoords[vertexIndex - 2], x1 = vCoords[vertexIndex - 3];
    float z2 = vCoords[vertexIndex - 4], y2 = vCoords[vertexIndex - 5], x2 = vCoords[vertexIndex - 6];
    float z3 = vCoords[vertexIndex - 7], y3 = vCoords[vertexIndex - 8], x3 = vCoords[vertexIndex - 9];

    float xa = x1 - x3, ya = y1 - y3, za = z1 - z3;
    float xb = x2 - x3, yb = y2 - y3, zb = z2 - z3;
    float xp = ya * zb - za * yb, yp = xb * za - xa * zb, zp = xa
      * yb - ya * xb;
    float rp = (float) Math.sqrt(xp * xp + yp * yp + zp * zp);
    float xans = xp / rp, yans = yp / rp, zans = zp / rp;
    rp = (float) Math.sqrt(light_source.getX()
      * light_source.getX() + light_source.getY()
      * light_source.getY() + light_source.getZ()
      * light_source.getZ());
    float lxans = light_source.getX() / rp, lyans = light_source
      .getY() / rp, lzans = light_source.getZ() / rp;
    float factor_light = (float) ((-(Math.pow(lxans * xans + lyans * yans + lzans
      * zans, 4)) + 2) * .3f);
    iColors[colorindex++] = tf.getRed() / (255.0f * factor_light);
    iColors[colorindex++] = tf.getGreen() / (255.0f * factor_light);
    iColors[colorindex++] = tf.getBlue() / (255.0f * factor_light);
    iColors[colorindex++] = tf.getAlpha() / (255.0f);
    iColors[colorindex++] = tf.getRed() / (255.0f * factor_light);
    iColors[colorindex++] = tf.getGreen() / (255.0f * factor_light);
    iColors[colorindex++] = tf.getBlue() / (255.0f * factor_light);
    iColors[colorindex++] = tf.getAlpha() / (255.0f);
    iColors[colorindex++] = tf.getRed() / (255.0f * factor_light);
    iColors[colorindex++] = tf.getGreen() / (255.0f * factor_light);
    iColors[colorindex++] = tf.getBlue() / (255.0f * factor_light);
    iColors[colorindex++] = tf.getAlpha() / (255.0f);

   }
  }

  // create the final buffers
  mVertexBuffer = makeFloatBuffer(vCoords);
  mIndexBuffer = makeIntBuffer(iCoords);
  mColorBuffer = makeFloatBuffer(iColors);
  makeFloatBuffer(tCoords);

 }

 private FloatBuffer makeFloatBuffer(float[] arr) {
  ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);
  bb.order(ByteOrder.nativeOrder());
  FloatBuffer fb = bb.asFloatBuffer();
  fb.put(arr);
  fb.position(0);
  return fb;
 }

 private IntBuffer makeIntBuffer(int[] arr) {
  ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);
  bb.order(ByteOrder.nativeOrder());
  IntBuffer ib = bb.asIntBuffer();
  ib.put(arr);
  ib.position(0);
  return ib;
 }

 public void draw(GL2 gl) {

  gl.glFrontFace(GL2.GL_CCW); // Front face in counter-clockwise
         // orientation
  gl.glEnable(GL2.GL_CULL_FACE); // Enable cull face
  gl.glCullFace(GL2.GL_BACK); // Cull the back face (don't display)

  gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
  gl.glEnableClientState(GL2.GL_COLOR_ARRAY);
  // gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
  // gl.glColor4f(0.5f, 0.5f, 0.1f, 1.0f);
  gl.glVertexPointer(NUM_VERTEX_COORDS, GL2.GL_FLOAT, 0, mVertexBuffer);
  gl.glColorPointer(4, GL2.GL_FLOAT, 0, mColorBuffer);
  // gl.glTexCoordPointer(NUM_TEX_COORDS, GL10.GL_FLOAT, 0, mTexBuffer);
  gl.glDrawElements(GL2.GL_TRIANGLES, mIndexBuffer.remaining(),
    GL2.GL_UNSIGNED_INT, mIndexBuffer);
  // gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
  gl.glDisableClientState(GL2.GL_COLOR_ARRAY);
  gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);

 }

 public TheThree getLight_source() {
  return light_source;
 }

 public void setLight_source(TheThree light_source) {
  this.light_source = light_source;
 }
}

One last demonstration of 3D GL. You can the work from Game environment to HIGHER GL RESPONSIVE FIELDS like GLmaps, GLcads etc. This is a Map of college Indian Institute of Technology (Indian School of Mined) Dhanbad created using GLAndroid.


You can find whole bag of code here. Place a comment if you need more help, or you have a question. 





Comments

Popular posts from this blog

Lets learn "About kube proxy in iptables mode"

Lets learn "System design for paste bin (or any text sharing website)"

Lets learn "What is CDN?"