#include <gl/glut.h>
#include <gl/glaux.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifndef __FORMATS_H
#include"formats.h"
#endif

double h = 15;
const double pi = 3.1415926535897;
double cx = 775, cy = h, cz = -775; //координаты наблюдателя
double ha = - pi / 2, va = 0; //углы направления камеры
double speed = 5, rspeed = 2;
double a_x = 0, a_y = 0, a_z = 0;
int width = 800, height = 600;
unsigned char * tex_bits;
int tex_width, tex_height;
int jump = 0, j_height = 50;;
float j_angle = 0;
const int GLwall = 0;
const int GLnumoftex = 1;
GLuint texture[GLnumoftex];
char fwalls[] = {0,0,0,0,0,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 1,0,0,0,0,0,0,0,1,0, 1,0,
                 1,1,1,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,1,1,0,1,1,0,0,0, 1,1,
			     0,0,0,0,0,0,1,0,1,1, 0,0,0,0,0,1,1,0,0,0, 0,1,1,1,0,1,1,1,1,1, 1,0,
			     0,1,1,0,0,1,1,1,1,0, 0,0,1,1,0,0,0,0,0,0, 1,1,0,1,0,1,1,0,0,0, 1,1,
			     0,0,0,0,0,0,0,1,1,1, 1,1,1,1,1,0,0,1,1,1, 0,0,0,0,0,0,0,0,0,1, 1,0,
			     0,1,1,0,0,1,1,1,0,0, 0,0,1,1,0,0,0,0,0,0, 0,0,1,1,0,1,0,1,1,0, 1,1,
			     1,1,0,0,0,0,1,1,1,0, 0,0,0,0,0,0,0,1,1,1, 1,0,0,0,0,0,1,1,0,1, 1,0,
			     0,1,0,0,0,1,1,1,1,1, 1,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,1,0,0,0, 1,1,
			     0,0,0,1,1,1,1,1,1,1, 1,1,0,1,1,1,1,0,0,0, 0,0,0,1,1,0,1,1,0,0, 0,0,
			     0,0,0,0,0,0,0,0,0,0, 0,0,1,1,1,1,1,0,0,0, 0,0,0,0,0,1,1,1,1,0, 0,0,

				 1,1,1,1,1,1,1,1,1,1, 1,0,1,1,1,1,1,0,0,1, 1,0,0,0,0,0,0,0,1,0, 0,0,
				 0,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,
				 1,1,1,1,1,1,1,1,1,0, 0,0,1,1,1,1,1,1,1,1, 1,0,0,0,0,0,0,0,0,0, 1,1,
				 0,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,0,0, 0,0,0,0,0,0,0,0,0,1, 1,0,
				 1,1,1,0,1,1,1,1,1,1, 1,1,1,0,0,0,0,0,0,0, 1,0,0,0,0,0,0,0,0,0, 0,0,
				 0,0,0,0,0,0,0,0,1,1, 1,0,0,0,0,0,0,1,1,0, 1,1,1,0,1,1,0,0,0,0, 0,0,
				 1,1,1,1,1,1,1,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,
				 0,0,0,0,0,0,1,1,1,1, 0,0,0,0,0,0,0,0,1,1, 0,0,0,0,0,0,0,0,0,1, 1,0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,1,1,1,0,0,0,0, 0,0,
				 0,1,1,1,1,1,1,1,1,0, 0,0,0,0,0,0,1,1,1,1, 1,1,0,0,0,0,1,1,0,0, 0,0,

				 0,0,1,1,0,0,1,0,1,0, 1,1,1,1,1,1,0,0,1,1, 0,1,1,1,1,0,0,0,1,1, 1,0,
				 0,0,0,0,0,0,0,1,1,1, 1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,
				 0,0,0,0,1,1,1,1,1,1, 1,1,1,0,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,1,1, 1,1,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,1,1,1,1, 1,1,0,0,0,0,0,0,0,0, 0,0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,0,0, 0,0,1,1,1,1,1,1,0,0, 0,0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,1,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,

				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0};

