#include <afxdlgs.h>
#include <math.h>
//=== Подключение заголовков библиотек OpenGL
#include <gl/gl.h>
#include <gl/glu.h>
#include <vector>
using namespace std;



#pragma once

//========== Вспомогательный класс
class CPoint3D
{
public:
	//====== Координаты точки
	float x;
	float y;
	float z;

	//====== Набор конструкторов
	CPoint3D () 
	{
 	x = y = z = 0.f;
 }

	CPoint3D (float c1, float c2, float c3)
	{
		x = c1;
		z = c2;
		y = c3;
	}
	//====== Операция присвоения
	CPoint3D& operator= (const CPoint3D& pt)
	{
		x = pt.x;	z = pt.z;	y = pt.y;
		return *this;
	}

	//====== Конструктор копирования
	CPoint3D (const CPoint3D& pt)
	{
		*this = pt;
	}
};

//=========== Класс окна OpenGL
class COGView : public CView
{
protected:
	COGView();
	DECLARE_DYNCREATE(COGView)

public:
	virtual ~COGView();
	virtual void OnDraw(CDC* pDC);
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

	//======= Новые данные класса
	long		m_BkClr;			// Цвет фона окна
	int			m_LightParam[11];// Параметры освещения
	HGLRC		m_hRC;				// Контекст OpenGL
	HDC			m_hdc; 			// Контекст Windows
	GLfloat	m_AngleX;			// Угол поворота вокруг оси X
	GLfloat	m_AngleY;			// Угол поворота вокруг оси Y
	GLfloat	m_AngleView;		// Угол перспективы
	GLfloat	m_fRangeX;		// Размер объекта вдоль X
	GLfloat	m_fRangeY;		// Размер объекта вдоль Y
	GLfloat	m_fRangeZ;		// Размер объекта вдоль Z
	GLfloat	m_dx;				// Квант смещения вдоль X
	GLfloat	m_dy;				// Квант смещения вдоль Y
	GLfloat	m_xTrans;			// Cмещение вдоль X
	GLfloat	m_yTrans;			// Cмещение вдоль Y
	GLfloat	m_zTrans;			// Cмещение вдоль Z
	GLenum		m_FillMode;		// Режим заполнения полигонов
	bool		m_bCaptured;		// Признак захвата мыши
	bool		m_bRightButton;	// Флаг правой кнопки мыши
	bool		m_bQuad;			// Флаг использования GL_QUAD
	CPoint		m_pt;				// Текущая позиция мыши
	UINT		m_xSize;			// Текущий размер окна вдоль X
	UINT		m_zSize;			// Текущий размер окна вдоль Y

	//====== Массив вершин поверхности
	vector <CPoint3D> m_cPoints;

	//====== Новые методы класса
	//====== Подготовка изображения
	void DrawScene();
	//====== Создание графика по умолчанию
	void DefaultGraphic();
	//====== Создание массива по данным из буфера
	void SetGraphPoints(BYTE* buff, DWORD nSize);
	//====== Установка параметров освещения
	void SetLight();
	//====== Изменение одного из параметров освещения
	void SetLightParam (short lp, int nPos);
	//====== Определение действующих параметров освещения
	void GetLightParams(int *pPos);
	//====== Работа с файлом данных
	void ReadData();
	//====== Чтение данных из файла
	bool DoRead(HANDLE hFile);
	//====== Установка Работа с файлом данных 
	void SetBkColor();

	DECLARE_MESSAGE_MAP()
};



COGView::COGView()
{
	//====== Контекст передачи пока отсутствует
	m_hRC = 0;

	//====== Начальный разворот изображения
	m_AngleX = 35.f;
	m_AngleY = 20.f;

	//====== Угол зрения для матрицы проекции
	m_AngleView = 45.f;

	//====== Начальный цвет фона
	m_BkClr = RGB(0, 0, 96);

	// Начальный режим заполнения внутренних точек полигона
	m_FillMode = GL_FILL;

	//====== Подготовка графика по умолчанию
	DefaultGraphic();

	//====== Начальное смещение относительно центра сцены
	//====== Сдвиг назад на полуторный размер объекта
	m_zTrans = -1.5f*m_fRangeX;
	m_xTrans = m_yTrans = 0.f;

	//== Начальные значения квантов смещения (для анимации)
	m_dx = m_dy = 0.f;

	//====== Мышь не захвачена
	m_bCaptured = false;
	//====== Правая кнопка не была нажата
	m_bRightButton = false;
	//====== Рисуем четырехугольниками
	m_bQuad = true;

	//====== Начальный значения параметров освещения
	m_LightParam[0] = 50;	// X position
	m_LightParam[1] = 80;	// Y position
	m_LightParam[2] = 100;	// Z position
	m_LightParam[3] = 15;	// Ambient light
	m_LightParam[4] = 70;	// Diffuse light
	m_LightParam[5] = 100;	// Specular light
	m_LightParam[6] = 100;	// Ambient material
	m_LightParam[7] = 100;	// Diffuse material
	m_LightParam[8] = 40;	// Specular material
	m_LightParam[9] = 70;	// Shininess material
	m_LightParam[10] = 0;	// Emission material
}



