For our graphics projects, for Computer Science honours at Rhodes University, we were given the task of creating a 3D environment using OpenGL and Renderman, containing certain fundamental features (that I will list below).

The Project allowed the choice between two scenarios, either:

The features (that I mentioned earlier) that were to be included in the project in some creative way are:

I decided to do the fantasy Island option as my project. The first concern for me was creating the landscape. After some initial thought it occured to me that I would want a floor with alternating levels, so a grid which stored height values, but those height values must not be too varied, you don't want too many sudden changes, you basically want your changes to fall within a bell shaped curve of occurance. Much help tp Yusuf for helping me put this algorithm into code. It is set up in the constructor of the GLWorld class. During the making of the island I also added the lights to code, so that we would be able to see the images.

The code to implement the height map of the island looks like this:

	level [0][0] = 0.0;
	for (int i = 0; i< tiles; i++)
	{
	   for (int j = 0; j< tiles; j++)
	   {
	      if (j == 0 || j == tiles-1 || i == 0 || i == tiles-1)level[j][i] = 0.0;
	      else {
	         level[j][i] = rand()%100 > 50 && level[j-1][i]-level_change > 0 ? level[j-1][i] - level_change : level[j-1][i] + level_change;
	         level[j][i] = (level[j][i-1]+level[j][i])/2.0;
	      }//else
            
	   }//middle for
	   
	}//first for, setting up random heights of the terrain

The lights are set up within the GLWorld class:

	int light1;
	int light2;
	int light3;   

//...

	light1 = 1;
	light2 = 0;
	light3 = 0;
//...

	tiSetLight (0, 0.0, 0.6, 0.1, 1.0);
        tiSetLight (1, 0.0, 1.0, 0.2, 0.0);
        tiSetLight (2, 0.0, 0.4, 1.0, 0.0);
//...
        // fixed light, relative to floor. 
        GLfloat light2pos [] = { -2.0, 30.0, 30.0, 1.0 };
	GLfloat light2dir [] = { 2.0, -30.0, -30.0, 0.0};
        glLightfv (GL_LIGHT2, GL_POSITION, light2pos);
	glLightfv (GL_LIGHT2, GL_SPOT_DIRECTION, light2dir);

The floor was drawn initially as follows:


      for (int i = 0; i < tiles; i++)
      {
         for (int j = 0; j < tiles; j++)
         {
            double x = tilesize * (i - tiles / 2);
            double z = tilesize * (j - tiles / 2);
    	    if (((i + j) / 10) % 2)
            {
	         GLfloat material_ambient[] = { 0.0, 0.6, 0.3, 1.0 };
	         GLfloat material_diffuse [] = { 0.0, 0.7, 0.2, 1.0 };
                 GLfloat material_specular [] = { 0.0, 0.4, 0.2, 0.8 };
                 GLfloat material_emission [] = { 0.01, 0.3, 0.1, 0.0 };
        	     
                 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
        	 glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
                 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,
                             material_specular);
		 glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION,
	                       material_emission);
                 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 2.0);
	    }
            else
            {   
               GLfloat material_ambient[] = { 0.0, 0.6, 0.2, 0.0 };
               GLfloat material_diffuse [] = { 0.0, 0.9, 0.2, 0.0 };
               GLfloat material_specular [] = { 0.0, 0.7, 0.2, 0.0 };
               GLfloat material_emission [] = { 0.01, 0.3, 0.1, 0.0 };
    	         
               glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
	       glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
               glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,
    		              material_specular);
	       glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION,
                             material_emission);
    	       glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 4.0);
	    }
		 
            tiPrimitiveBegin (TYPE_POLYGON);
              //tiPrimitiveNormal (sin(x * 0.1), 1.0, sin(z * 0.1));
              tiPrimitiveTextureCoordinate(0.0, 0.0);
	      tiPrimitiveVertex (x, level[i][j], z); 
              tiPrimitiveTextureCoordinate(0.0, 1.0);
	      tiPrimitiveVertex (x + tilesize, level[i+1][j], z);
	      tiPrimitiveTextureCoordinate(1.0, 1.0);
              tiPrimitiveVertex (x + tilesize, level[i+1][j+1], z + tilesize);
              tiPrimitiveTextureCoordinate(1.0, 0.0);
	      tiPrimitiveVertex (x, level[i][j+1], z + tilesize);
	    tiPrimitiveEnd ();
	 }
      }   