char swalls[] = {0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,1,0,0,0, 0,
                 0,0,0,0,1,0,0,0,0,0, 0,1,0,0,0,0,0,0,1,0, 1,1,0,1,1,0,1,0,1,1, 0,
				 0,0,0,0,0,0,0,1,0,0, 0,1,0,0,1,0,0,0,1,1, 1,0,0,0,0,0,0,0,1,0, 0,
				 0,0,1,0,1,0,0,0,0,1, 0,1,0,0,1,0,1,0,1,1, 0,0,0,1,0,0,0,1,1,0, 0,
				 0,0,0,0,0,0,0,0,0,1, 0,0,0,0,1,0,1,0,0,1, 1,0,1,1,1,0,1,1,1,0, 0,
				 0,0,0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,0,0,0, 1,0,0,1,0,0,1,0,1,0, 0,
				 0,0,1,0,1,0,0,0,0,1, 0,1,0,1,0,0,0,0,0,0, 1,1,0,0,1,1,0,0,1,0, 0,
				 0,1,1,0,1,0,0,0,0,1, 0,1,1,1,0,0,1,0,1,0, 0,1,0,0,1,0,0,1,1,0, 0,
				 1,0,1,0,0,0,0,0,0,0, 0,0,1,0,0,0,1,0,1,0, 0,1,0,0,1,0,0,1,1,1, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,1,0, 0,0,1,0,1,0,0,0,1,1, 0,
					   
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,0,1,0, 0,0,0,0,0,0,1,0,0,0, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,0, 0,0,0,0,0,0,1,1,1,1, 0,
				 0,0,0,0,0,0,0,0,0,0, 1,0,0,0,0,0,1,0,0,0, 0,0,0,0,0,0,1,1,1,1, 0,
				 0,0,0,0,0,0,0,0,0,0, 1,0,0,0,0,0,0,1,0,0, 1,0,0,0,0,0,1,1,1,0, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,1,0,0, 1,0,0,0,0,0,1,1,0,1, 0,
				 0,0,1,1,0,0,0,0,0,0, 0,0,1,0,0,0,1,1,0,0, 0,0,0,0,0,0,1,1,0,1, 0,
				 0,0,0,0,0,0,1,1,0,0, 1,0,1,0,0,0,1,1,0,0, 0,0,1,1,0,1,1,1,0,1, 0,
				 0,1,0,0,1,0,0,1,0,0, 0,1,1,0,0,1,1,1,0,0, 0,0,1,1,1,1,1,1,0,0, 0,
				 0,1,0,0,1,0,0,0,0,1, 0,1,1,0,0,1,1,0,0,0, 0,0,1,0,1,1,1,1,1,0, 1,
				 0,0,0,0,0,0,0,0,0,1, 0,1,0,0,0,1,0,0,0,0, 0,0,0,0,0,0,0,1,1,1, 1,

				 0,0,0,0,0,0,0,0,0,1, 0,1,0,0,0,1,0,0,0,1, 0,0,0,0,1,0,0,1,0,1, 1,
				 0,1,0,1,0,1,0,0,1,0, 0,0,0,0,0,0,0,0,1,1, 0,0,0,0,0,0,0,0,0,0, 0,
				 0,1,0,1,0,1,0,0,0,0, 0,0,0,0,0,0,0,0,1,0, 0,0,0,0,0,0,0,0,0,0, 0,
				 0,1,0,0,0,0,0,0,0,0, 0,0,1,1,0,0,0,0,0,0, 0,0,1,0,1,0,1,1,0,0, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,1,1,0,1,0,0,0,0, 0,1,0,1,0,1,0,1,0,0, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,1,0,1,1,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,

				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,
				 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0};

struct edge
	{
		float vx1, vx2, vx3, vx4, vy1, vy2, vy3, vy4, vz1, vz2, vz3, vz4;
		float nx1, nx2, nx3, nx4, ny1, ny2, ny3, ny4, nz1, nz2, nz3, nz4;
	};
const int A = 30, B = 10, R = 10, r = 2;
struct edge buff;
struct edge v_array[A * B];
int v_index = 0;


unsigned char* LoadTrueColorBMPFile(const char *path,int *width,int *height);
void renderopaque(int def);

void reshape(int w, int h)
{
	glViewport(0, 0, w, h);
	width = w;
	height = h;
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60, (GLfloat) w / h, 1, 8000);
}

void loadtextures()
{
	tex_width = tex_height = 256;

	glGenTextures(1, texture);
    // Создаем массив с цветами пикселов
	tex_bits = LoadTrueColorBMPFile("textures/bricks.bmp", &tex_width, &tex_height);
	// Создаем текстурный объект
	if (tex_bits == NULL)
		exit(1);
	// Активизируем текстурный  объект
	glBindTexture(GL_TEXTURE_2D, texture[GLwall]);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D,
		         0,                     // Mip-level
			     GL_RGB,                // Формат текстуры
			     tex_width,tex_height,  // Размер текстуры
			     0,                     // Ширина границы
			     GL_RGB,                // Формат исходных данных
			     GL_UNSIGNED_BYTE,      // Тип исходных данных
				 tex_bits);             // Указатель на исходные данные 
	// Освобождаем память, в которой хранилась текстура
	delete[] tex_bits;
	// Устанавливаем параметры текстуры
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
}