BOOL COGView::PreCreateWindow(CREATESTRUCT& cs)
{
	//====== Добавляем биты стиля, нужные OpenGL
	cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
	return CView::PreCreateWindow(cs);
}

BOOL COGView::OnEraseBkgnd(CDC* pDC)
{
	return TRUE;
}



int COGView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	PIXELFORMATDESCRIPTOR pfd =	// Описатель формата
	{
		sizeof(PIXELFORMATDESCRIPTOR),// Размер структуры
		1,							// Номер версии
		PFD_DRAW_TO_WINDOW |	// Поддержка GDI
		PFD_SUPPORT_OPENGL |	// Поддержка OpenGL
		PFD_DOUBLEBUFFER,		// Двойная буферизация
		PFD_TYPE_RGBA,			// Формат RGBA, не палитра
		24, 						// Количество плоскостей
		 							// в каждом буфере цвета
		24,	0,						// Для компоненты Red
		24,	0,						// Для компоненты Green
		24,	0,						// Для компоненты Blue
		24,	0,						// Для компоненты Alpha
		0,							// Количество плоскостей
									// буфера Accumulation
		0,			 				// То же для компоненты Red
		0,			 				// для компоненты Green
		0,							// для компоненты Blue
		0,							// для компоненты Alpha
		32, 						// Глубина Z-буфера
		0,							// Глубина буфера Stencil
		0,							// Глубина буфера Auxiliary
		0,				 			// Теперь игнорируется
		0,							// Количество плоскостей
		0,							// Теперь игнорируется
		0,							// Цвет прозрачной маски
		0							// Теперь игнорируется
	};

	//====== Добываем дежурный контекст
	m_hdc = ::GetDC(GetSafeHwnd());

	//====== Просим выбрать ближайший совместимый формат
	int iD = ChoosePixelFormat(m_hdc, &pfd);
	if ( !iD )
	{
		MessageBox("ChoosePixelFormat::Error");
		return -1;
	}

	//====== Пытаемся установить этот формат
	if ( !SetPixelFormat (m_hdc, iD, &pfd) )
	{
		MessageBox("SetPixelFormat::Error");
		return -1;
	}

	//====== Пытаемся создать контекст передачи OpenGL
	if ( !(m_hRC = wglCreateContext (m_hdc)))
	{
		MessageBox("wglCreateContext::Error");
		return -1;
	}

	//====== Пытаемся выбрать его в качестве текущего
	if ( !wglMakeCurrent (m_hdc, m_hRC))
	{
		MessageBox("wglMakeCurrent::Error");
		return -1;
	}

	//====== Теперь можно посылать команды OpenGL
	glEnable(GL_LIGHTING);			// Будет освещение
	//====== Будет только один источник света
	glEnable(GL_LIGHT0);
	//====== Необходимо учитывать глубину (ось Z)
	glEnable(GL_DEPTH_TEST);
	//====== Необходимо учитывать цвет материала поверхности
	glEnable(GL_COLOR_MATERIAL);

	//====== Устанавливаем цвет фона
	SetBkColor();

	//====== Создаем изображение и запоминаем в списке
	DrawScene();
	return 0;
}



void COGView::OnDestroy(void)
{
	//====== Останавливаем таймер анимации
	KillTimer(1);

	//====== Отсоединяем кнтекст от потока
	wglMakeCurrent(0, 0);
	//====== Удаляем контекст
	if (m_hRC)
	{
		wglDeleteContext(m_hRC);
		m_hRC = 0;
	}

	CView::OnDestroy();
}


void COGView::OnSize(UINT nType, int cx, int cy)
{
	//====== Вызов родительской версии
	CView::OnSize(nType, cx, cy);

	//====== Вычисление диспропорций окна
	double dAspect = cx<=cy ? double(cy)/cx 
								 : double(cx)/cy;

	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();

	//====== Установка режима перспективной проекции
	gluPerspective (m_AngleView, dAspect, 0.01, 10000.);

	//====== Установка прямоугольника просмотра
	glViewport(0, 0, cx, cy);
}



