/*

	Landscape Builder Demo - for KG/2007 exams
	Special Winter Edition - version 1.1
	 
	Added:
	  - Particle Effects - Snow
	  - Music Effects - Jingle Bells
	  - Light Effects - Moving Light Sphere
	  - Random Yolko Builder
	
	Created by ::
		Reznichenko Anton /AlterVision/
		Masyutin Dmitry /ExDmitry/

	Special Thanks to NeHe for 'help.chm' =)

*/

#include <windows.h>                
#include <stdio.h>	
#include <math.h>
#include <gl\gl.h>                      
#include <gl\glu.h>                    
#include <gl\glaux.h>                    

// UnComment in case on necessity ;)
// define true false

// Height Builder defines :: Start

#define		MAP_SIZE  256	// Bitmap Size
#define		STEP_SIZE  4	// Step of builder
#define		MAP_CHAN 1		// Chanell of bitmap, used for builder (0-red, 1-green, 2-blue)
#define		WATER_H	0		// Water Height
#define		SKY_H	45		// Water Height
#define		MN_HEIGHT -1	// Minimal Height of Map
#define		MX_HEIGHT 30	// Maximal Height of Map

struct ground_point {
	float x, y, z;		// Coords
	float nx, ny, nz;	// Normal Vector
};

BYTE g_HeightMap[MAP_SIZE * MAP_SIZE];		// Ground Height Map
ground_point g_matrix[MAP_SIZE * MAP_SIZE];	// Ground Matrix

// Height Builder Defines :: End

HDC    hDC = NULL;      // GDI Context
HGLRC  hRC = NULL;      // Render Context
HWND   hWnd = NULL;     // Window Descroptor
HINSTANCE hInstance;    // App Dercriptor

// Particle Effects Snow :: Start

#define SNOW_COUNT 12000	// Particles Count
#define MIN_SNOW_DIST 20		// Particles Far Distance
#define MAX_SNOW_DIST 120	// Particles Near Distance
#define SNOW_SPEED 0.2f		// Particles Drop Speed
#define SNOW_SIZE 0.08f		// Particles Size

struct snow {		// Structure for one particle
	float x, y, z;	// Coordinates
	float speed;	// Particle Drop Speed
	float size;		// Particle Size
};
snow snows[SNOW_COUNT]; // Array of particles

// Particle Effects Snow :: End

// Random Yolko Generator :: Start
#define YOLKO_COUNT		5
#define YOLKO_HEIGHT	7.0f
#define YOLKO_WIDTH		4.0f

struct yolko {
	float x,y,z;	// Coordinates
	float scale;	// Scale
};
yolko yolkas[YOLKO_COUNT];	// Array of Yolkas

// Random Yolko Generator :: End

bool keys[256];				// KeyPress Array
bool fullscreen = false;		// Fullscreen Mode Flag
bool wireframe = false;		// Wireframe Mode Flag
bool reflections = true;	// Reflections Flag
bool music = false;			// Music Play Flag
bool normal = true;			// Martix Normals
bool snowing = true;		// Weather Control =)
bool snowtex = true;		// Snow Texturing
bool light = true;			// Lighting
bool yolker = false;		// Yolker
bool wp,rp,mp,sp,tp,np,lp,qp;

// Walker Defines :: Start

#define MAX_SPEED 1.0f	// Walker Maximum Speed
#define CAM_HEIGHT 5.0f // Camera Height over Ground

float wx, wy, wz;		// Walker Coordinates
float cx, cy, cz;		// Camera Viewpoint
float yang, xang;		// Camera Rotation Angles
float wspeed, sspeed;	// Walk/Streif Speed on X/Z-Axis

#define MOUSE_SENS 0.3f // Mouse Sensivity
#define MOUSE_STX 320	// Mouse Start X Position
#define MOUSE_STY 240	// Mouse Start Y Position

int mmvx, mmvy;			// MouseMove x/y

// Walker Defines :: End

#define SCREEN_X 1024
#define SCREEN_Y 768

GLfloat LightAmbient[]  = { 0.2f, 0.2f, 0.2f, 1.0f };    // Значения фонового света 
GLfloat LightDiffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };    // Значения диффузного света 
GLfloat LightPosition[] = { 0.12f, 1.0f, 0.15f, 0.0f };    // Позиция света 
GLfloat LightPosition2[]= { 0.0f, 0.0f, 1.0f, 0.0f };    // Позиция света 
GLfloat LightPosition3[]= { 0.0f, 0.0f, 1.0f, 1.0f };    // Позиция света 
GLfloat fogColor[]		= { 0.316f, 0.636f, 0.922f, 1.0f };	 // Цвет тумана

GLfloat SphereAmbient[]  = { 0.0f, 0.0f, 0.0f, 1.0f };    // Значения фонового света 
GLfloat SphereDiffuse[]  = { 0.0f, 1.0f, 0.0f, 1.0f };    // Значения диффузного света 
GLfloat SphereSpotDir[]	 = { 0.0f, -1.0f, 0.0f };
GLfloat SpherePosition[] = { 190.0f, 22.0f, 60.0f, 1.0f };    // Позиция света 
GLUquadricObj *spQMesh;

// Texturing
#define TEX_COUNT 7
LPSTR	texname[] = {
		"data/snow.bmp",		// 0 - snow
		"data/water.bmp",		// 1 - water
        "data/sky.bmp",			// 2 - sky
		"data/snowflake.bmp",	// 3 - snow image
		"data/snowmask.bmp",	// 4 - snow mask
		"data/yolko.bmp",		// 5 - yolko
		"data/yolkomask.bmp"	// 6 - yolko mask
};
GLuint	texture[TEX_COUNT];		// Our texture (three for now, may be we'll add some more later) ...