void init()
{
	int i, j, i2, j2, step = 50, size = 3;
	float phi1, phi2, psi1, psi2, nx, ny, nz;
	float angle = 0;
	float a_step = 0.5;
	float l0_ambient[] = {0.0, 0.0, 0.0, 1.0};
	float l0_diffuse[] = {1.0, 1.0, 1.0, 1.0};
	float l0_specular[] = {1.0, 1.0, 1.0, 1.0};
	float lm_ambient[] = {1.0, 1.0, 1.0, 1.0};
	
	loadtextures();

	glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 90);
	glLightfv(GL_LIGHT0, GL_AMBIENT, l0_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, l0_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, l0_specular);
	
	glNewList(1, GL_COMPILE); //половина квадратной сетки для пола
	glBegin(GL_QUADS);
	glNormal3f(0, 1, 0);
	for (i = -800; i + step <= 800; i += step)
		for (j = -800; j + step <= 800; j += step)
		{
			glVertex3f(i, 0, j);
			glVertex3f(i, 0, j + step / 2);
			glVertex3f(i + step / 2, 0, j + step / 2);
			glVertex3f(i + step / 2, 0, j);
			
			glVertex3f(i + step / 2, 0, j + step / 2);
			glVertex3f(i + step / 2, 0, j + step);
			glVertex3f(i + step, 0, j + step);
			glVertex3f(i + step, 0, j + step / 2);
		}
	glEnd();
	glEndList();


	glNewList(2, GL_COMPILE); //половина квадратной сетки для пола
	glBegin(GL_QUADS);
	for (i = -800; i + step <= 800; i += step)
		for (j = -800; j + step <= 800; j += step)
		{
			glVertex3f(i, 0, j + step / 2);
			glVertex3f(i, 0, j + step);
			glVertex3f(i + step / 2, 0, j + step);
			glVertex3f(i + step / 2, 0, j + step / 2);
			
			glVertex3f(i + step / 2, 0, j);
			glVertex3f(i + step / 2, 0, j + step / 2);
			glVertex3f(i + step, 0, j + step / 2);
			glVertex3f(i + step, 0, j);
		} 
	glEnd();
	glEndList();

	
	glNewList(3, GL_COMPILE); //куб
	glBegin(GL_QUADS);

	glNormal3f(0, 0, 1);
	glVertex3f(-size, -size, size);
	glVertex3f(size, -size, size);
	glVertex3f(size, size, size);
	glVertex3f(-size, size, size); 

	glNormal3f(0, 0, -1);
	glVertex3f(size, -size, -size);
	glVertex3f(-size, -size, -size);
	glVertex3f(-size, size, -size);
	glVertex3f(size, size, -size); 

	glNormal3f(-1, 0, 0);
	glVertex3f(-size, -size, -size);
	glVertex3f(-size, -size, size);
	glVertex3f(-size, size, size);
	glVertex3f(-size, size, -size);

	glNormal3f(1, 0, 0);
	glVertex3f(size, -size, size);
	glVertex3f(size, -size, -size);
	glVertex3f(size, size, -size);
	glVertex3f(size, size, size);

	glNormal3f(0, 1, 0);
	glVertex3f(-size, size, size);
	glVertex3f(size, size, size);
	glVertex3f(size, size, -size);
	glVertex3f(-size, size, -size); 

	glNormal3f(0, -1, 0);
	glVertex3f(-size, -size, -size);
	glVertex3f(size, -size, -size);
	glVertex3f(size, -size, size);
	glVertex3f(-size, -size, size);  
	
	glEnd();
	glEndList();

    glNewList(4, GL_COMPILE); //элемент стены для лабиринта
	glBegin(GL_QUADS);

	glNormal3f(0, 0, 1);
	glTexCoord2d(0, 0);
	glVertex3f(0, 0, 0);
	glTexCoord2d(1, 0);
	glVertex3f(step, 0, 0);
	glTexCoord2d(1, 1);
	glVertex3f(step, step, 0);
	glTexCoord2d(0, 1);
	glVertex3f(0, step, 0);

	glNormal3f(0, 0, -1);
	glTexCoord2d(1, 0);
	glVertex3f(0, 0, 0.01);
    glTexCoord2d(1, 1);
	glVertex3f(0, step, 0.01);
	glTexCoord2d(0, 1);
	glVertex3f(step, step, 0.01);
	glTexCoord2d(0, 0);
	glVertex3f(step, 0, 0.01);  

	glEnd();
	glEndList();

	

	glNewList(5, GL_COMPILE); //элемент стены для лабиринта
	glBegin(GL_QUADS);

	glNormal3f(1, 0, 0);
	glTexCoord2d(0, 0);
	glVertex3f(0, 0, 0);
	glTexCoord2d(1, 0);
	glVertex3f(0, 0, -step);
	glTexCoord2d(1, 1);
	glVertex3f(0, step, -step);
	glTexCoord2d(0, 1);
	glVertex3f(0, step, 0);

	glNormal3f(-1, 0, 0);
	glTexCoord2d(1, 0);
	glVertex3f(-0.01, 0, 0);
	glTexCoord2d(1, 1);
	glVertex3f(-0.01, step, 0);
	glTexCoord2d(0, 1);
	glVertex3f(-0.01, step, -step);
	glTexCoord2d(0, 0);
	glVertex3f(-0.01, 0, -step);  

	glEnd();
	glEndList();


	glNewList(6, GL_COMPILE); //тор
	
	glBegin(GL_QUADS);
	for(i = 0; i < A; ++i) 
	{
		i2 = (i < A - 1) ? (i + 1) : (0);
		phi1 = 2 * i * pi / A;
		phi2 = 2 * i2 * pi / A;
		for(j = 0; j < B; ++j) 
		{
			j2 = (j < B - 1) ? (j + 1) : (0);
			psi1 = 2 * j * pi / B;
			psi2 = 2 * j2 * pi / B;

			nx = cos(phi1) * cos(psi1);
			ny = sin(psi1);
			nz = sin(phi1) * cos(psi1);

			glNormal3d(nx, ny, nz);
			glVertex3d(R * cos(phi1) + r * nx, r * ny, R * sin(phi1) + r * nz);

			nx = cos(phi1) * cos(psi2);
			ny = sin(psi2);
			nz = sin(phi1) * cos(psi2);

			glNormal3d(nx, ny, nz);
			glVertex3d(R * cos(phi1) + r * nx, r * ny, R * sin(phi1) + r * nz);

			nx = cos(phi2) * cos(psi2);
			ny = sin(psi2);
			nz = sin(phi2) * cos(psi2);

			glNormal3d(nx, ny, nz);
			glVertex3d(R * cos(phi2) + r * nx, r * ny, R * sin(phi2) + r * nz);

			nx = cos(phi2) * cos(psi1);
			ny = sin(psi1);
			nz = sin(phi2) * cos(psi1);

			glNormal3d(nx, ny, nz);
			glVertex3d(R * cos(phi2) + r * nx, r * ny, R * sin(phi2) + r * nz);
		}
	}
	glEnd();
	
	glEndList();

	for(i = 0; i < A; ++i) //сохраняем тор в массиве
	{
		i2 = (i < A - 1) ? (i + 1) : (0);
		phi1 = 2 * i * pi / A;
		phi2 = 2 * i2 * pi / A;
		for(j = 0; j < B; ++j) 
		{
			j2 = (j < B - 1) ? (j + 1) : (0);
			psi1 = 2 * j * pi / B;
			psi2 = 2 * j2 * pi / B;

			nx = cos(phi1) * cos(psi1);
			ny = sin(psi1);
			nz = sin(phi1) * cos(psi1);

			buff.nx1 = nx;
			buff.ny1 = ny;
			buff.nz1 = nz;
			buff.vx1 = R * cos(phi1) + r * nx;
			buff.vy1 = r * ny;
			buff.vz1 = R * sin(phi1) + r * nz;

			nx = cos(phi1) * cos(psi2);
			ny = sin(psi2);
			nz = sin(phi1) * cos(psi2);

			buff.nx2 = nx;
			buff.ny2 = ny;
			buff.nz2 = nz;
			buff.vx2 = R * cos(phi1) + r * nx;
			buff.vy2 = r * ny;
			buff.vz2 = R * sin(phi1) + r * nz;

			nx = cos(phi2) * cos(psi2);
			ny = sin(psi2);
			nz = sin(phi2) * cos(psi2);

			buff.nx3 = nx;
			buff.ny3 = ny;
			buff.nz3 = nz;
			buff.vx3 = R * cos(phi2) + r * nx;
			buff.vy3 = r * ny;
			buff.vz3 = R * sin(phi2) + r * nz;

			nx = cos(phi2) * cos(psi1);
			ny = sin(psi1);
			nz = sin(phi2) * cos(psi1);

			buff.nx4 = nx;
			buff.ny4 = ny;
			buff.nz4 = nz;
			buff.vx4 = R * cos(phi2) + r * nx;
			buff.vy4 = r * ny;
			buff.vz4 = R * sin(phi2) + r * nz;

			v_array[v_index] = buff;
			++v_index;
		}
	}
	
	glEnable(GL_BLEND);
	glEnable(GL_POLYGON_SMOOTH);
	glEnable(GL_LIGHTING);
	glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);
	glutSetCursor(GLUT_CURSOR_NONE);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lm_ambient);
	glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
}