void COGView:: OnDraw(CDC* pDC)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//======= Установка параметров освещения
	SetLight();

	//====== Формирование матрицы моделирования
	glTranslatef(m_xTrans,m_yTrans,m_zTrans);
	glRotatef (m_AngleX, 1.0f, 0.0f, 0.0f );
	glRotatef (m_AngleY, 0.0f, 1.0f, 0.0f );

	//====== Вызов рисующих команд из списка
	glCallList(1);

	//====== Переключение буферов
	SwapBuffers(m_hdc);
}



void COGView::SetLight()
{
	//====== Обе поверхности изображения участвуют
	//====== при вычислении цвета пикселов
	//====== при учете параметров освещения
	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,1);

	//====== Позиция источника освещения
	//====== зависит от размеров объекта
	float fPos[] =
	{
		(m_LightParam[0]-50)*m_fRangeX/100,
		(m_LightParam[1]-50)*m_fRangeY/100,
		(m_LightParam[2]-50)*m_fRangeZ/100,
		1.f
	};
	glLightfv(GL_LIGHT0, GL_POSITION, fPos);

	//====== Интенсивность окружающего освещения
	float f = m_LightParam[3]/100.f;
	float fAmbient[4] = { f, f, f, 0.f };
	glLightfv(GL_LIGHT0, GL_AMBIENT, fAmbient);

	//====== Интенсивность рассеянного света
	f = m_LightParam[4]/100.f;	
	float fDiffuse[4] = { f, f, f, 0.f };
	glLightfv(GL_LIGHT0, GL_DIFFUSE, fDiffuse);

	//====== Интенсивность отраженного света
	f = m_LightParam[5]/100.f;
	float fSpecular[4] = { f, f, f, 0.f };
	glLightfv(GL_LIGHT0, GL_SPECULAR, fSpecular);

	//====== Отражающие свойства материала
	//====== для разных компонент света
	f = m_LightParam[6]/100.f;
	float fAmbMat[4] = { f, f, f, 0.f };
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, fAmbMat);

	f = m_LightParam[7]/100.f;
	float fDifMat[4] = { f, f, f, 1.f };
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fDifMat);

	f = m_LightParam[8]/100.f;
	float fSpecMat[4] = { f, f, f, 0.f };
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fSpecMat);

	//====== Блесткость материала
	float fShine = 128 * m_LightParam[9]/100.f;
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, fShine);

	//====== Излучение света материалом
	f = m_LightParam[10]/100.f;
	float fEmission[4] = { f, f, f, 0.f };
	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, fEmission);
}



void COGView::SetBkColor()
{
	//====== Расщепление цвета на три компоненты
	GLclampf red	= GetRValue(m_BkClr)/255.f,
			 green	= GetGValue(m_BkClr)/255.f,
			 blue	= GetBValue(m_BkClr)/255.f;
	//====== Установка цвета фона (стирания) окна
	glClearColor (red, green, blue, 0.f);

	//====== Непосредственное стирание
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}



void COGView::DrawScene()
{
	//====== Создание списка рисующих команд
	glNewList(1, GL_COMPILE);

	//====== Установка режима заполнения
	//====== внутренних точек полигонов
	glPolygonMode(GL_FRONT_AND_BACK, m_FillMode);

	//====== Размеры изображаемого объекта
	UINT	nx = m_xSize-1,
			nz = m_zSize-1;

	//====== Выбор способа создания полигонов
	if (m_bQuad)
		glBegin (GL_QUADS);

	//====== Цикл прохода по слоям изображения (ось Z)
	for (UINT z=0, i=0;  z<nz;  z++, i++)
	{
		//====== Связанные полигоны начинаются
		//====== на каждой полосе вновь
		if (!m_bQuad)
			glBegin(GL_QUAD_STRIP);

		//====== Цикл прохода вдоль оси X
		for (UINT x=0;  x<nx;  x++, i++)
		{
			// i, j, k, n - 4 индекса вершин примитива при
			// обходе в направлении против часовой стрелки

			int	j = i + m_xSize,	// Индекс узла с большим Z
				k = j+1,			// Индекс узла по диагонали
				n = i+1; 			// Индекс узла справа

			//=== Выбор координат 4-х вершин из контейнера
			float
				xi = m_cPoints[i].x,
				yi = m_cPoints[i].y,
				zi = m_cPoints[i].z,

				xj = m_cPoints[j].x,
				yj = m_cPoints[j].y,
				zj = m_cPoints[j].z,

				xk = m_cPoints[k].x,
				yk = m_cPoints[k].y,
				zk = m_cPoints[k].z,

				xn = m_cPoints[n].x,
				yn = m_cPoints[n].y,
				zn = m_cPoints[n].z,

				//=== Координаты векторов боковых сторон
				ax = xi-xn,
				ay = yi-yn,

				by = yj-yi,
				bz = zj-zi,

				//====== Вычисление вектора нормали
				vx = ay*bz,
				vy = -bz*ax,
				vz = ax*by,

				//====== Модуль нормали
				v  = float(sqrt(vx*vx + vy*vy + vz*vz));
			
			//====== Нормировка вектора нормали
			vx /= v;
			vy /= v;
			vz /= v;

			//====== Задание вектора нормали
			glNormal3f (vx,vy,vz);

			// Ветвь создания несвязанных четырехугольников
			if (m_bQuad)
			{
				//====== Обход вершин осуществляется
				//====== в направлении против часовой стрелки
				glColor3f (0.2f, 0.8f, 1.f);
				glVertex3f (xi, yi, zi);
				glColor3f (0.6f, 0.7f, 1.f);
				glVertex3f (xj, yj, zj);
				glColor3f (0.7f, 0.9f, 1.f);
				glVertex3f (xk, yk, zk);
				glColor3f (0.7f, 0.8f, 1.f);
				glVertex3f (xn, yn, zn);
			}
			else
			//====== Ветвь создания цепочки четырехугольников
			{
				glColor3f (0.9f, 0.9f, 1.0f);
				glVertex3f (xn, yn, zn);
				glColor3f (0.5f, 0.8f, 1.0f);
				glVertex3f (xj, yj, zj);
			}
		}
		//====== Закрываем блок команд GL_QUAD_STRIP
		if (!m_bQuad)
			glEnd();
	}
	//====== Закрываем блок команд GL_QUADS
	if (m_bQuad)
		glEnd();

	//====== Закрываем список команд OpenGL
	glEndList();
}