// SubRoutines
float g2r (float angle) { // Gradus to Radian
	if (angle>360)	angle -= 360;
	if (angle<0)	angle += 360;
	return angle * 3.141f * 2.0f / 360.0f;
}
float randf() {
	return (float) rand() / (float) RAND_MAX;
}

/*
	Textures Loading Function
	Uses 'texname'-array as a list of textures and TEX_COUNT as a number
*/
void LoadTextures () {
	AUX_RGBImageRec *teximg[TEX_COUNT];
	
	// Loading Files
	for (int i = 0; i < TEX_COUNT; i++) {
		teximg[i] = auxDIBImageLoad (texname[i]);
	}

	// Creating  textures
	glGenTextures(TEX_COUNT, &texture[0]);

	// Generating Textures
	for (int i = 0; i < TEX_COUNT; i++) {
		glBindTexture(GL_TEXTURE_2D, texture[i]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	//	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, teximg[i]->sizeX, teximg[i]->sizeY,  GL_RGB, GL_UNSIGNED_BYTE, teximg[i]->data); 
		glTexImage2D(GL_TEXTURE_2D, 0, 3, teximg[i]->sizeX, teximg[i]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, teximg[i]->data);
	}

	// Clearing Memory
	for (int i = 0; i < TEX_COUNT; i++) {    
		if (teximg[i]) {       
			if (teximg[i]->data) { 
				free(teximg[i]->data);
			}
			free(teximg[i]);     
		}
	}

}

// Getting the height of current point
float Height(BYTE *pHeightMap, int X, int Y)  {
	// Small AntiBug
	int x = X % MAP_SIZE;  // x for no more       
	int y = Y % MAP_SIZE;  // y for no more        
	if (!pHeightMap) return 0; 
	// All right - returning our height
	return ( (MX_HEIGHT - MN_HEIGHT) * ( (pHeightMap [x + (y * MAP_SIZE)]) / 256.0f) ) + MN_HEIGHT;  
}

// HeightMap Loading Function
void LoadHeightFile (LPSTR strName, int nSize, BYTE *pHeightMap, ground_point *pGMatrix) {
	
	// Defines
	FILE *pFile = NULL;						// File of Image
	AUX_RGBImageRec *heightmap;				// Our Image
	byte* cptr;								// Pointer to Color
	int pointptr = 0;						// Pointer (number) of current point
	int maxpoints = MAP_SIZE * MAP_SIZE;	// Maximum Points in Map

	// Checking for File
	pFile = fopen (strName, "rb");
	if (pFile == NULL) {
		MessageBox(NULL, "Can't Find The Height Map!", "Error", MB_OK);
		return;
	}
	fclose(pFile);

	// Loading Image
	heightmap = auxDIBImageLoad(strName);

	cptr = heightmap->data;	// Setting Pointer to Beginning if the Data
	cptr += MAP_CHAN;		// Setting Map Channel

	// Getting Points
	while (pointptr < maxpoints) {

		pHeightMap[pointptr] = *cptr;	// Getting Value via Pointer

		pointptr += 1;	// Increment current point by one
		cptr += 3;		// Increment pointer by three

	}

	// Crear Memory
	if (heightmap) {       
		if (heightmap->data) { 
			free(heightmap->data);
		}
		free(heightmap);     
	}

	for (int X = 0; X < MAP_SIZE; X++ ) {
		for (int Y = 0; Y < MAP_SIZE; Y++ ) {
			int id = X + (Y * MAP_SIZE);
			pGMatrix[id].x = (float) X;              
			pGMatrix[id].y = Height (pHeightMap, X, Y);  
			pGMatrix[id].z = (float) Y;    
		}
	}

	for (int X = 1; X < MAP_SIZE; X++ ) {
		for (int Y = 1; Y < MAP_SIZE; Y++ ) {

			int id = X + (Y * MAP_SIZE);
			int id2 = (X-1) + ((Y-1) * MAP_SIZE);
			int id4 = (X-1) + (Y * MAP_SIZE);
			int id8 = X + ((Y-1) * MAP_SIZE);

			float y1, y2, dy;

			float x1 = (X-1) * 25.0f;
			y1 = pGMatrix[id].y;
			float x2 = (X+0) * 25.0f;
			y2 = pGMatrix[id4].y;
			float dx = x2 - x1;
			dy = y2 - y1;
			float ax = g2r(90.0f) - atan2(dx, dy);

			float z1 = (Y-1) * 25.0f;
			y1 = pGMatrix[id2].y;
			float z2 = (Y+0) * 25.0f;
			y2 = pGMatrix[id8].y;
			float dz = z2 - z1;
			dy = y2 - y1;
			float az = g2r(90.0f) - atan2(dz, dy);


			pGMatrix[id].nx = sin(ax);
			pGMatrix[id].ny = cos(ax);
			pGMatrix[id].nz = sin(az);

		}
	}
}

float GetGroundHeight (BYTE *pHeightMap, float wx, float wz) {
	int iwx, iwz;					// Integer Parts of wx and wz
	float hpx0, hpx1, hpz0, hpz1;	// hard points for x/z-axis num 0/1
	float dx, dz;					// x/z bias
	float hb0, hb1;					// height bias 0/1
	float r_height;					// return height

	// If we are out of bounds, return zero
	if (wx < 0) return 0;
	if (wz < 0) return 0;
	if (wx > MAP_SIZE) return 0;
	if (wz > MAP_SIZE) return 0;
	// That was the easiest =)

	iwx = (int) (wx);
	iwz = (int) (wz);

	// If we are unlucky at all - General Case
	dx = wx - iwx;
	dz = wz - iwz;
	
	// Tambourine Activities
	hpx0 = Height (g_HeightMap, iwx, iwz);
	hpx1 = Height (g_HeightMap, iwx, iwz + 1);
	hpz0 = Height (g_HeightMap, iwx + 1, iwz);
	hpz1 = Height (g_HeightMap, iwx + 1, iwz + 1);

	hb0 = ( (hpx1 - hpx0) * dz ) + hpx0;
	hb1 = ( (hpz1 - hpz0) * dz ) + hpz0;

	r_height = ( (hb1 - hb0) *  dx) + hb0; // Buben-dance complete

	return r_height + CAM_HEIGHT;
}

/*
	Landscape Color Tone Function
	Sets special color tone for the landscape, based on the height of point
*/
void SetVertexColor (byte *pHeightMap, int x, int y) {
  if (! pHeightMap) return;     
  float fColor = 0.5f + ((pHeightMap [x + (y * MAP_SIZE)]) / 512.0f);
  glColor4f (fColor, fColor, fColor, 1.0f);
}

void RenderWater () {
	// Creating Water
	glColor4f (1.0f, 1.0f, 1.0f, 0.7f);
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);	glVertex3f (-0.5f * MAP_SIZE, WATER_H, -0.5f * MAP_SIZE);
		glTexCoord2f(0.0f, 10.0f);	glVertex3f (-0.5f * MAP_SIZE, WATER_H,  1.5f * MAP_SIZE);
		glTexCoord2f(10.0f, 10.0f);	glVertex3f ( 1.5f * MAP_SIZE, WATER_H,  1.5f * MAP_SIZE);
		glTexCoord2f(10.0f, 0.0f);	glVertex3f ( 1.5f * MAP_SIZE, WATER_H, -0.5f * MAP_SIZE);
	glEnd();
}