/* void rendercubemap()  //кубмэп с эффектом бесконечного удаления
{
	float cm_ambient[] = {1.0, 0.0, 0.0, 1.0};
	float cm_diffuse[] = {0.0, 0.0, 0.0, 1.0};
	float cm_specular[] = {0.0, 0.0, 0.0, 1.0};
	float c_size = 1600;
	
//	glPushMatrix(); //вывод кубмэпа
//	glColor4f(1.0, 0.0, 0.0, 1.0);
//	glDisable(GL_LIGHTING);

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, cm_ambient);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cm_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, cm_specular);  

	glBindTexture(GL_TEXTURE_2D, texture[GLsky]); //pos_y.bmp

	glBegin(GL_QUADS);

   	glNormal3f(0, -1, 0);
	glTexCoord2d(0, 0);
	glVertex3f(-c_size - 2 + cx, 2 * c_size, -c_size - 2 + cz);
	glTexCoord2d(1, 0);
	glVertex3f(c_size + 2 + cx, 2 * c_size, -c_size - 2 + cz);
	glTexCoord2d(1, 1);
	glVertex3f(c_size + 2 + cx, 2 * c_size, c_size + 2 + cz);
	glTexCoord2d(0, 1);
	glVertex3f(-c_size - 2 + cx, 2 * c_size, c_size + 2 + cz);

	glEnd();


	glBindTexture(GL_TEXTURE_2D, texture[GLmright]); //pos_x.bmp

	glBegin(GL_QUADS);

	glNormal3f(-1, 0, 0);
	glTexCoord2d(1, 1);
	glVertex3f(c_size + cx, 0, -c_size - 2 + cz);
	glTexCoord2d(0, 1);
	glVertex3f(c_size + cx, 0, c_size + 2 + cz);
	glTexCoord2d(0, 0);
	glVertex3f(c_size + cx, 2 * c_size + 2, c_size + 2 + cz);
	glTexCoord2d(1, 0);
	glVertex3f(c_size + cx, 2 * c_size + 2, -c_size - 2 + cz);

	glEnd();



	glBindTexture(GL_TEXTURE_2D, texture[GLmleft]); //neg_x.bmp

	glBegin(GL_QUADS);

	glNormal3f(1, 0, 0);
	glTexCoord2d(1, 1);
	glVertex3f(-c_size + cx, 0, c_size + 2 + cz);
	glTexCoord2d(0, 1);
	glVertex3f(-c_size + cx, 0, -c_size - 2 + cz);
	glTexCoord2d(0, 0);
	glVertex3f(-c_size + cx, 2 * c_size + 2, -c_size - 2 + cz);
	glTexCoord2d(1, 0);
	glVertex3f(-c_size + cx, 2 * c_size + 2, c_size + 2 + cz);

	glEnd();


	glBindTexture(GL_TEXTURE_2D, texture[GLmfront]); //neg_z.bmp

	glBegin(GL_QUADS);

	glNormal3f(0, 0, 1);
	glTexCoord2d(1, 1);
	glVertex3f(-c_size - 2 + cx, 0, -c_size + cz);
	glTexCoord2d(0, 1);
	glVertex3f(c_size + 2 + cx, 0, -c_size + cz);
	glTexCoord2d(0, 0);	
	glVertex3f(c_size + 2 + cx, 2 * c_size + 2, -c_size + cz);
	glTexCoord2d(1, 0);
	glVertex3f(-c_size - 2 + cx, 2 * c_size + 2, -c_size + cz);

	glEnd();


	glBindTexture(GL_TEXTURE_2D, texture[GLmback]); //pos_z.bmp

	glBegin(GL_QUADS);

	glNormal3f(0, 0, -1);
	glTexCoord2d(0, 1);
	glVertex3f(-c_size - 2 + cx, 0, c_size + cz);
	glTexCoord2d(0, 0);
	glVertex3f(-c_size - 2 + cx, 2 * c_size + 2, c_size + cz);
	glTexCoord2d(1, 0);
	glVertex3f(c_size + 2 + cx, 2 * c_size + 2, c_size + cz);
	glTexCoord2d(1, 1);
	glVertex3f(c_size + 2 + cx, 0, c_size + cz);

	glEnd(); 

//	glEnable(GL_LIGHTING); // 

//	glPopMatrix();
} */