void COGView::DefaultGraphic()
{
	//====== Размеры сетки узлов
	m_xSize = m_zSize = 33;

	//====== Число ячеек на единицу меньше числа узлов
	UINT	nz = m_zSize - 1,
			nx = m_xSize - 1;

	// Размер файла в байтах для хранения значений функции
	DWORD nSize = m_xSize * m_zSize * sizeof(float)
						+ 2*sizeof(UINT);

	//====== Временный буфер для хранения данных
	BYTE *buff = new BYTE[nSize+1];

	//====== Показываем на него указателем целого типа
	UINT *p = (UINT*)buff;

	//====== Размещаем данные целого типа
	*p++ = m_xSize;
	*p++ = m_zSize;

	//====== Меняем тип указателя, так как дальше
	//====== собираемся записывать вещественные числа
	float *pf = (float*)p;

	//=== Предварительно вычисляем коэффициенты уравнения
	double	fi = atan(1.)*6,
			kx = fi/nx,
			kz = fi/nz;

	//====== В двойном цикле пробега по сетке узлов
	//=== вычисляем и помещаем в буфер данные типа float
	for (UINT i=0;  i<m_zSize;  i++)
	{
		for (UINT j=0;  j<m_xSize;  j++)
		{
			*pf++ = float (sin(kz*(i-nz/2.))
							* sin(kx*(j-nx/2.)));
		}
	}

	//=== Переменная для того, чтобы узнать сколько
	//=== байт было реально записано в файл
	DWORD nBytes;

	//=== Создание и открытие файла данных sin.dat
	HANDLE hFile = CreateFile(_T("sin.dat"), GENERIC_WRITE,
					0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);

	//====== Запись в файл всего буфера
	WriteFile(hFile,(LPCVOID)buff, nSize,&nBytes, 0);

	//====== Закрываем файл
	CloseHandle(hFile);

	//====== Создание динамического массива m_cPoints
	SetGraphPoints (buff, nSize);

	//====== Освобождаем временный буфер
	delete [] buff;
}