void RenderSkyBox () {
	// Creating Sky
	glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);	glVertex3f (-0.5f * MAP_SIZE, SKY_H, -0.5f * MAP_SIZE);
		glTexCoord2f(0.0f, 1.0f);	glVertex3f (-0.5f * MAP_SIZE, SKY_H,  1.5f * MAP_SIZE);
		glTexCoord2f(1.0f, 1.0f);	glVertex3f ( 1.5f * MAP_SIZE, SKY_H,  1.5f * MAP_SIZE);
		glTexCoord2f(1.0f, 0.0f);	glVertex3f ( 1.5f * MAP_SIZE, SKY_H, -0.5f * MAP_SIZE);
	glEnd();
}


void RenderSphere() {

	glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
	glBindTexture(GL_TEXTURE_2D, NULL);

	gluQuadricOrientation (spQMesh, GLU_OUTSIDE);
	gluQuadricNormals (spQMesh, GLU_SMOOTH);
	gluQuadricDrawStyle (spQMesh, GLU_FILL);

	glTranslatef(SpherePosition[0],SpherePosition[1]+0.5f,SpherePosition[2]);
	gluSphere (spQMesh, 0.5f, 16, 16);
	glTranslatef(-SpherePosition[0],-SpherePosition[1]-0.5f,-SpherePosition[2]);

	glLightfv(GL_LIGHT3, GL_POSITION, SpherePosition);  // Light Position
	glLightfv(GL_LIGHT3, GL_SPOT_DIRECTION, SphereSpotDir);

}

/*
	Landscape Rendering Function
	This is the main function of the program, which simply builds the landscape
*/
void RenderHeightMap(BYTE pHeightMap[], ground_point pGMatrix[]) 
{
	int X = 0, Y = 0;    // Coordinates to travel across our heights array
	int x, y, z;         // Three more for usability of setting the point
	if (!pHeightMap) return;     
	if (!pGMatrix) return;     

	// Texturing
	glBindTexture(GL_TEXTURE_2D, texture[0]);

	// Chosing rendering mode
	if (wireframe) {
		glBegin( GL_LINE_STRIP ); // draw lines for wireframe
	} else {
		glBegin( GL_QUADS ); // draw polygones for solid view
	}

	for ( X = 0; X < MAP_SIZE - STEP_SIZE; X += STEP_SIZE ) {
		for ( Y = 0; Y < MAP_SIZE - STEP_SIZE; Y += STEP_SIZE ) {
			int id;
		// Bottom Left Point of Quad
			id = X + (Y * MAP_SIZE);
			SetVertexColor(pHeightMap, (int) pGMatrix[id].x, (int) pGMatrix[id].z);
			glTexCoord2f(0.0f, 0.0f); 
			if (normal) glNormal3f (pGMatrix[id].nx, pGMatrix[id].ny, pGMatrix[id].nz);
			glVertex3f (pGMatrix[id].x,  pGMatrix[id].y,  pGMatrix[id].z);

		// Top Left Point of Quad
			id = X + ( (Y + STEP_SIZE) * MAP_SIZE);
			SetVertexColor(pHeightMap, (int) pGMatrix[id].x, (int) pGMatrix[id].z);
			glTexCoord2f(0.0f, 1.0f); 
			if (normal) glNormal3f (pGMatrix[id].nx, pGMatrix[id].ny, pGMatrix[id].nz);
			glVertex3f (pGMatrix[id].x,  pGMatrix[id].y,  pGMatrix[id].z);

		// Top Right Point of Quad
			id = (X + STEP_SIZE) + ( (Y + STEP_SIZE) * MAP_SIZE);			
			SetVertexColor(pHeightMap, (int) pGMatrix[id].x, (int) pGMatrix[id].z);
			glTexCoord2f(1.0f, 1.0f); 
			if (normal) glNormal3f (pGMatrix[id].nx, pGMatrix[id].ny, pGMatrix[id].nz);
			glVertex3f (pGMatrix[id].x,  pGMatrix[id].y,  pGMatrix[id].z);

		// Bottom Right Point of Quad
			id = (X + STEP_SIZE) + (Y * MAP_SIZE);			
			SetVertexColor(pHeightMap, (int) pGMatrix[id].x, (int) pGMatrix[id].z);
			glTexCoord2f(1.0f, 0.0f); 
			if (normal) glNormal3f (pGMatrix[id].nx, pGMatrix[id].ny, pGMatrix[id].nz);
			glVertex3f (pGMatrix[id].x,  pGMatrix[id].y,  pGMatrix[id].z);

		}
	}

	glEnd(); // Finishing our rendering

}