void renderlimtorus() //прозрачный тор
{
	float* mtrx;
	struct edge vert[A * B], hlp2;
	float len[A * B];
	float xx, yy, zz, nx, ny, nz, nw;
	float hlp;
	int i, j;

	mtrx = (float*) malloc(16 * sizeof(float));
	glGetFloatv(GL_MODELVIEW_MATRIX, mtrx);
	for (i = 0; i < A * B; ++i)
	{
		vert[i] = v_array[i];
	}
	
	for (i = 0; i < A * B; ++i)
	{
		xx = (vert[i].vx1 + vert[i].vx2 + vert[i].vx3 + vert[i].vx4) / 4;
		yy = (vert[i].vy1 + vert[i].vy2 + vert[i].vy3 + vert[i].vy4) / 4;
      	zz = (vert[i].vz1 + vert[i].vz2 + vert[i].vz3 + vert[i].vz4) / 4;

		nx = xx * mtrx[0] + yy * mtrx[4] + zz * mtrx[8] + mtrx[12];
		ny = xx * mtrx[1] + yy * mtrx[5] + zz * mtrx[9] + mtrx[13];
		nz = xx * mtrx[2] + yy * mtrx[6] + zz * mtrx[10] + mtrx[14];
		nw = xx * mtrx[3] + yy * mtrx[7] + zz * mtrx[11] + mtrx[15];

		nx /= nw;
		ny /= nw;
		nz /= nw;

		len[i] = sqrt(nx * nx + ny * ny + nz * nz);
	}

	for (i = 1; i < A * B + 1; ++i)
		for (j = 0; j < A * B - 1; ++j)
		{
			if (len[j] < len[j + 1])
			{
				hlp = len[j + 1];
				len[j + 1] = len[j];
				len[j] = hlp;

				hlp2 = vert[j + 1];
				vert[j + 1] = vert[j];
				vert[j] = hlp2;
			}
		} 

	for (i = 0; i < A * B; ++i)
	{
		glBegin(GL_QUADS);
		glNormal3f(vert[i].nx1, vert[i].ny1, vert[i].nz1);
		glVertex3f(vert[i].vx1, vert[i].vy1, vert[i].vz1);
		glNormal3f(vert[i].nx2, vert[i].ny2, vert[i].nz2);
		glVertex3f(vert[i].vx2, vert[i].vy2, vert[i].vz2);
		glNormal3f(vert[i].nx3, vert[i].ny3, vert[i].nz3);
		glVertex3f(vert[i].vx3, vert[i].vy3, vert[i].vz3);
		glNormal3f(vert[i].nx4, vert[i].ny4, vert[i].nz4);
		glVertex3f(vert[i].vx4, vert[i].vy4, vert[i].vz4);
		glEnd();
	}

	free(mtrx);
}

void renderwalls() //стены, кроме боковых
{
	int i, j;

	glPushMatrix();
	glTranslatef(-850, 0, -750);

    for (i = 1; i <= 31; ++i)
	{
		for (j = 1; j <= 32; ++j)
		{
			glTranslatef(50, 0, 0);
			if (fwalls[32 * (i - 1) +j - 1])
				glCallList(4);
		}
		glTranslatef(-1600, 0, 50);
	}

	glPopMatrix();

	glPushMatrix();
	glTranslatef(-800, 0, -750);

    for (i = 1; i <= 32; ++i)
	{
		for (j = 1; j <= 31; ++j)
		{
			glTranslatef(50, 0, 0);
			if (swalls[31 * (i - 1) + j - 1])
				glCallList(5);
		}
		glTranslatef(-1550, 0, 50);
	}

	glPopMatrix(); 
}