void COGView::SetGraphPoints(BYTE* buff, DWORD nSize)
{
	//====== Готовимся к расшифровке данных буфера
	//====== Указываем на него указателем целого типа
	UINT *p = (UINT*)buff;

	//=== Выбираем данные целого типа, сдвигая указатель
	m_xSize = *p;
	m_zSize = *++p;

	//====== Проверка на непротиворечивость
	if (m_xSize<2 || m_zSize<2 || 
			m_xSize*m_zSize*sizeof(float)
			+ 2 * sizeof(UINT) != nSize)
	{
		MessageBox(_T("Данные противоречивы"));
		return;
	}

	//====== Изменяем размер контейнера
	//====== При этом его данные разрушаются
	m_cPoints.resize(m_xSize*m_zSize);

	if (m_cPoints.empty())
	{
		MessageBox(_T("Не возможно разместить данные"));
		return;
	}

	//====== Подготовка к циклу пробега по буферу
	//====== и процессу масштабирования
	float	x, z,
			//====== Считываем первую ординату
			*pf   = (float*)++p,
			fMinY = *pf,
			fMaxY = *pf,
			right = (m_xSize-1)/2.f,
			left  = -right,
			rear  = (m_zSize-1)/2.f,
			front = -rear, 
			range = (right + rear)/2.f;

	UINT	i, j, n;

	//====== Вычисление размаха изображаемого объекта
	m_fRangeY = range;
	m_fRangeX = float(m_xSize);
	m_fRangeZ = float(m_zSize);

	//====== Величина сдвига вдоль оси Z
	m_zTrans = -1.5f * m_fRangeZ;

	//====== Генерируем координаты сетки (X-Z)
	//====== и совмещаем с ординатами Y из буфера
	for (z=front, i=0, n=0;  i<m_zSize;  i++, z+=1.f)
	{
		for (x=left, j=0;  j<m_xSize;  j++, x+=1.f, n++)
		{
			MinMax (*pf, fMinY, fMaxY);
			m_cPoints[n] = CPoint3D(x,z,*pf++);
		}
	}

	//====== Масштабирование ординат
	float zoom = fMaxY > fMinY ? range/(fMaxY-fMinY)
									 : 1.f;

	for (n=0;  n<m_xSize*m_zSize;  n++)
	{
		m_cPoints[n].y = zoom * (m_cPoints[n].y - fMinY)
							- range/2.f;
	}
}



inline void MinMax (float d, float& Min, float& Max)
{
	//====== Корректируем переданные по ссылке параметры
	if (d > Max)
		Max = d;			// Претендент на максимум
	else if (d < Min)
		Min = d;			// Претендент на минимум
}



void COGView::ReadData()
{
	//=== Строка, в которую будет помещен файловый путь
	TCHAR szFile[MAX_PATH] = { 0 };

	//====== Строка фильтров демонстрации файлов
	TCHAR *szFilter =TEXT("Graphics Data Files (*.dat)\0")
						TEXT("*.dat\0")
						TEXT("All Files\0")
						TEXT("*.*\0");

	//====== Выявляем текущую директорию	
	TCHAR szCurDir[MAX_PATH];
	::GetCurrentDirectory(MAX_PATH-1,szCurDir);

	//=== Структура данных, используемая файловым диалогом
	OPENFILENAME ofn;
	ZeroMemory(&ofn,sizeof(OPENFILENAME));

	//====== Установка параметров будущего диалога
	ofn.lStructSize	= sizeof(OPENFILENAME);
	//====== Окно-владелец диалога
	ofn.hwndOwner = GetSafeHwnd();
	ofn.lpstrFilter	= szFilter;
	//====== Индекс строки фильтра (начиная с единицы)
	ofn.nFilterIndex	= 1;
	ofn.lpstrFile		= szFile;
	ofn.nMaxFile		= sizeof(szFile);
	//====== Заголовок окна диалога
	ofn.lpstrTitle	= _T("Найдите файл с данными");
	ofn.nMaxFileTitle = sizeof (ofn.lpstrTitle);
	//====== Особый стиль диалога (только в Win2K)
	ofn.Flags			= OFN_EXPLORER;

	//====== Создание и вызов диалога
	//=== В случае неудачи GetOpenFileName возвращает 0
	if (GetOpenFileName(&ofn))
	{
		// Попытка открыть файл, который должен существовать
		HANDLE hFile = CreateFile(ofn.lpstrFile, GENERIC_READ,
				FILE_SHARE_READ, 0, OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL, 0);

		//=== В случае неудачи CreateFile возвращает -1
		if (hFile == (HANDLE)-1)
		{
			MessageBox(_T("Не удалось открыть файл"));
			return;
		}

		//====== Попытка прочесть данные о графике 
		if (!DoRead(hFile))
			return;

		//====== Создание нового изображения
		DrawScene();

		//====== Перерисовка окна OpenGL
		Invalidate(FALSE);
	}
}



//====== Функция непосредственного чтения данных
bool COGView::DoRead(HANDLE hFile)
{
	//====== Сначала узнаем размер файла
	DWORD nSize = GetFileSize (hFile, 0);

	//=== Если не удалось определить размер, GetFileSize
	//====== возвращает 0xFFFFFFFF
	if (nSize == 0xFFFFFFFF)
	{
		GetLastError();
		MessageBox(_T("Некорректный размер файла"));
		CloseHandle(hFile);
		return false;
	}

	//=== Создаем временный буфер размером в весь файл
	BYTE *buff = new BYTE[nSize+1];

	//====== Обработка отказа выделить память
	if (!buff)
	{
		MessageBox(_T("Слишком большой размер файла"));
		CloseHandle(hFile);
		return false;
	}

	//====== Реальный размер файла
	DWORD nBytes;
	//====== Попытка прочесть файл
	ReadFile (hFile, buff, nSize, &nBytes, 0);
	CloseHandle(hFile);

	//====== Если реально прочитано меньшее число байт
	if (nSize != nBytes)
	{
		MessageBox(_T("Ошибка при чтении файла"));
		return false;
	}

	//====== Генерация точек изображения
	SetGraphPoints (buff, nSize);

	//====== Освобождение временного буфера
	delete [] buff;
	//====== Возвращаем успех
	return true;
}