//
// Special Winter Snow Processor :: Start
//


// Creating Particles
void CreateSnow () {
	for (int i = 0; i < SNOW_COUNT; i++) {
		snows[i].x = wx + ((float) MAX_SNOW_DIST * (randf() - 0.5f));
		snows[i].y = (float) SKY_H * randf();
		snows[i].z = wz + ((float) MAX_SNOW_DIST * (randf() - 0.5f));
		snows[i].size = SNOW_SIZE * (randf() + 0.5f);
		snows[i].speed = SNOW_SPEED * (0.8f + 0.4f * randf());
	}
}

// Processing Particles
void ProcessSnow () {

	for (int i = 0; i < SNOW_COUNT; i++) {

		// Distance Between Walker and particle
		float dist = sqrt( (snows[i].x - wx) * (snows[i].x - wx) + (snows[i].z - wz) * (snows[i].z - wz) );
		if (dist > MAX_SNOW_DIST) {
			snows[i].x += 2.0f * (wx - snows[i].x);
			snows[i].z += 2.0f * (wz - snows[i].z);
		}

		// Height Over The Ground
		float snwh = GetGroundHeight(g_HeightMap, snows[i].x, snows[i].z) - CAM_HEIGHT;

		// If snow is too far or too deep
		if (snows[i].y < snwh) {

			// Create New Particle
			snows[i].x = wx + ((float) MAX_SNOW_DIST * (randf() - 0.5f));
			snows[i].y = (float) SKY_H;
			snows[i].z = wz + ((float) MAX_SNOW_DIST * (randf() - 0.5f));

			snows[i].size = SNOW_SIZE * (randf() + 0.5f);
			snows[i].speed = SNOW_SPEED * (0.8f + 0.4f * randf());

		} else {

			// Drop Snow Down
			snows[i].y -= snows[i].speed;

		}

	}

}

// Particle Output Function
void DrawSnowOne (snow onesnow, bool snowtx) {

	float smx = onesnow.x - onesnow.size * cos(g2r(yang));
	float smy = onesnow.y + onesnow.size;
	float smz = onesnow.z + onesnow.size * sin(g2r(yang));

	glNormal3f(0.0, 0.0, 1.0);

	if (snowtx) {
		// Draw With Texture Coordinates
		glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 0.0f); glVertex3f(onesnow.x, onesnow.y, onesnow.z);
			glTexCoord2f(1.0f, 0.0f); glVertex3f(smx, onesnow.y, smz);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(smx, smy, smz);
			glTexCoord2f(0.0f, 1.0f); glVertex3f(onesnow.x, smy, onesnow.z);
		glEnd();
	} else {
		// Draw Without Texture
		glBegin(GL_QUADS);
			glVertex3f(onesnow.x, onesnow.y, onesnow.z);
			glVertex3f(smx, onesnow.y, smz);
			glVertex3f(smx, smy, smz);
			glVertex3f(onesnow.x, smy, onesnow.z);
		glEnd();
	}

}

// Let It Snow !!
void RenderSnow () {

	glEnable(GL_LIGHT2);
	glLightfv(GL_LIGHT2, GL_POSITION, LightPosition2);  // Light Position

	glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
	for (int i = 0; i < SNOW_COUNT; i++) {

		if (snowtex) {	

			glEnable(GL_BLEND);       // Разрешение смешивания
			glDisable(GL_DEPTH_TEST); // Запрет теста глубины

			glBlendFunc(GL_DST_COLOR, GL_ZERO); // Смешивание цвета экрана с нулем (Черное)
			glBindTexture(GL_TEXTURE_2D, texture[4]); // Выбор первого изображения текстуры
			 DrawSnowOne(snows[i], true);
		
			glBlendFunc(GL_ONE, GL_ONE); // Копирование цветного изображения 1 на экран
			glBindTexture(GL_TEXTURE_2D, texture[3]); // Выбор первого изображения текстуры
			 DrawSnowOne(snows[i], true);

			glEnable(GL_DEPTH_TEST); // Разрешение теста глубины
			glDisable(GL_BLEND);     // Запрещение смешивания
		
		} else {

			glBindTexture(GL_TEXTURE_2D, NULL);
			DrawSnowOne(snows[i], false);
		
		}

	}
	glDisable(GL_LIGHT2);

}

//
// Special Winter Snow Processor :: End
//

//
// The Yolker :: Start
//