void renderlim(int def) //def = default material
{
	float torus1_ambient[] = {0.745, 0.958, 0.724, 0.5};
	float torus1_diffuse[] = {0.78, 0.989, 0.796, 0.5};
	float torus1_specular[] = {0.3, 0.3, 0.3, 0,5};
	float torus2_ambient[] = {0.4, 0.4, 0.4, 0.5};
	float torus2_diffuse[] = {0.5, 0.5, 0.5, 0.5};
	float torus2_specular[] = {0.3, 0.3, 0.3, 0.5};

	glPushMatrix();  //рисуем тор

	glTranslatef(400, 20, -150); //положение 400, -150
	glRotatef(2.0 * a_x, 1, 0, 0);
	glRotatef(2.1 * a_y, 0, 1, 0);
	glRotatef(1.9 * a_z, 0, 0, 1);

	if (def)
	{ 
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, torus1_ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, torus1_diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, torus1_specular);
	}

	renderlimtorus(); //вывод тора
	
	glPopMatrix(); //закончили выводить тор */
}

void renderopaque(int def) //def = default material весь непрозрачный верх
{
	float cube1_ambient[] = {0.444, 0.135, 0.176, 1.0};
	float cube1_diffuse[] = {0.7, 0.2424, 0.07568, 1.0};
	float cube1_specular[] = {0.0, 0.0, 0.0, 1.0};
	float cube2_ambient[] = {0.344, 0.376, 0.735, 1.0};
	float cube2_diffuse[] = {0.4, 0.47568, 0.9424, 1.0};
	float cube2_specular[] = {0.0, 0.0, 0.0, 1.0};
	float cube3_ambient[] = {0.844, 0.776, 0.235, 1.0};
	float cube3_diffuse[] = {0.9, 0.87568, 0.3424, 1.0};
	float cube3_specular[] = {0.0, 0.0, 0.0, 1.0};
	float torus_ambient[] = {0.944, 0.376, 0.235, 1.0};
	float torus_diffuse[] = {0.97, 0.37568, 0.2424, 1.0};
	float torus_specular[] = {0.0, 0.0, 0.0, 1.0};
	float torus2_ambient[] = {1.0, 1.0, 0.0, 1.0};
	float torus2_diffuse[] = {1.0, 1.0, 0.0, 1.0};
	float torus2_specular[] = {0.3, 0.3, 0.3, 1.0};
	float wall_ambient[] = {0.649, 0.3174, 0.0488, 1.0};
	float wall_diffuse[] = {0.649, 0.3174, 0.0488, 1.0};
	float wall_specular[] = {0.3, 0.1, 0.0, 1.0};
	float red_color[] = {1.0, 0.0, 0.0, 1.0};
	float green_color[] = {0.0, 1.0, 0.0, 1.0};
	float blue_color[] = {0.0, 0.0, 1.0, 1.0};
	float spec[] = {0.0, 0.0, 0.0, 1.0};
	
	int i;

	glPushMatrix();  //рисуем куб

	glTranslatef(0, 15, 0); //положение 0,0
	glRotatef(a_x, 1, 0, 0);
	glRotatef(a_y, 0, 1, 0);
	glRotatef(a_z, 0, 0, 1);

	if (def)
	{ 
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, cube1_ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cube1_diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, cube1_specular);
	}

	glCallList(3); //вывод куба
	
	glPopMatrix(); //закончили выводить первый куб */

	glPushMatrix();  //рисуем второй куб

	glTranslatef(400, 10, -400); //положение 400,-400
	glRotatef(3.0 * a_x, 1, 0, 0);
	glRotatef(0.5 * a_y, 0, 1, 0);
	glRotatef(1.3 * a_z, 0, 0, 1);

	if (def)
	{ 
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, cube2_ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cube2_diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, cube2_specular);
	}

	glCallList(3); //вывод куба
	
	glPopMatrix(); //закончили выводить второй куб */

	glPushMatrix();  //рисуем третий куб

	glTranslatef(-600, 15, 100); //положение 600,-100
	glRotatef(5.0 * a_x, 1, 0, 0);
	glRotatef(2.5 * a_y, 0, 1, 0);
	glRotatef(3.3 * a_z, 0, 0, 1);

	if (def)
	{ 
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, cube3_ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cube3_diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, cube3_specular);
	}

	glCallList(3); //вывод куба
	
	glPopMatrix(); //закончили выводить третий куб */

	glPushMatrix();  //рисуем тор

	glTranslatef(0, 20, -400); //положение 0,-400
	glRotatef(5.0 * a_x, 1, 0, 0);
	glRotatef(2.5 * a_y, 0, 1, 0);
	glRotatef(3.3 * a_z, 0, 0, 1);

	if (def)
	{ 
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, torus_ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, torus_diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, torus_specular);
	}

	glCallList(6); //вывод тора
	
	glPopMatrix(); //закончили выводить тор */

	glPushMatrix();  //рисуем второй тор

	glTranslatef(-600, 15, -550); //положение -600, -550
	glRotatef(7.0 * a_x, 1, 0, 0);
	glRotatef(8.3 * a_y, 0, 1, 0);
	glRotatef(8.7 * a_z, 0, 0, 1);

	if (def)
	{ 
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, torus2_ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, torus2_diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, torus2_specular);
	}

	renderlimtorus(); //вывод тора
	
	glPopMatrix(); //закончили выводить второй тор */



	glPushMatrix();  //рисуем стены

	if (def)
	{
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, wall_ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, wall_diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, wall_specular);
	}

	glTranslatef(-800, 0, -800); //сначала спереди
	glCallList(4);
	for (i = 1; i <= 31; ++i)
	{
		glTranslatef(50, 0, 0);
        glCallList(4);
		
	}

	glTranslatef(0, 0, 1600);  //потом сзади
	glCallList(4);
	for (i = 1; i <= 31; ++i)
	{
		glTranslatef(-50, 0, 0);
	    glCallList(4);
	}
	
	glPopMatrix(); 

	glPushMatrix(); //теперь слева

	glTranslatef(-800, 0, 800);
	glCallList(5);
	for (i = 1; i <= 31; ++i)
	{
		glTranslatef(0, 0, -50);
	    glCallList(5);
	}

	glTranslatef(1600, 0, 0); //и справа
	glCallList(5);
	for (i = 1; i <= 31; ++i)
	{
		glTranslatef(0, 0, 50);
	    glCallList(5);
	}

	glPopMatrix(); 

	renderwalls(); //внутренние стены
}