void COGView::OnLButtonDown(UINT nFlags, CPoint point)
{
	//====== Останавливаем таймер
	KillTimer(1);

	//====== Обнуляем кванты перемещения
	m_dx = 0.f;
	m_dy = 0.f;

	//====== Захватываем сообщения мыши,
	//====== направляя их в свое окно
	SetCapture();
	//====== Запоминаем факт захвата
	m_bCaptured = true;
	//====== Запоминаем координаты курсора
	m_pt = point;
}


void COGView::OnRButtonDown(UINT nFlags, CPoint point)
{
	//====== Запоминаем факт нажатия правой кнопки
	m_bRightButton = true;

	//====== Воспроизводим реакцию на левую кнопку
	OnLButtonDown(nFlags, point);
}


void COGView::OnLButtonUp(UINT nFlags, CPoint point)
{
	//====== Если был захват,
	if (m_bCaptured)
	{
		//=== то анализируем желаемый квант перемещения
		//=== на превышение порога чувствительности
		if (fabs(m_dx) > 0.5f || fabs(m_dy) > 0.5f)
			//=== Включаем режим постоянного вращения
			SetTimer(1,33,0);
		else
			//=== Выключаем режим постоянного вращения
			KillTimer(1);

		//====== Снимаем флаг захвата мыши
		m_bCaptured = false;
		//====== Отпускаем сообщения мыши
		ReleaseCapture();
	}
}


void COGView::OnRButtonUp(UINT nFlags, CPoint point)
{
	//====== Правая кнопка отпущена
	m_bRightButton = false;
	//====== Снимаем флаг захвата мыши
	m_bCaptured = false;
	//====== Отпускаем сообщения мыши
	ReleaseCapture();
}


void COGView::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_bCaptured)		// Если был захват,
	{
		// Вычисляем компоненты желаемой скорости вращения
		m_dy = float(point.y - m_pt.y)/40.f;
		m_dx = float(point.x - m_pt.x)/40.f;

		//====== Если одновременно была нажата Ctrl,
		if (nFlags & MK_CONTROL)
		{
			//=== Изменяем коэффициенты сдвига изображения
			m_xTrans += m_dx;
			m_yTrans -= m_dy;
		}
		else
		{
			//====== Если была нажата правая кнопка
			if (m_bRightButton)
				//====== Усредняем величину сдвига
				m_zTrans += (m_dx + m_dy)/2.f;
			else
			{
				//====== Иначе, изменяем углы поворота
				m_AngleX += m_dy;
				m_AngleY += m_dx;
			}
		}
		//=== В любом случае запоминаем новое положение мыши
		m_pt = point;
		Invalidate(FALSE);
	}
}


void COGView::OnTimer(UINT nIDEvent)
{
	//====== Если это был наш таймер
	if (nIDEvent==1)
	{
		//====== Увеличиваем углы поворота
		m_AngleX += m_dy;
		m_AngleY += m_dx;
		//====== Ограничители роста углов
		if (m_AngleX > 360)
			m_AngleX -= 360;
		if (m_AngleX <-360)
			m_AngleX += 360;

		if (m_AngleY > 360)
			m_AngleY -= 360;
		if (m_AngleY <-360)
			m_AngleY += 360;

		//====== Просим перерисовать окно
		Invalidate(FALSE);
	}
	else
		//=== Каркас приложения обработает другие таймеры
		CView::OnTimer(nIDEvent);
}



void COGView::OnEditBackground(void)
{
	//====== Создаем объект диалогового класса
	CColorDialog dlg(m_BkClr);
	//====== Устанавливаем бит стиля
	dlg.m_cc.Flags |= CC_FULLOPEN;
	//====== Запускаем диалог и выбираем результат
	if (dlg.DoModal()==IDOK)
	{
		m_BkClr = dlg.m_cc.rgbResult;
		//====== Изменяем цвет фона
		SetBkColor();
		Invalidate(FALSE);
	}
}



void COGView::OnViewQuad(void)
{
	// Инвертируем признак стиля задания четырехугольников
	m_bQuad = ! m_bQuad;

	//====== Заново создаем изображение
	DrawScene();
	Invalidate(FALSE);
	UpdateWindow();
}