void CreateYolko () {
	for (int i = 0; i < YOLKO_COUNT; i++) {
		while (1) {
			yolkas[i].x = MAP_SIZE * randf();
			yolkas[i].z = MAP_SIZE * randf();
			yolkas[i].y = GetGroundHeight (g_HeightMap, yolkas[i].x,yolkas[i].z) - CAM_HEIGHT;
			if (yolkas[i].y > WATER_H) break;
		}
		yolkas[i].scale = randf() + 0.5f;
	}
}

void DrawYolko (yolko tree) {
	float x1 = tree.x + 0.5f * YOLKO_WIDTH * tree.scale * cos(g2r(yang));
	float x2 = tree.x - 0.5f * YOLKO_WIDTH * tree.scale * cos(g2r(yang));
	float y1 = tree.y;
	float y2 = tree.y + YOLKO_HEIGHT * tree.scale;
	float z1 = tree.z - 0.5f * YOLKO_WIDTH * tree.scale * sin(g2r(yang));
	float z2 = tree.z + 0.5f * YOLKO_WIDTH * tree.scale * sin(g2r(yang));

	glNormal3f(0.0, 0.0, 1.0);
		glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 0.0f); glVertex3f(x1, y1, z1);
			glTexCoord2f(1.0f, 0.0f); glVertex3f(x2, y1, z2);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(x2, y2, z2);
			glTexCoord2f(0.0f, 1.0f); glVertex3f(x1, y2, z1);
		glEnd();
}

void RenderYolko () {
	glEnable(GL_LIGHT2);
	glLightfv(GL_LIGHT2, GL_POSITION, LightPosition2);  // Light Position

	glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
	for (int i = 0; i < YOLKO_COUNT; i++) {

		float dist = sqrt( (yolkas[i].x - wx) * (yolkas[i].x - wx) + (yolkas[i].z - wz) * (yolkas[i].z - wz) );
		if (dist > 100) continue;

			glEnable(GL_BLEND);       // Разрешение смешивания
			glDisable(GL_DEPTH_TEST); // Запрет теста глубины

			glBlendFunc(GL_DST_COLOR, GL_ZERO); // Смешивание цвета экрана с нулем (Черное)
			glBindTexture(GL_TEXTURE_2D, texture[6]); // Выбор первого изображения текстуры
			 DrawYolko(yolkas[i]);
		
			glBlendFunc(GL_ONE, GL_ONE); // Копирование цветного изображения 1 на экран
			glBindTexture(GL_TEXTURE_2D, texture[5]); // Выбор первого изображения текстуры
			 DrawYolko(yolkas[i]);

			glEnable(GL_DEPTH_TEST); // Разрешение теста глубины
			glDisable(GL_BLEND);     // Запрещение смешивания

	}
	glDisable(GL_LIGHT2);
}

//
// The Yolker :: End
//

void InitGL(GLsizei Width, GLsizei Height)
{
	
	ShowCursor(false);
	SetCursorPos(MOUSE_STX, MOUSE_STY);

	glEnable(GL_TEXTURE_2D);      // Enable Global Texturing
	glShadeModel(GL_SMOOTH);      // Enable Smooth Shading

	glClearColor(0.316f, 0.636f, 0.922f, 1.0f); // Background color
	glClearDepth(1.0f);       
	glClearStencil(0);
	glEnable(GL_DEPTH_TEST);     
	glDepthFunc(GL_LEQUAL);     
	glEnable(GL_NORMALIZE);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); 
	
	glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);    // Ambient Lighting
	glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);    // Diffuse Lighting
	glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);  // Light Position
	glEnable(GL_LIGHT1);

	glLightfv(GL_LIGHT2, GL_AMBIENT, LightDiffuse);    // Ambient Lighting
	glLightfv(GL_LIGHT2, GL_DIFFUSE, LightDiffuse);    // Diffuse Lighting
	glLightfv(GL_LIGHT2, GL_POSITION, LightPosition2);  // Light Position

	glLightfv (GL_LIGHT3, GL_AMBIENT,  SphereAmbient);	// Ambient Lighting
	glLightfv (GL_LIGHT3, GL_DIFFUSE,  SphereDiffuse);	// Diffuse Lighting
	glLightfv (GL_LIGHT3, GL_POSITION, SpherePosition);	// Light Position
	glLightfv (GL_LIGHT3, GL_SPOT_DIRECTION, SphereSpotDir);
	glLightf  (GL_LIGHT3, GL_SPOT_EXPONENT, 0.0f);
	glLightf  (GL_LIGHT3, GL_SPOT_CUTOFF, 60.0f);
	glLightf  (GL_LIGHT3, GL_CONSTANT_ATTENUATION, 0.0f);
	glLightf  (GL_LIGHT3, GL_LINEAR_ATTENUATION, 0.38f);
	glLightf  (GL_LIGHT3, GL_QUADRATIC_ATTENUATION, 0.0f);
	glEnable(GL_LIGHT3);

	glEnable(GL_LIGHTING);							   // Fiat Lux!
	
	glEnable(GL_FOG);							// Fog On
	glFogi(GL_FOG_MODE, GL_LINEAR);				// Fog Mode
	glFogfv(GL_FOG_COLOR, fogColor);			// Fog Color
	glFogf(GL_FOG_DENSITY, 0.35f);				// Fog Density
	glHint(GL_FOG_HINT, GL_DONT_CARE);			// Fog SubMode
	glFogf(GL_FOG_START, 100.0f);				// Fog Start Deep
	glFogf(GL_FOG_END, 150.0f);					// Fog End Deep

	LoadHeightFile ("data/height.bmp", MAP_SIZE * MAP_SIZE, g_HeightMap, g_matrix);
	LoadTextures ();

	if (music) PlaySound ("data/jinglebells.wav", NULL, SND_ASYNC | SND_LOOP | SND_FILENAME);

	// Subroutine : Init Walker Start Position :: Start
	wx = 50.0f;
	wz = 50.0f;
	wy = GetGroundHeight (g_HeightMap, wx,wz);
	// Subroutine : Init Walker Start Position :: End

	spQMesh = gluNewQuadric();

	CreateSnow ();
	CreateYolko ();

}