The result is an island that looks as follows:
View 1 of Island

and then from different angles I have:

View 2 of the Island

View 3 of the Island

View 4 of the Island

After having accomplished an island (and doing my little dance of joy ;-) I moved onto navigation through my little Island. I wanted to have keys to let the user navigate themselves through the Island and also that the camera would follow a set path via a spline. This took some time, again thanks to Yusuf and David for help with the special keys, which was then followed by the spline. The spline used is that of a Bezier spline, I had considered a B-spline with knotts, inorder to have a little circuit right round the island, but I decided in the end it was more work and over-kill.

The code for navigation was relatively simple, for the user governed navigation, I did this:


   void keyPressEvent(QKeyEvent* e) {
      switch (e->key()) {
	case Key_A: m_vy-=0.05; break;
	case Key_Z: m_vy+=0.05; break;
        case Key_X: m_vx+=0.05; break;
        case Key_C: m_vx-=0.05; break;    
	case Key_K: m_vz+=0.05; break;
	case Key_M: m_vz-=0.05; break;
        case Key_T: m_rx+=1.0; break;
	case Key_Y: m_rx-=1.0; break;
        case Key_G: m_ry+=1.0; break;
        case Key_H: m_ry-=1.0; break; 
	case Key_B: m_rz+=1.0; break;
        case Key_N: m_rz-=1.0; break;
      }
    }

and the spline was relatively simple after that: here is the source code and then it is implemented (called) from painGL() in the GLWorld class.
   
      //camera moving via Spline, calls to the splineAt function in the
      //spline class
      spline->splineAt(actualpos, vx, vz);
The GLWorld class Source code

After adding the camera motion, it occured to me that my little island needed some sort of texture, so next was the quick and easy step of adding the texture, which in the end was the flowers that you saw on the island on the images above.

This was accomplished by adding the TextureLandscape Class, again source code here and including the following lines of code in my drawfloor() in GLWorld class:

     int textureid = TLS->textureID();
 
      glEnable (GL_TEXTURE_2D);
      glBindTexture (GL_TEXTURE_2D, textureid);

	....

      glDisable (GL_TEXTURE_2D);

The next task I undertook was to create the boid, or flocking algorithm. I created a flocking class and then instantiated the class in the GLWorld class, in the paintGl class. This probably took the most time for me, my knowledge of C and graphics being very limited, basically this course being the most it has been challenged. Again many thanks to Yusuf for his endless patience in explaining and really clever algorithms for accomplishing all that I wanted to do. Once I had a flock of square boids floating around my island I decided to give them some personality, so I made them a flock of dragons and mapped a dragon image onto each of them. Actually I found a number of cool dragon images, so I tend to change them regularly for a bit of variety ;-)

As I said, the boids, took a while, they can be found in the flocking class, source code The class was then made use of in the paintGL() in the GLWorld class:

      
      //call boid class and draw them
      for (int i = 0; i < 10; i++)
      {
          f->myFlock[i].boidIndex = i;
	  f->updateFlock(f->myFlock[i]);
      } 

The texture of the boids, namely creating my flock of dragons, was added to the boids in much the same way that the texturing was done for landscape. Its all in the source code.

This is what my island looks like with my boid flying around:

Misty Dragons

Misty Dragons

Black silhouettes of dragons

Black silhouettes of dragons

At this point I have accomplished 5 out of 8 tasks. I have:

What I haven't done is:


The reasons for this are, there is currently so much other work that needs to be done, that I actually have to remove this off my todo list and continue with those, as well as I am not confident in my abilities to do any more on this project.

So basically, here is where I stop. This is, unfortunately, as far as I can go with this endeavour. It has been a lot of fun and a major learning experiance and I think in my fact I will play around a lot more with graphics and see what else I can learn.

Everything, (with the other classes needed to make everything work well) all together is available in source code here