void COGView::OnUpdateViewQuad(CCmdUI* pCmdUI)
{
	//====== Вставляем или убираем маркер (пометку)
	pCmdUI->SetCheck(m_bQuad==true);
}



void COGView::OnViewFill(void)
{
	//=== Переключаем режим заполнения четырехугольника
	m_FillMode = m_FillMode==GL_FILL ? GL_LINE : GL_FILL;

	//====== Заново создаем изображение
	DrawScene();
	Invalidate(FALSE);
	UpdateWindow();
}

void COGView::OnUpdateViewFill(CCmdUI *pCmdUI)
{
	//====== Вставляем или убираем маркер выбора
	pCmdUI->SetCheck(m_FillMode==GL_FILL);
}



void COGView::GetLightParams(int *pPos)
{
	//====== Проход по всем регулировкам
	for (int i=0; i<11; i++)
		//====== Заполняем транспортный массив pPos
		pPos[i] = m_LightParam[i];
}

void COGView::SetLightParam (short lp, int nPos)
{
	//====== Синхронизируем параметр lp и
	//====== устанавливаем его в положение nPos
	m_LightParam[lp] = nPos;

	//====== Перерисовываем Вид с учетом изменений
	Invalidate(FALSE);
}



Элемент
Идентификатор
Диалог
IDD_PROP
Ползунок Ambient в группе Light
IDC_AMBIENT
Ползунок Diffuse в группе Light
IDC_DIFFUSE
Ползунок Specular в группе Light
IDC_SPECULAR
Static Text справа от Ambient в группе Light
IDC_AMB_TEXT
Static Text справа от Diffuse в группе Light
IDC_DIFFUSE_TEXT
Static Text справа от Specular в группе Light
IDC_SPECULAR_TEXT
Ползунок Ambient в группе Material
IDC_AMBMAT
Ползунок Diffuse в группе Material
IDC_DIFFMAT
Ползунок Specular в группе Material
IDC_SPECMAT
Static Text справа от Ambient в группе Material
IDC_AMBMAT_TEXT
Static Text справа от Diffuse в группе Material
IDC_DIFFMAT_TEXT
Static Text справа от Specular в группе Material
IDC_SPECMAT_TEXT
Ползунок Shininess
IDC_SHINE
Ползунок Emission
IDC_EMISSION
Static Text справа от Shininess
IDC_SHINE_TEXT
Static Text справа от Emission
IDC_EMISSION_TEXT
Ползунок X
IDC_XPOS
Ползунок Y
IDC_YPOS
Ползунок Z
IDC_ZPOS
Static Text справа от X
IDC_XPOS_TEXT
Static Text справа от Y
IDC_YPOS_TEXT
Static Text справа от Z
IDC_ZPOS_TEXT
Кнопка Data File
IDC_FILENAME



#pragma once

class COGView;				// Упреждающее объявление

class CPropDlg : public CDialog
{
	DECLARE_DYNAMIC(CPropDlg)

public:
	COGView *m_pView; 		// Адрес Вида
	int m_Pos[11]; 			// Массив позиций ползунков

	CPropDlg(COGView* p);
	virtual ~CPropDlg();

	// Метод для выяснения идентификатора активного ползунка
	int GetSliderNum(HWND hwnd, UINT& nID);

	enum { IDD = IDD_PROP };

protected:
	virtual void DoDataExchange(CDataExchange* pDX);

	DECLARE_MESSAGE_MAP()
};


CPropDlg::CPropDlg(COGView* p)
	: CDialog(CPropDlg::IDD, p)
{
	//====== Запоминаем адрес Вида
	m_pView = p;
}



BOOL CPropDlg::OnInitDialog(void)
{
	CDialog::OnInitDialog();

	//====== Заполняем массив текущих параметров света
	m_pView->GetLightParams(m_Pos);

	//====== Массив идентификаторов ползунков
	UINT IDs[] = 
	{
		IDC_XPOS,		IDC_YPOS,		IDC_ZPOS,
		IDC_AMBIENT,
		IDC_DIFFUSE,
		IDC_SPECULAR,
		IDC_AMBMAT,
		IDC_DIFFMAT,
		IDC_SPECMAT,
		IDC_SHINE,
		IDC_EMISSION
	};

	//====== Цикл прохода по всем регуляторам
	for (int i=0; i<sizeof(IDs)/sizeof(IDs[0]); i++)
	{
		//=== Добываем Windows-описатель окна ползунка
		HWND hwnd = GetDlgItem(IDs[i])->GetSafeHwnd();
		UINT nID;

		//====== Определяем его идентификатор
		int num = GetSliderNum(hwnd, nID);

		// Требуем установить ползунок в положение m_Pos[i]
		::SendMessage(hwnd, TBM_SETPOS, TRUE,
						(LPARAM)m_Pos[i]);
		char s[8];
		//====== Готовим текстовый аналог текущей позиции
		sprintf (s,"%d",m_Pos[i]);
		//====== Помещаем текст в окно справа от ползунка
		SetDlgItemText(nID, (LPCTSTR)s);
	}

	return TRUE;
}