void ReSizeGLScene(GLsizei Width, GLsizei Height)
{
	if (Height==0)  Height=1; // AntiDivByZero
	
	glViewport(0, 0, Width, Height);	// Reset Perspective

	glMatrixMode(GL_PROJECTION);        // Reset projection maxtix
    glLoadIdentity();                   // Reset projection maxtix

    // Counting window size
	gluPerspective (45.0f, (GLfloat) Width / (GLfloat) Height, 0.1f, 150.0f);
    glMatrixMode(GL_MODELVIEW);			// Choosing model viewing matrix
}

void DrawGLScene(GLvoid)
{

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);    // "cls"
    glLoadIdentity();						               // Reset view

	// Position Processing :: Start

	yang += MOUSE_SENS * (float) mmvx; // Mouse Moves Left-Right
	xang += MOUSE_SENS * (float) mmvy; // Mouse Moves Top-Bottom

	if (yang < 0.0f) yang = yang + 360.0f;
	if (yang > 360.0f) yang = yang - 360.0f;
	if (xang > 85.0f) xang = 85.0f;
	if (xang < -85.0f) xang = -85.0f;

	// Reset Mouse Position
	SetCursorPos(MOUSE_STX, MOUSE_STY);
	mmvx = mmvy = 0;

	wx += wspeed * sin (g2r(yang)); // X-move
	wz += wspeed * cos (g2r(yang)); // Z-move
	wx -= sspeed * cos (g2r(yang)); // X-strafe
	wz += sspeed * sin (g2r(yang)); // Z-strafe

	if (wx > MAP_SIZE) wx = MAP_SIZE;
	if (wz > MAP_SIZE) wz = MAP_SIZE;

	if (wx < 0) wx = 0;
	if (wz < 0) wz = 0;

	wy = GetGroundHeight (g_HeightMap, wx, wz);

	cx = wx + 10.0f * sin (g2r(yang)) * cos (g2r(xang));
	cz = wz + 10.0f * cos (g2r(yang)) * cos (g2r(xang));
	cy = wy + 10.0f * sin (g2r(xang));

	gluLookAt(wx, wy, wz,  cx, cy, cz, 0, 1, 0);

	glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);  // Light Position
	//LightPosition2[0] = 1.0f * sin (g2r(yang));
	//LightPosition2[2] = 1.0f * cos (g2r(yang));
	glLightfv(GL_LIGHT2, GL_POSITION, LightPosition2);  // Light Position

	// Position Processing :: End

	if (snowing) ProcessSnow();

	// Choose Rendering Style

	if (reflections) {

		glEnable(GL_LIGHT1);
		//glDisable(GL_LIGHT1);

		// Rendering with Reflections
		// Borrowed from NeHe lesson 26
		// Realy don't know, how it works

		double eqr[] = {0.0f,-1.0f, 0.0f, 0.0f};
		glColorMask(0,0,0,0);    

//		glDisable(GL_LIGHTING);
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_ALWAYS, 1, 1); 
		glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
		glDisable(GL_DEPTH_TEST);

			glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
			RenderWater();

		glEnable(GL_DEPTH_TEST); 
		glColorMask(1,1,1,1);
		glStencilFunc(GL_EQUAL, 1, 1); 
		glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

		glEnable(GL_CLIP_PLANE0);
		glClipPlane(GL_CLIP_PLANE0, eqr);
		glPushMatrix();  
		glScalef(1.0f, -1.0f, 1.0f); 

			glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
			RenderSphere();
			RenderHeightMap(g_HeightMap, g_matrix);
			RenderSkyBox();
			if (yolker) RenderYolko();
			//RenderSnow();

		glPopMatrix();    

		glDisable(GL_CLIP_PLANE0);
		glDisable(GL_STENCIL_TEST);

		glEnable(GL_BLEND);
		if (light) glDisable(GL_LIGHTING);
		glColor4f(1.0f, 1.0f, 1.0f, 0.7f);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

			RenderWater();

		glDisable(GL_BLEND); 
		if (light) glEnable(GL_LIGHTING);

			glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
			RenderSphere();
			RenderHeightMap(g_HeightMap, g_matrix);
			RenderSkyBox();
			if (yolker) RenderYolko();
			if (snowing) RenderSnow();
			
		glFlush();

	} else {
		// Simple Rendering		
		RenderSphere();
		RenderHeightMap(g_HeightMap, g_matrix);
		RenderWater();	
		RenderSkyBox();
		if (yolker) RenderYolko();
		if (snowing) RenderSnow();
	}
}