void renderscene()
{
/*
    алгоритм вывода сцены: сначала нарисовать все непрозрачные объекты сверху
	затем нарисовать все непрозрачные объекты снизу
	нарисовать все прозрачные объекты снизу
	нарисовать пол
	нарисовать тени на полу
	нарисовать прозрачные сверху
*/
	float y_ambient[] = {0.629, 0.235, 0.276, 0.2};
	float y_diffuse[] = {0.7, 0.61424, 0.07568, 0.2};
	float y_specular[] = {0.2, 0.1, 0.2, 0.2};

	float l0_position[] = {-800, 300, -800, 0};
	float l0_direction[] = {0.6, -1, 0.35};
	float f1_ambient[] = {0.3, 0.5, 0.3, 0.85};
	float f1_diffuse[] = {0.05, 0.1, 0.05, 0.85};
	float f1_specular[] = {0.4, 0.4, 0.4, 0.85};
	float f2_ambient[] = {0.7, 0.7, 0.7, 0.85};
	float f2_diffuse[] = {0.4, 0.4, 0.4, 0.85};
	float f2_specular[] = {0.3, 0.3, 0.3, 0.85};
	float sdw_mat[] = {0, 0, 0, 0.4};
	float sdwlim_mat[] = {0, 0, 0, 0.1};
	float sdw_mtrx[] = {1, 0,    0, 0, //матрица проекции для теней
		              0.6, 0, 0.25, 0,
						0, 0,    1, 0,
                        0, 0,    0, 1};  

	glLightfv(GL_LIGHT0, GL_POSITION, l0_position);
	glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, l0_direction); 
	
	glEnable(GL_LIGHT0);
	renderopaque(1);  //вывод верха

	glPushMatrix();  //вывод отражения
	glScalef(1, -1, 1);
	glLightfv(GL_LIGHT0, GL_POSITION, l0_position);
	glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, l0_direction); 
    glFrontFace(GL_CW);
	renderopaque(1);  //рисуем отражение
	renderlim(1); //прозрачное отражение
	glFrontFace(GL_CCW);
	glPopMatrix(); //отражение нарисовано  


	glPushMatrix(); //рисуем пол
	glLightfv(GL_LIGHT0, GL_POSITION, l0_position);
	glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, l0_direction); 

	glClearStencil(0x0);  //очищаем буфер
	glStencilFunc(GL_ALWAYS, 0x1, 0xffffffff);
	glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);  
	glEnable(GL_STENCIL_TEST);
	//при любой записи в буфер помещается единичка
	//т.е. пол является местом вывода теней
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, f1_ambient);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, f1_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, f1_specular);

	glCallList(1); //половина пола нарисована
	
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, f2_ambient);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, f2_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, f2_specular);

	glCallList(2);
	
	glPopMatrix();  //пол нарисован */


	
	glPushMatrix();  //рисуем тени 
	glDisable(GL_LIGHT0); //для вывода теней свет не нужен

	glStencilFunc(GL_EQUAL, 0x1, 0xffffffff);
	glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);

	// устанавливаем материал для тени - чёрный цвет, альфа = 0.2 

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, sdw_mat);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, sdw_mat);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, sdw_mat);

	

	glTranslatef(0, 0.1, 0);
	glMultMatrixf(sdw_mtrx); //задаём матрицу проецирования на пол
	renderopaque(0); //ещё раз рисуем сцену

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, sdwlim_mat); // тени от прозрачных объектов
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, sdwlim_mat);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, sdwlim_mat);
	renderlim(0);

	glDisable(GL_STENCIL_TEST);
	glPopMatrix();  //тени нарисованы  */

	glPushMatrix();
	glEnable(GL_LIGHT0);
	renderlim(1);
	glPopMatrix();
}