int CPropDlg::GetSliderNum(HWND hwnd, UINT& nID)
{
	//====== GetDlgCtrlID по известному hwnd определяет
	//====== и возвращает идентификатор элемента управления
	switch (::GetDlgCtrlID(hwnd))
	{
	//====== Выясняем идентификатор окна справа
	case IDC_XPOS:
		nID = IDC_XPOS_TEXT;
		return 0;
	case IDC_YPOS:
		nID = IDC_YPOS_TEXT;
		return 1;
	case IDC_ZPOS:
		nID = IDC_ZPOS_TEXT;
		return 2;
	case IDC_AMBIENT:
		nID = IDC_AMB_TEXT;
		return 3;
	case IDC_DIFFUSE:
		nID = IDC_DIFFUSE_TEXT;
		return 4;
	case IDC_SPECULAR:
		nID = IDC_SPECULAR_TEXT;
		return 5;
	case IDC_AMBMAT:
		nID = IDC_AMBMAT_TEXT;
		return 6;
	case IDC_DIFFMAT:
		nID = IDC_DIFFMAT_TEXT;
		return 7;
	case IDC_SPECMAT:
		nID = IDC_SPECMAT_TEXT;
		return 8;
	case IDC_SHINE:
		nID = IDC_SHINE_TEXT;
		return 9;
	case IDC_EMISSION:
		nID = IDC_EMISSION_TEXT;
		return 10;
	}
	return 0;
}



void CPropDlg::OnHScroll(UINT nSBCode, UINT nPos,
					CScrollBar* pScrollBar)
{
	//====== Windows-описатель окна активного ползунка
	HWND hwnd = pScrollBar->GetSafeHwnd();

	UINT nID;

	//=== Определяем индекс в массиве позиций ползунков
	int num = GetSliderNum(hwnd, nID);
	int delta, newPos;

	//====== Анализируем код события
	switch (nSBCode)
	{
	case SB_THUMBTRACK:
	case SB_THUMBPOSITION:		// Управление мышью
		m_Pos[num] = nPos;
		break;
	case SB_LEFT:					// Клавиша Home
		delta = -100;
		goto New_Pos;
	case SB_RIGHT:				// Клавиша End
		delta = +100;
		goto New_Pos;
	case SB_LINELEFT:			// Клавиша <-
		delta = -1;
		goto New_Pos;
	case SB_LINERIGHT:			// Клавиша ->
		delta = +1;
		goto New_Pos;
	case SB_PAGELEFT:			// Клавиша PgUp
		delta = -20;
		goto New_Pos;
	case SB_PAGERIGHT:			// Клавиша PgDn
		delta = +20;
		goto New_Pos;

	New_Pos:						// Общая ветвь
		//====== Устанавливаем новое значение регулятора
		newPos = m_Pos[num] + delta;
		//====== Ограничения
		m_Pos[num] = newPos<0 ? 0 : newPos>100 ? 100 : newPos;
		break;
	case SB_ENDSCROLL:
	default:
		return;
	}

	//====== Синхронизируем текстовый аналог позиции
	char s[8];
	sprintf (s,"%d",m_Pos[num]);
	SetDlgItemText(nID, (LPCTSTR)s);

	//====== Передаем изменение в класс Вида
	m_pView->SetLightParam (num, m_Pos[num]);
}



void CPropDlg::OnClose(void)
{
	//=== Обнуляем указатель на объект своего класса
	m_pView->m_pDlg = 0;
	//====== Уничтожаем окно
	CDialog::OnClose();
	//====== Освобождаем память
	delete this;
}


void CPropDlg:: OnClickedFilename(void)
{
	//=== Открываем файловый диалог и читаем данные
	m_pView->ReadData();
}



void COGView::OnEditProperties (void)
{
	//====== Если диалог еще не открыт
	if (!m_pDlg)
	{
		//=== Создаем его и запускаем в немодальном режиме
		m_pDlg = new CPropDlg(this);
		m_pDlg->Create(IDD_PROP);
	}
	else
		// Иначе, переводим фокус в окно существующего диалога
		m_pDlg->SetActiveWindow();
}


void COGView::OnUpdateEditProperties(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck (m_pDlg != 0);
}


void COGView::OnEditProperties (void)
{
	m_pDlg = new CPropDlg(this);
	m_pDlg->Create(IDD_PROP);
}


void COGView::OnUpdateEditProperties(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(m_pDlg == 0);
}