LRESULT CALLBACK WndProc( HWND    hWnd, UINT    message, WPARAM  wParam, LPARAM  lParam)
{

	RECT    Screen;         // используется позднее для размеров окна
	GLuint  PixelFormat;
	static  PIXELFORMATDESCRIPTOR pfd =	{
		sizeof(PIXELFORMATDESCRIPTOR),  // Размер этой структуры
        1,                              // Номер версии (?)
        PFD_DRAW_TO_WINDOW |            // Формат для Окна
        PFD_SUPPORT_OPENGL |            // Формат для OpenGL
//		PFD_GENERIC_ACCELERATED |		// Какое-то там ускорение, моталось тут, но по ходу оно левое ...
		PFD_DOUBLEBUFFER,               // Формат для двойного буфера
        PFD_TYPE_RGBA,                  // Требуется RGBA формат
        32,                             // Выбор 16 бит глубины цвета
        0, 0, 0, 0, 0, 0,               // Игнорирование цветовых битов (?)
        0,                              // нет буфера прозрачности
        0,                              // Сдвиговый бит игнорируется (?)
        0,                              // Нет буфера аккумуляции
        0, 0, 0, 0,                     // Биты аккумуляции игнорируются (?)
        32,                             // 16 битный Z-буфер (буфер глубины) 
        0,                              // Нет буфера траффарета
        0,                              // Нет вспомогательных буферов (?)
        PFD_MAIN_PLANE,                 // Главный слой рисования
        0,                              // Резерв (?)
        0, 0, 0                         // Маски слоя игнорируются (?)
	};

	switch (message)        // Main Switch
	{

		// App Create
        case WM_CREATE:

			// WM_Create :: Start

			hDC = GetDC (hWnd);      
			PixelFormat = ChoosePixelFormat(hDC, &pfd);
			if (!PixelFormat){
				MessageBox(0, "Can't Find A Suitable PixelFormat.", "Error", MB_OK | MB_ICONERROR);
				PostQuitMessage(0);
                break; 
            }

			SetPixelFormat (hDC, PixelFormat, &pfd);

			hRC = wglCreateContext (hDC);
			if(!hRC) {
                MessageBox(0, "Can't Create A GL Rendering Context.", "Error", MB_OK | MB_ICONERROR);
                PostQuitMessage(0);
                break;
            }

			if(!wglMakeCurrent(hDC, hRC)) {
                MessageBox(0, "Can't activate GLRC.", "Error", MB_OK | MB_ICONERROR);
                PostQuitMessage(0);
                break;
            }

			GetClientRect(hWnd, &Screen);
			InitGL(Screen.right, Screen.bottom);

			break;
			// WM_Create :: End

		// App Die
		case WM_DESTROY:
		case WM_CLOSE:

			// WM_Close :: Start

			ShowCursor(true);
			if (music) PlaySound(NULL, NULL, 0);
			gluDeleteQuadric(spQMesh);

			ChangeDisplaySettings(NULL, 0);

			wglMakeCurrent(hDC,NULL);
			wglDeleteContext(hRC);
			ReleaseDC(hWnd,hDC);

			PostQuitMessage(0);
			break;

			// WM_Close :: End

		// App KeyState

		case WM_KEYDOWN:
			keys[wParam] = true;
			break;

		case WM_KEYUP:
			keys[wParam] = false;
			break;

		// Mouse Moves
		case WM_MOUSEMOVE:
			mmvx = 320 - LOWORD(lParam);
			mmvy = 240 - HIWORD(lParam);
			break;

		// Win Resize

		case WM_SIZE:
			ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
			break;

		// Default Win Proc		

        default:
			return (DefWindowProc(hWnd, message, wParam, lParam));
    } // end - Main Switch

	return (0);

}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)
{
	MSG             msg;    // Структура сообщения Windows
	WNDCLASS        wc;		// Структура класса Windows для установки типа окна
	HWND            hWnd;   // Сохранение дискриптора окна

	wc.style                = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc          = (WNDPROC) WndProc;
	wc.cbClsExtra           = 0;
	wc.cbWndExtra           = 0;
	wc.hInstance            = hInstance;
	wc.hIcon                = NULL;
	wc.hCursor              = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground        = NULL;
	wc.lpszMenuName         = NULL;
	wc.lpszClassName        = "OpenGL WinClass";

	if(!RegisterClass(&wc))	{
		MessageBox (0,"Failed To Register The Window Class.", "Error", MB_OK|MB_ICONERROR);
		return false;
	}


	hWnd = CreateWindow (
        "OpenGL WinClass",
        "Landscape Modeling via BMP - AlterVision Creatives", // Заголовок вверху окна
        WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
        0, 0,                   // Позиция окна на экране
        SCREEN_X, SCREEN_Y,               // Ширина и высота окна
        NULL,
        NULL,
        hInstance,
        NULL
	);

	if(!hWnd) {
		MessageBox(0,"Window Creation Error.", "Error", MB_OK|MB_ICONERROR); 
		return false;
	}

	// GoTo FullScreen :: Start :: Взято у кого-то, не помню у кого, но полезно для жизни ...
	if (fullscreen) {
		DEVMODE dmScreenSettings;								// Режим работы
		memset(&dmScreenSettings, 0, sizeof(DEVMODE));          // Очистка для хранения установок
		dmScreenSettings.dmSize = sizeof(DEVMODE);              // Размер структуры Devmode
		dmScreenSettings.dmPelsWidth    = SCREEN_X;                  // Шир ина экрана
		dmScreenSettings.dmPelsHeight   = SCREEN_Y;                  // Высота экрана
		dmScreenSettings.dmFields       = DM_PELSWIDTH | DM_PELSHEIGHT; // Режим Пиксела
		ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);   // Переключение в полный экран
	}	
	// GoTo FullScreen :: End


	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);
	SetFocus(hWnd);

	while (1)
	{
		// Sync Rate :: 30 fps :: Get Time
		DWORD start_time = GetTickCount();

		while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))	{
			if (GetMessage(&msg, NULL, 0, 0)) {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			} else {
				return true;
			}
		}

		DrawGLScene();                          // Go To The PArty ))
		SwapBuffers(hDC);                       // Show the Results

	// Key Handling :: Start

		// WireFrame Activator
		if (keys['W'] && !wp) {			// Key Press
			wp = true;					// Set WKeyPressed into true
			wireframe = !wireframe;		// Invent Wireframe
		}
		if (!keys['W'])  wp = false;      // Key Release   
		
		// Reflections Activator
		if (keys['R'] && !rp) {			// Key Press
			rp = true;					// Set RKeyPressed into true
			reflections = !reflections;		// Invent Reflections Mode
		}
		if (!keys['R'])  rp = false;      // Key Release   

		// Normals Activator
		if (keys['N'] && !np) {			// Key Press
			np = true;					// Set NKeyPressed into true
			normal = !normal;			// Normalize Matrix
		}
		if (!keys['N'])  np = false;      // Key Release   

		// Snow Activator
		if (keys['S'] && !sp) {			// Key Press
			sp = true;					// Set SKeyPressed into true
			snowing = !snowing;			// Let it snow !?
		}
		if (!keys['S'])  sp = false;      // Key Release   

		// Snow Texture Activator
		if (keys['T'] && !tp) {			// Key Press
			tp = true;					// Set SKeyPressed into true
			snowtex = !snowtex;			// Let it snow !?
		}
		if (!keys['T'])  tp = false;      // Key Release   

		// Yolker Activator
		if (keys['Q'] && !qp) {			// Key Press
			qp = true;					// Set QKeyPressed into true
			yolker = !yolker;			// Yolker Builder
		}
		if (!keys['Q'])  qp = false;      // Key Release   

		// Lighting Activator
		if (keys['L'] && !lp) {			// Key Press
			lp = true;					// Set LKeyPressed into true
			light = !light;				// Fiat Lux ...
			if (light) {
				glEnable(GL_LIGHTING);
			} else {
				glDisable(GL_LIGHTING);
			}
		}
		if (!keys['L'])  lp = false;      // Key Release   

		// Music Controls
		if (keys['M'] && !mp) {			// Key Press
			mp = true;					// Set MKeyPressed into true
			music = !music;				// Play/Stop Music
			if (music) {
				//PlaySound ("data/jinglebells.wav", NULL, SND_ASYNC | SND_LOOP | SND_FILENAME);
			} else {
				PlaySound (NULL, NULL, 0);
			}
		}
		if (!keys['M'])  mp = false;      // Key Release   

		// Walking Ahead Speed Controls
		 // Speed Up
		if (keys[VK_UP])	{
			wspeed += (MAX_SPEED / 10.0f);
			if (wspeed > MAX_SPEED) wspeed = MAX_SPEED;
		}
		 // Speed Down
		if (keys[VK_DOWN])  {
			wspeed -= (MAX_SPEED / 10.0f);
			if (wspeed < -MAX_SPEED) wspeed = -MAX_SPEED;
		}
		 // Inertia
		if (!keys[VK_UP] && !keys[VK_DOWN]) {
			if (wspeed > (MAX_SPEED / 20.0f)) {
				wspeed -= (MAX_SPEED / 20.0f);
			} else if (wspeed < -(MAX_SPEED / 20.0f)) {
				wspeed += (MAX_SPEED / 20.0f);
			} else {
				wspeed = 0.0f;
			}
		}
		 // Strafe Right
		if (keys[VK_RIGHT])	{
			sspeed += (MAX_SPEED / 10.0f);
			if (sspeed > MAX_SPEED) sspeed = MAX_SPEED;
		}
		 // Strafe Left
		if (keys[VK_LEFT])  {
			sspeed -= (MAX_SPEED / 10.0f);
			if (sspeed < -MAX_SPEED) sspeed = -MAX_SPEED;
		}
		 // Inertia
		if (!keys[VK_RIGHT] && !keys[VK_LEFT]) {
			if (sspeed > (MAX_SPEED / 20.0f)) {
				sspeed -= (MAX_SPEED / 20.0f);
			} else if (sspeed < -(MAX_SPEED / 20.0f)) {
				sspeed += (MAX_SPEED / 20.0f);
			} else {
				sspeed = 0.0f;
			}
		}

		// Moving Light
		if (keys['G']) SpherePosition[0] += 0.4f;
		if (keys['H']) SpherePosition[0] -= 0.4f;
		if (keys['Y']) SpherePosition[2] += 0.4f;
		if (keys['B']) SpherePosition[2] -= 0.4f;
		if (keys['U']) SpherePosition[1] += 0.4f;
		if (keys['J']) SpherePosition[1] -= 0.4f;
		if (keys['I']) {
				SphereDiffuse[0] = randf();
				SphereDiffuse[1] = randf();
				SphereDiffuse[2] = randf();
				glLightfv (GL_LIGHT3, GL_DIFFUSE,  SphereDiffuse);
		}

		// EscapeKey for Exit Procedure
		if (keys[VK_ESCAPE]) SendMessage (hWnd,WM_CLOSE,0,0);    

	// Key Handling :: End
		
		// Sync Rate :: 30 fps :: Syncronize;
		while((GetTickCount() - start_time) < 33) Sleep(3);
	}

}

// end. =)
// ЗЫ: sorry for my english =)
// Достоверно подтверждаю, что эта дрянь работала у меня на ноутбуке Acer Aspire 3690 /Cel 1.6GHz, 512MB, GMA950)
// По вопросам и багам: mailto:altervision13@gmail.com / http://www.av13.net.ru/