void display()
{
	glClearColor(0.48, 0.96, 0.99, 1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(cx, cy, cz, 
		      cx + cos(ha) * cos(va),  cy + sin(va) , cz - sin(ha) * cos(va), 
			  0, 1, 0); 

    renderscene();
	
	glutSwapBuffers();
}

int test(float mx, float mz)
{
	int i, j;

	if (800 - cx < 15 && mx > 0)
		return 0;
	if (-800 - cx > -15 && mx < 0)
		return 0;
	if (800 - cz < 15 && mz > 0)
		return 0;
	if (-800 - cz > -15 && mz < 0)
		return 0;
	
	for (j = 1; j<= 32; ++j)
		if ( (cx > -800 + 50 * (j - 1)) && (cx < -800 + 50 * j) )
		{
			for (i = 1; i <= 31; ++i)
				if ( (cz < -800 + 50 * i + 15) && (cz > -800 + 50 * i - 15) )
					if (fwalls[32 * (i - 1) + j - 1])
					{
						if (cz < -800 + 50 * i && mz > 0)
							return 0;
						if (cz > -800 + 50 * i && mz < 0)
							return 0;
					}
		}

	for (i = 1; i <= 32; ++i)
		if ( (cz > -800 + 50 * (i - 1)) && (cz < -800 + 50 * i) )
		{
			for (j = 1; j <= 31; ++j)
				if ( (cx < -800 + 50 * j + 15) && (cx > -800 + 50 * j - 15) )
					if (swalls[31 * (i - 1) + j - 1])
					{
						if (cx < -800 + 50 * j && mx > 0)
							return 0;
						if (cx > -800 + 50 * j && mx < 0)
							return 0;
					}
		}  

	return 1;
}

void keyboard(unsigned char key, int x, int y)
{
	switch (key)
	{
        case 27: exit(0);

		case 'w': case 'W': 
		{
			if (test(cos(ha) * cos(va), -sin(ha) * cos(va)))
			{
				cx += cos(ha) * cos(va) * speed;
				cz -= sin(ha) * cos(va) * speed;
			}
			glutPostRedisplay();
			break;
		}

		case 's': case 'S':
		{
			if (test(-cos(ha) * cos(va), sin(ha) * cos(va)))
			{
				cx -= cos(ha) * cos(va) * speed;
				cz += sin(ha) * cos(va) * speed;
			}
			glutPostRedisplay();
			break;
		}

		case 'a': case 'A':
		{
			if (test(cos(ha + pi / 2), sin(ha - pi / 2)))
			{
				cx += cos(ha + pi / 2) * speed;
				cz += sin(ha - pi / 2) * speed;
			}
			glutPostRedisplay();
			break;
		}

		case 'd': case 'D':
		{
			if (test(cos(ha - pi / 2), -sin(ha - pi / 2)))
			{
				cx += cos(ha - pi / 2) * speed;
				cz -= sin(ha - pi / 2) * speed;
			}
			glutPostRedisplay();
			break;
		}

		case 'U': case 'u': //speed up
		{
			if (speed < 10)
				speed += 0.2;
			break;
		}

		case 'J': case 'j': //speed down
		{
			if (speed > 1)
				speed -= 0.2;
			break;
		}

		case 32:
		{
			jump = 1;
			break;
		}

	}
}

void motionfunc(int x, int y)
{
	ha -= (x - width / 2.0) / 5000 * rspeed;
	va -= (y - height / 2.0) / 5000 * rspeed;
	if (va > pi / 3)
	{
		va = pi / 3;
	}
	if (va < -pi / 6)
	{
		va = -pi / 6;
	}
	if (x != width / 2 || y != height / 2)
	    glutWarpPointer(width / 2, height / 2);
	glutPostRedisplay();
}

void idle()
{
	a_x += 0.08;

	a_y += 0.1;

	a_z += 0.12;

	if (jump == 1)
	{
		j_angle += 0.12;
		if (j_angle >= pi)
		{
			j_angle = 0;
			cy = h;
			jump = 0;
		}
		cy = 15 + j_height * sin(j_angle);
	}

	glutPostRedisplay();
}

void main()
{
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
	glutInitWindowSize(width, height);
	glutCreateWindow("Final project");

	init();

	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMotionFunc(motionfunc);
	glutIdleFunc(idle);

	glutMainLoop();
}

unsigned char* LoadTrueColorBMPFile(const char* path, int* width,int* height)
{
	unsigned char *result = NULL;

    FILE *in = fopen(path, "rb");
	if (!in)
		return NULL;

    BMPHeader hdr;
    fread(&hdr, sizeof(hdr), 1, in);
    if (hdr.type != 0x4D42)           // Not a bitmap file at all
		return NULL;

	BMPInfoHeader infoHdr;
	fread(&infoHdr, sizeof(infoHdr), 1, in);
	if (infoHdr.bitsPerPixel != 24)   // Not a truecolor bitmap
		return NULL;
	if (infoHdr.compression)          // Compressed bitmap
		return NULL;
	if ((result = new unsigned char[infoHdr.width * infoHdr.height * 3]) == NULL)
		return NULL;
    fseek(in, hdr.offset - sizeof(hdr) - sizeof(infoHdr), SEEK_CUR);   

	unsigned char* dst = result;
	for (int y = 0; y < infoHdr.height; y++) 
	{
		for (int x = 0; x < infoHdr.width; x++) 
		{
			dst[2] = fgetc(in);
			dst[1] = fgetc(in);
			dst[0] = fgetc(in);
			dst += 3;
		}
        for (x = 0; x < ((4 - (infoHdr.width & 3)) & 3); x++)  // Skip alignment bytes
           fgetc(in);
    }
    fclose(in);
	*width = infoHdr.width;
	*height = infoHdr.height;

	return result;
}
