Выбрать главу

inline float DotProduct (VECTOR &V1, VECTOR &V2) // Calculate The Angle Between The 2 Vectors

{

 return V1.X * V2.X + V1.Y * V2.Y + V1.Z * V2.Z; // Return The Angle

}

inline float Magnitude (VECTOR &V) // Calculate The Length Of The Vector

{

 return sqrtf (V.X * V.X + V.Y * V.Y + V.Z * V.Z); // Return The Length Of The Vector

}

void Normalize (VECTOR &V) // Creates A Vector With A Unit Length Of 1

{

 float M = Magnitude (V); // Calculate The Length Of The Vector

 if (M != 0.0f) // Make Sure We Don't Divide By 0

 {

  V.X /= M; // Normalize The 3 Components

  V.Y /= M;

  V.Z /= M;

 }

}

This function rotates a vector using the matrix provided. Please note that it ONLY rotates the vector — it has nothing to do with the position of the vector. This is used when rotating normals to make sure that they stay pointing in the right direction when we calculate the lighting.

void RotateVector (MATRIX &M, VECTOR &V, VECTOR &D) // Rotate A Vector Using The Supplied Matrix

{

 D.X = (M.Data[0] * V.X) + (M.Data[4] * V.Y) + (M.Data[8] * V.Z); // Rotate Around The X Axis

 D.Y = (M.Data[1] * V.X) + (M.Data[5] * V.Y) + (M.Data[9] * V.Z); // Rotate Around The Y Axis

 D.Z = (M.Data[2] * V.X) + (M.Data[6] * V.Y) + (M.Data[10] * V.Z); // Rotate Around The Z Axis

}

The first major function of the engine, Initialize does exactly what is says. I've cut out a few lines of code as they are not needed in the explaination.

// Any GL Init Code & User Initialiazation Goes Here

BOOL Initialize (GL_Window* window, Keys* keys) {

These 3 variables are used to load the shader file. Line contains space for a single line in the text file, while shaderData stores the actual shader values. You may be wondering why we have 96 values instead of 32. Well, we need to convert the greyscale values to RGB so that OpenGL can use them. We can still store the values as greyscale, but we will simply use the same value for the R, G and B components when uploading the texture.

 char Line[255]; // Storage For 255 Characters

 float shaderData[32][3]; // Storate For The 96 Shader Values

 FILE *In = NULL; // File Pointer

When drawing the lines, we want to make sure that they are nice and smooth. Initially this value is turned off, but by pressing the "2" key, it can be toggled on/off.

 glShadeModel (GL_SMOOTH); // Enables Smooth Color Shading

 glDisable (GL_LINE_SMOOTH); // Initially Disable Line Smoothing

 glEnable (GL_CULL_FACE); // Enable OpenGL Face Culling

We disable OpenGL lighting because we do all of the lighting calculations ourself.

 glDisable (GL_LIGHTING); // Disable OpenGL Lighting

Here is where we load the shader file. It is simply 32 floating point values stored as ASCII (for easy modification), each one on a seperate line.

 In = fopen ("Data\\shader.txt", "r"); // Open The Shader File

 if (In) // Check To See If The File Opened

 {

  for (i = 0; i < 32; i++) // Loop Though The 32 Greyscale Values

  {

   if (feof (In)) // Check For The End Of The File

    break;

   fgets (Line, 255, In); // Get The Current Line

Here we convert the greyscale value into RGB, as described above.

   // Copy Over The Value

   shaderData[i][0] = shaderData[i][1] = shaderData[i][2] = atof (Line);

  }

  fclose (In); // Close The File

 } else return FALSE; // It Went Horribly Horribly Wrong

Now we upload the texture. At it clearly states, do not use any kind of filtering on the texture or else it will look odd, to say the least. GL_TEXTURE_1D is used because it is a 1D array of values.

 glGenTextures (1, &shaderTexture[0]); // Get A Free Texture ID

 glBindTexture (GL_TEXTURE_1D, shaderTexture[0]); // Bind This Texture. From Now On It Will Be 1D

 // For Crying Out Loud Don't Let OpenGL Use Bi/Trilinear Filtering!

 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

 // Upload

 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGB, 32, 0, GL_RGB , GL_FLOAT, shaderData);

Now set the lighting direction. I've got it pointing down positive Z, which means it's going to hit the model face-on.

 lightAngle.X = 0.0f; // Set The X Direction

 lightAngle.Y = 0.0f; // Set The Y Direction

 lightAngle.Z = 1.0f; // Set The Z Direction

 Normalize (lightAngle); // Normalize The Light Direction

Load in the mesh from file (described above).

 return ReadMesh (); // Return The Value Of ReadMesh

}

The opposite of the above function, Deinitalize deletes the texture and polygon data created by Initalize and ReadMesh.

void Deinitialize (void) // Any User DeInitialization Goes Here

{

 glDeleteTextures (1, &shaderTexture[0]); // Delete The Shader Texture

 delete [] polyData; // Delete The Polygon Data

}

The main demo loop. All this does is process the input and update the angle. Controls are as follows:

<SPACE> = Toggle rotation

1 = Toggle outline drawing

2 = Toggle outline anti-alaising

<UP> = Increase line width

<DOWM> = Decrease line width

void Update (DWORD milliseconds) // Perform Motion Updates Here

{

 if (g_keys->keyDown [' '] == TRUE) // Is the Space Bar Being Pressed?

 {

  modelRotate = !modelRotate; // Toggle Model Rotation On/Off

  g_keys->keyDown [' '] = FALSE;

 }

 if (g_keys->keyDown ['1'] == TRUE) // Is The Number 1 Being Pressed?

 {

  outlineDraw = !outlineDraw; // Toggle Outline Drawing On/Off

  g_keys->keyDown ['1'] = FALSE;

 }

 if (g_keys->keyDown ['2'] == TRUE) // Is The Number 2 Being Pressed?

 {

  outlineSmooth = !outlineSmooth; // Toggle Anti-Aliasing On/Off

  g_keys->keyDown ['2'] = FALSE;

 }

 if (g_keys->keyDown [VK_UP] == TRUE) // Is The Up Arrow Being Pressed?

 {

  outlineWidth++; // Increase Line Width

  g_keys->keyDown [VK_UP] = FALSE;

 }

 if (g_keys->keyDown [VK_DOWN] == TRUE) // Is The Down Arrow Being Pressed?

 {

  outlineWidth--; // Decrease Line Width

  g_keys->keyDown [VK_DOWN] = FALSE;

 }

 if (modelRotate) // Check To See If Rotation Is Enabled