#include <math.h>
#include <gl/gl.h>
#include <gl/glu.h>

inline void MinMax (float d, float& Min, float& Max)
{
	if (d > Max)
		Max = d;
	else if (d < Min)
		Min = d;
}



// OpenGL.h : Declaration of the COpenGL
#pragma once
#include "resource.h"       // main symbols
#include <atlctl.h>
#include "_IOpenGLEvents_CP.h"

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

	//====== Набор конструкторов и операция присвоения
	CPoint3D () { x = y = z = 0; }

	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;
	}
};

//====== Основной класс, экспонирующий интерфейс IOpenGL
class ATL_NO_VTABLE COpenGL : 
	public CComObjectRootEx<CComSingleThreadModel>,
	public CStockPropImpl<COpenGL, IOpenGL>,
	public IPersistStreamInitImpl<COpenGL>,
	public IOleControlImpl<COpenGL>,
	public IOleObjectImpl<COpenGL>,
	public IOleInPlaceActiveObjectImpl<COpenGL>,
	public IViewObjectExImpl<COpenGL>,
	public IOleInPlaceObjectWindowlessImpl<COpenGL>,
	public ISupportErrorInfo,
	public IConnectionPointContainerImpl<COpenGL>,
	public CProxy_IOpenGLEvents<COpenGL>, 
	public IPersistStorageImpl<COpenGL>,
	public ISpecifyPropertyPagesImpl<COpenGL>,
	public IQuickActivateImpl<COpenGL>,
	public IDataObjectImpl<COpenGL>,
	public IProvideClassInfo2Impl<&CLSID_OpenGL,
			&__uuidof(_IOpenGLEvents), &LIBID_ATLGLLib>,
	public CComCoClass<COpenGL, &CLSID_OpenGL>,
	public CComControl<COpenGL>
{
public:

	//===== Переменные, необходимые для реализации интерфейса
	OLE_COLOR m_clrFillColor;	// Цвет фона окна
	int			m_LightParam[11];// Параметры освещения
	int			m_xPos, m_yPos; 	// Текущая позиция мыши
	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
	UINT		m_xSize;			// Текущий размер окна вдоль X
	UINT		m_zSize;			// Текущий размер окна вдоль Y

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

	//====== Функции, присутствовавшие в стартовой заготовке
	COpenGL();
	HRESULT OnDraw(ATL_DRAWINFO& di);
	void OnFillColorChanged();

DECLARE_OLEMISC_STATUS(OLEMISC_RECOMPOSEONRESIZE | 
	OLEMISC_CANTLINKINSIDE | 
	OLEMISC_INSIDEOUT | 
	OLEMISC_ACTIVATEWHENVISIBLE | 
	OLEMISC_SETCLIENTSITEFIRST
)

DECLARE_REGISTRY_RESOURCEID(IDR_OPENGL)

BEGIN_COM_MAP(COpenGL)
	COM_INTERFACE_ENTRY(IOpenGL)
	COM_INTERFACE_ENTRY(IDispatch)
	COM_INTERFACE_ENTRY(IViewObjectEx)
	COM_INTERFACE_ENTRY(IViewObject2)
	COM_INTERFACE_ENTRY(IViewObject)
	COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
	COM_INTERFACE_ENTRY(IOleInPlaceObject)
	COM_INTERFACE_ENTRY2(IOleWindow,
							IOleInPlaceObjectWindowless)
	COM_INTERFACE_ENTRY(IOleInPlaceActiveObject)
	COM_INTERFACE_ENTRY(IOleControl)
	COM_INTERFACE_ENTRY(IOleObject)
	COM_INTERFACE_ENTRY(IPersistStreamInit)
	COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)
	COM_INTERFACE_ENTRY(ISupportErrorInfo)
	COM_INTERFACE_ENTRY(IConnectionPointContainer)
	COM_INTERFACE_ENTRY(ISpecifyPropertyPages)
	COM_INTERFACE_ENTRY(IQuickActivate)
	COM_INTERFACE_ENTRY(IPersistStorage)
	COM_INTERFACE_ENTRY(IDataObject)
	COM_INTERFACE_ENTRY(IProvideClassInfo)
	COM_INTERFACE_ENTRY(IProvideClassInfo2)
END_COM_MAP()

BEGIN_PROP_MAP(COpenGL)
	PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
	PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
	PROP_ENTRY("FillColor",DISPID_FILLCOLOR,
					CLSID_StockColorPage)
END_PROP_MAP()

BEGIN_CONNECTION_POINT_MAP(COpenGL)
	CONNECTION_POINT_ENTRY(DIID__IOpenGLEvents)
END_CONNECTION_POINT_MAP()

BEGIN_MSG_MAP(COpenGL)
	CHAIN_MSG_MAP(CComControl<COpenGL>)
	DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()

	//====== Поддержка интерфейса ISupportsErrorInfo
	STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid)
	{
		static const IID* arr[] = 
		{
			&IID_IOpenGL,
		};

		for (int i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
		{
			if (InlineIsEqualGUID(*arr[i], riid))
				return S_OK;
		}
		return S_FALSE;
	}

	//====== Поддержка интерфейса IViewObjectEx
	DECLARE_VIEW_STATUS(VIEWSTATUS_SOLIDBKGND |
							VIEWSTATUS_OPAQUE)

	//====== Поддержка интерфейса IOpenGL
public:
	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}
	
	void FinalRelease() 
	{
	}

//====== Экспонируемые методы
	STDMETHODIMP GetLightParams(int* pPos);
	STDMETHODIMP SetLightParam(short lp, int nPos);
	STDMETHODIMP ReadData(void);

	//====== Новые методы класса
	
	//====== Установка параметров освещения
	void SetLight();
	//====== Создание демонстрационного графика
	void DefaultGraphic();

	//====== Чтение файла с данными о графике
	bool DoRead(HANDLE hFile);

	// Заполнение координат точек графика по данным из буфера
	void SetGraphPoints(BYTE* buff, DWORD nSize);

	//====== Управление цветом фона окна
	void SetBkColor();

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

OBJECT_ENTRY_AUTO(__uuidof(OpenGL), COpenGL)



LRESULT COpenGL::OnEraseBkgnd(UINT /*uMsg*/, WPARAM
			 /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
	//====== Устанавливаем флаг завершения обработки
	bHandled = TRUE;
	return 0;
}



LRESULT COpenGL::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/,
							LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	//======= Описатель формата окна OpenGL
	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();
	int iD = ChoosePixelFormat(m_hdc, &pfd);
	if ( !iD )
	{
		ATLASSERT(FALSE);
		return -1;
	}
	//====== Пытаемся установить этот формат
	if ( !SetPixelFormat (m_hdc, iD, &pfd))
	{
		ATLASSERT(FALSE);
		return -1;
	}
	//====== Пытаемся создать контекст передачи OpenGL
	if ( !(m_hRC = wglCreateContext (m_hdc)))
	{
		ATLASSERT(FALSE);
		return -1;
	}
	//====== Пытаемся выбрать его в качестве текущего
	if ( !wglMakeCurrent (m_hdc, m_hRC))
	{
		ATLASSERT(FALSE);
		return -1;
	}

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



LRESULT COpenGL::OnSize(UINT /*uMsg*/, WPARAM /*wParam*/,
								LPARAM lParam, BOOL& /*bHandled*/)
{
	// Распаковываем длинный параметр и узнаем размеры окна
	UINT cx = LOWORD(lParam), cy = HIWORD(lParam);

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

	// Задаем тип текущей матрицы (матрица проекции)
	glMatrixMode (GL_PROJECTION);
	//====== Приравниваем ее к единичной диагональной
	glLoadIdentity();

	//== Параметры перспективы (45 градусов - угол обзора)
	gluPerspective (45., dAspect, 1., 10000.);

	glViewport(0, 0, cx, cy);
	DrawScene();
	return 0;
}



LRESULT COpenGL::OnDestroy(UINT /*uMsg*/, WPARAM
		 /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	KillTimer(1);
	if (m_hRC)
	{
		wglDeleteContext(m_hRC);
		m_hRC = 0;
	}
	return 0;
}



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

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

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

	//====== Начальный цвет фона
	m_clrFillColor = RGB(255,245,255);

	//====== Начальный режим заполнения
	//====== внутренних точек полигона
	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
}



HRESULT COpenGL::OnDraw(ATL_DRAWINFO& di)
{
	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);
	return S_OK;
}



void COpenGL::SetBkColor()
{
	//====== Расщепление цвета на три компоненты
	GLclampf red	= GetRValue(m_clrFillColor)/255.f,
			 green	= GetGValue(m_clrFillColor)/255.f,
			 blue	= GetBValue(m_clrFillColor)/255.f;
	//====== Установка цвета фона (стирания) окна
	glClearColor (red, green, blue, 0.f);
	//====== Непосредственное стирание
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}


void COpenGL::OnFillColorChanged()
{
	//====== Если выбран системный цвет,
	if (m_clrFillColor & 0x80000000)
		//====== то выбираем его по индексу
		m_clrFillColor = ::GetSysColor(m_clrFillColor & 0x1f);

	//====== Изменяем цвет фона окна OpenGL
	SetBkColor();	
}



//====== Подготовка изображения
void COpenGL::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 (xi, yi, zi);
				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.)*12, 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 (exp(-(i+20.*j)/256.)
					*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;
}



STDMETHODIMP COpenGL::ReadData(void)
{
	//=== Строка, в которую будет помещен файловый путь
	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 S_FALSE;
		}

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

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

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



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);
}


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

	return S_OK;
}

STDMETHODIMP COpenGL::SetLightParam(short lp, int nPos)
{
	//====== Синхронизируем параметр lp и устанавливаем
	//====== его в положение nPos
	m_LightParam[lp] = nPos;
	//====== Перерисовываем Вид с учетом изменений
	FireViewChange();
	return S_OK;
}
	


m_bWindowOnly = TRUE;



STDMETHODIMP COpenGL::GetFillMode(DWORD* pMode)
{
	//======= Режим заполнения полигонов
	*pMode = m_FillMode;
	return S_OK;
}

STDMETHODIMP COpenGL::SetFillMode(DWORD nMode)
{
	m_FillMode = nMode;
	//====== Построение нового списка команд OpenGL
	DrawScene();

	// Требование получить разрешение перерисовать окно
	FireViewChange();
	return S_OK;
}

STDMETHODIMP COpenGL::GetQuad(BOOL* bQuad)
{
	//======= Режим построения полигонов
	*bQuad = m_bQuad;
	return S_OK;
}

STDMETHODIMP COpenGL::SetQuad(BOOL bQuad)
{
	m_bQuad = bQuad == TRUE;
	//======= Построение нового списка команд OpenGL
	DrawScene();
	//======= Просьба о перерисовке
	FireViewChange();
	return S_OK;
}



library ATLGLLib
{
	importlib("stdole32.tlb");
	importlib("stdole2.tlb");

	[
		uuid(6DEBB446-C43A-4AB5-BEE1-110510C7AC89),
		helpstring("_IOpenGLEvents Interface")
	]
	dispinterface _IOpenGLEvents
	{
		properties:
		methods:
	};

	[
		uuid(5B3EF182-CD91-426F-9309-2E4869C353DB),
		helpstring("OpenGL Class")
	]
	coclass COpenGL
	{
		[default] interface IOpenGL;
		[default, source] dispinterface _IOpenGLEvents;
	};
	//====== Новые элементы в библиотеке типов сервера
	[
		uuid(3AE16CD6-4558-460F-8A7E-5AB83D40DE9A),
		helpstring("_IGraphPropEvents Interface")
	]
	dispinterface _IGraphPropEvents
	{
		properties:
		methods:
	};

	[
		uuid(1A0C756A-DA17-4630-91B0-72722950B8F7),
		helpstring("GraphProp Class")
	]
	coclass PropDlg
	{
		interface IUnknown;
		[default, source] dispinterface _IGraphPropEvents;
	};



CPropDlg()
{
	m_dwTitleID = IDS_TITLEPropDlg;
	m_dwHelpFileID = IDS_HELPFILEPropDlg;
	m_dwDocStringID = IDS_DOCSTRINGPropDlg;
}



	//====== Реакция на нажатие кнопки Apply
	STDMETHOD(Apply)(void)
	{
		ATLTRACE(_T("CPropDlg::Apply\n"));
		for (UINT i = 0; i < m_nObjects; i++)
		{
			// Do something interesting here
			// ICircCtl* pCirc;
			// m_ppUnk[i]->QueryInterface(IID_ICircCtl,
			//					(void**)&pCirc);
			// pCirc->put_Caption(CComBSTR("smth	special"));
			// pCirc->Release();
		}
		m_bDirty = FALSE;
		return S_OK;
	}



Элемент
Идентификатор
Диалог
IDD_PROPDLG
Ползунок Общая в группе Освещенность
IDC_AMBIENT
Ползунок Рассеянная в группе Освещенность
IDC_DIFFUSE
Ползунок Отраженная в группе Освещенность
IDC_SPECULAR
Text справа от Общая в группе Освещенность
IDC_AMB_TEXT
Text справа от Рассеянная в группе Освещенность
IDC_DIFFUSE_TEXT
Text справа от Отраженная в группе Освещенность
IDC_SPECULAR_TEXT
Ползунок Общая в группе Материал
IDC_AMBMAT
Ползунок Рассеянная в группе Материал
IDC_DIFFMAT
Ползунок Отраженная в группе Материал
IDC_SPECMAT
Text справа от Общая в группе Материал
IDC_AMBMAT_TEXT
Text справа от Рассеянная в группе Материал
IDC_DIFFMAT_TEXT
Text справа от Отраженная в группе Материал
IDC_SPECMAT_TEXT
Ползунок Блестскость
IDC_SHINE
Ползунок Эмиссия
IDC_EMISSION
Text справа от Блестскость
IDC_SHINE_TEXT
Text справа от Эмиссия
IDC_EMISSION_TEXT
Ползунок X
IDC_XPOS
Ползунок Y
IDC_YPOS
Ползунок Z
IDC_ZPOS
Text справа от X
IDC_XPOS_TEXT
Text справа от Y
IDC_YPOS_TEXT
Text справа от Z
IDC_ZPOS_TEXT
Выпадающий список Заполнение
IDC_FILLMODE
Кнопка Quads
IDC_QUADS
Кнопка Выбор файла
IDC_FILENAME



LRESULT CPropDlg::OnInitDialog(UINT uMsg, WPARAM wParam,
								LPARAM lParam, BOOL& bHandled)
{
	__super::OnInitDialog(uMsg, wParam, lParam, bHandled);

	//====== Кроим умный указатель по шаблону IOpenGL
	CComQIPtr<IOpenGL> p(m_ppUnk[0]);
	
	//=== Пытаемся связаться с классом COpenGL и выяснить
	//=== значение переменной m_FillMode
	//=== В случае неудачи даем сообщение об ошибке
	DWORD mode;
	if FAILED (p->GetFillMode(&mode))
	{
		ShowError();
		return 0;
	}

	//====== Работа с combobox по правилам API
	//====== Получаем Windows-описатель окна
	HWND hwnd = GetDlgItem(IDC_FILLMODE);

	//====== Наполняем список строками текста
	SendMessage(hwnd, CB_ADDSTRING, 0,
						(LPARAM)(LPCTSTR)"Points");
	SendMessage(hwnd, CB_ADDSTRING, 0,
						(LPARAM)(LPCTSTR)"Lines");
	SendMessage(hwnd, CB_ADDSTRING, 0,
						(LPARAM)(LPCTSTR)"Fill");

	// Выбираем текущую позицию списка в соответствии
	// со значением, полученным из COpenGL
	WPARAM w = mode == GL_POINT ? 0 
				: mode == GL_LINE ? 1 : 2;
	SendMessage(hwnd, CB_SETCURSEL, w, 0);

	// Повторяем сеанс связи для выяснения позиций ползунков
	if FAILED (p->GetLightParams(m_Pos))
	{
		ShowError();
		return 0;
	}
	
	//=== Мы не надеемся на упорядоченность идентификаторов
	//=== элементов и поэтому заводим массив отображений
	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++)
	{
		//====== Получаем описатель окна
		hwnd = GetDlgItem(IDs[i]);
		UINT nID;
	
		//====== Узнаем идентификатор элемента
		int num = GetSliderNum(hwnd, nID);

		//====== Выставляем позицию
		SendMessage(hwnd, TBM_SETPOS, TRUE, (LPARAM)m_Pos[i]);

		//=== Приводим в соответствие текстовый ярлык
		char s[8];
		sprintf (s,"%d",m_Pos[i]);
		SetDlgItemText(nID, s);
	}

	//=== Выясняем состояние режима изображения полигонов
	if FAILED (p->GetQuad(&m_bQuad))
	{
		ShowError();
		return 0;
	}

	//====== Устанавливаем текст
	SetDlgItemText(IDC_QUADS, m_bQuad ? "Quads" : "Strips");

	return 1;
}



int CPropDlg::GetSliderNum(HWND hwnd, UINT& nID)
{
	// Получаем идентификатор по известному описателю окна
	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::ShowError()
{
	USES_CONVERSION;
	//====== Создаем инерфейсный указатель
	CComPtr<IErrorInfo> pError;
	//====== Класс для работы с Unicode-строками
	CComBSTR sError;
	//====== Выясняем причину отказа
	GetErrorInfo(0, &pError);
	pError->GetDescription(&sError);

	// Преобразуем тип строкового объекта для вывода в окно
	MessageBox(OLE2T(sError),_T("Error"),MB_ICONEXCLAMATION);
}



LRESULT CPropDlg::OnHScroll(UINT /*uMsg*/, WPARAM wParam,
							LPARAM lParam, BOOL& /*bHandled*/)
{
	//====== Информация о событии запакована в wParam
	int	nCode = LOWORD(wParam),
		nPos  = HIWORD(wParam),
		delta,
		newPos;
	HWND hwnd = (HWND) lParam;
		
	// Выясняем номер и идентификатор активного ползунка
	UINT nID;
	int num = GetSliderNum(hwnd, nID); 

	//====== Выясняем суть события
	switch (nCode)
	{
	case SB_THUMBTRACK:		
	case SB_THUMBPOSITION:
		m_Pos[num] = nPos;
		break;

	//====== Сдвиг до упора влево (клавиша Home)
	case SB_LEFT:
		delta = -100;
		goto New_Pos;

	//====== Сдвиг до упора вправо (клавиша End)
	case SB_RIGHT:
		delta = +100;
		goto New_Pos;
	case SB_LINELEFT: // И т.д.
		delta = -1;
		goto New_Pos;
	case SB_LINERIGHT:
		delta = +1;
		goto New_Pos;
	case SB_PAGELEFT:
		delta = -20;
		goto New_Pos;
	case SB_PAGERIGHT:
		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 0;
	}
	
	//=== Готовим текстовое выражение позиции ползунка
	char s[8];
	sprintf (s,"%d",m_Pos[num]);
	SetDlgItemText(nID, (LPCTSTR)s);

	//====== Цикл пробега по всем объектам типа PropDlg
	for (UINT i = 0; i < m_nObjects; i++)
	{
		//====== Добываем интерфейсный указатель
		CComQIPtr<IOpenGL, &IID_IOpenGL> p(m_ppUnk[i]);

		//====== Устанавливаем конкретный параметр
		if FAILED (p->SetLightParam (num,  m_Pos[num]))
		{
			ShowError();
			return 0;
		}
	}
	return 0;
}



LRESULT CPropDlg::OnSelchangeFillmode(WORD /*wNotifyCode*/,
		 WORD /*wID*/, HWND hWndCtl, BOOL& /*bHandled*/)
{
	//====== Цикл пробега по всем объектам типа PropDlg
	for (UINT i = 0; i < m_nObjects; i++)
	{
		CComQIPtr<IOpenGL, &IID_IOpenGL> p(m_ppUnk[i]);
		
		//=== Выясняем индекс строки, выбранной в окне списка
		DWORD sel = (DWORD)SendMessage(hWndCtl,
										CB_GETCURSEL,0,0);
		//=== Преобразуем индекс в режим отображения полигонов
		sel = sel==0 ? GL_POINT
			 : sel==1 ? GL_LINE : GL_FILL;

		//====== Устанавливаем режим в классе COpenGL
		if FAILED (p->SetFillMode(sel))
		{
			ShowError();
			return 0;
		}
	}
	return 0;
}



LRESULT CPropDlg::OnClickedQuads(WORD /*wNotifyCode*/,
		WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
	//====== По всем объектам PropDlg
	for (UINT i = 0; i < m_nObjects; i++)
	{
		//====== Добываем интерфейсный указатель
		CComQIPtr<IOpenGL, &IID_IOpenGL> p(m_ppUnk[i]);

		//====== Переключаем режим
		m_bQuad = !m_bQuad;

		//====== Устанавливаем текст на кнопке
		SetDlgItemText(IDC_QUADS, m_bQuad ? "Quads" 
												: "Strip");
		if FAILED (p->SetQuad(m_bQuad))
		{
			ShowError();
			return 0;
		}
	}
	return 0;
}



LRESULT CPropDlg::OnClickedFilename(WORD /*wNotifyCode*/,
		WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
	for (UINT i = 0; i < m_nObjects; i++)
	{
		CComQIPtr<IOpenGL, &IID_IOpenGL> p(m_ppUnk[i]);

		//====== Вызываем функцию класса COpenGL		
		if FAILED (p->ReadData())
		{
			ShowError();
			return 0;
		}
	}
	return 0;
}



void COpenGL::LimitAngles()
{
	//====== Нормирование углов поворота так, 
	//====== чтобы они были в диапазоне (-360(, +360()
	while (m_AngleX < -360.f)
		m_AngleX += 360.f;
	while (m_AngleX > 360.f)
		m_AngleX -= 360.f;
	while (m_AngleY < -360.f)
		m_AngleY += 360.f;
	while (m_AngleY > 360.f)
		m_AngleY -= 360.f;
}



LRESULT COpenGL::OnMouseMove(UINT /*uMsg*/, WPARAM wParam,
					LPARAM lParam, BOOL& /*bHandled*/)
{
	//====== Если был захват
	if (m_bCaptured)
	{
		//====== Вычисляем желаемую скорость вращения
		short xPos = (short)LOWORD(lParam);
		short yPos = (short)HIWORD(lParam);
		
		m_dy = float(yPos - m_yPos)/20.f;
		m_dx = float(xPos - m_xPos)/20.f;
		
		//====== Если одновременно была нажата Ctrl,
		if (wParam & MK_CONTROL)
		{
			//=== Изменяем коэффициенты сдвига изображения
			m_xTrans += m_dx;
			m_yTrans -= m_dy;
		}
		else 
		{
			//====== Если была нажата правая кнопка
			if (m_bRightButton)
				//====== Усредняем величину сдвига
				m_zTrans += (m_dx + m_dy)/2.f;
			else
			{
				//====== Иначе, изменяем углы поворота
				//====== Сначала нормируем оба угла
				LimitAngles();
				//=== Затем вычисляем модуль одного из них
				double a = fabs(m_AngleX);
				// и изменяем знак приращения (если необходимо)
				if (90. < a && a < 270.)
					m_dx = -m_dx;
				m_AngleX += m_dy;
				m_AngleY += m_dx;
			}
		}
		//=== В любом случае запоминаем новое положение мыши
		m_xPos = xPos;
		m_yPos = yPos;
		FireViewChange();
	}
	return 0;
}
 
LRESULT COpenGL::OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/,
						LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	//====== Нормировка углов поворота
	LimitAngles();

	//====== Увеличиваем эти углы
	m_AngleX += m_dy;
	m_AngleY += m_dx;
	
	//====== Просим перерисовать окно
	FireViewChange();
	return 0;
}



LRESULT COpenGL::OnLButtonDown(UINT /*uMsg*/, WPARAM
				/*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
	//====== Останавливаем таймер
	KillTimer(1);

	//====== Обнуляем кванты перемещения
	m_dx = 0.f;
	m_dy = 0.f;
	//====== Захватываем сообщения мыши,
	//====== направляя их в свое окно
	SetCapture();
	//====== Запоминаем факт захвата
	m_bCaptured = true;
	//====== Запоминаем координаты курсора
	m_xPos = (short)LOWORD(lParam);
	m_yPos = (short)HIWORD(lParam);
	return 0;
}



LRESULT COpenGL::OnLButtonUp(UINT /*uMsg*/, WPARAM 
		/*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	//====== Если был захват,
	if (m_bCaptured)
	{
		//=== то анализируем желаемый квант перемещения
		//=== на превышение порога чувствительности
		if (fabs(m_dx) > 0.5f || fabs(m_dy) > 0.5f)
			//====== Включаем режим постоянного вращения
			SetTimer(1,33);
		else
			//====== Выключаем режим постоянного вращения
			KillTimer(1);

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



LRESULT COpenGL::OnRButtonDown(UINT uMsg, WPARAM wParam,
								LPARAM lParam, BOOL& bHandled)
{
	//====== Запоминаем факт нажатия правой кнопки
	m_bRightButton = true;

	//====== Воспроизводим реакцию на левую кнопку
	OnLButtonDown(uMsg, wParam, lParam, bHandled);
	return 0;
}


LRESULT COpenGL::OnRButtonUp(UINT /*uMsg*/, WPARAM
		 /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	m_bRightButton = false;
	m_bCaptured = false;
	ReleaseCapture();
	return 0;
}



#pragma once

//=========== COpenGL wrapper class
class COpenGL : public CWnd
{
protected:
	DECLARE_DYNCREATE(COpenGL)
public:

	//==== Метод для добывания CLSID нашего элемента
	CLSID const& GetClsid()
	{
		static CLSID const clsid =
		{
			0x519d9ed8, 0xbc46, 0x4367, { 0x9c, 0xc0, 0x49,
			0x81, 0x40, 0xf3, 0x94, 0x16 }
		};
		return clsid;
	}

	virtual BOOL Create(LPCTSTR lpszClassName,
			LPCTSTR lpszWindowName, DWORD dwStyle,
			const RECT& rect, CWnd* pParentWnd, UINT nID,
			CCreateContext* pContext = NULL)
	{
		return CreateControl(GetClsid(), lpszWindowName,
							dwStyle, rect, pParentWnd, nID);
	}

	BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,
		const RECT& rect, CWnd* pParentWnd, UINT nID,
		CFile* pPersist = NULL, BOOL bStorage = FALSE,
		BSTR bstrLicKey = NULL)
	{
		return CreateControl(GetClsid(), lpszWindowName,
					dwStyle, rect, pParentWnd, nID,
					pPersist, bStorage, bstrLicKey);
	}

//====== Методы, экспонируемые элементом ActiveX
public:
	void SetFillColor(unsigned long newValue);
	unsigned long GetFillColor();
	void GetLightParams(long* pPos);
	void SetLightParam(short lp, long nPos);
	void ReadData();
	void SetFillMode(DWORD mode);
	void GetFillMode(DWORD* pMode);
	void GetQuad(BOOL* bQuad);
	void SetQuad(BOOL bQuad);
};



	CLSID const& GetClsid()
	{
		// Следующая строка взята из файла ATLGL.IDL
		// 519D9ED8-BC46-4367-9CC0-498140F39416

		static CLSID const clsid =
		{
			//======== Эти цифры взяты из файла ATLGL.IDL
			0x519d9ed8, 0xbc46, 0x4367, { 0x9c, 0xc0, 0x49,
			0x81, 0x40, 0xf3, 0x94, 0x16 }
		};
		return clsid;
	}



#include "stdafx.h"
#include "opengl.h"

IMPLEMENT_DYNCREATE(COpenGL, CWnd)

//====== Стандартное свойство реализовано
//====== в виде пары методов Get/Set
void COpenGL::SetFillColor(unsigned long newValue)
{
	static BYTE parms[] =
		VTS_I4;
	InvokeHelper(0xfffffe02, DISPATCH_PROPERTYPUT, VT_EMPTY,
					NULL, parms, newValue);
}

//====== Стандартное свойство
unsigned long COpenGL::GetFillColor()
{
	unsigned long result;
	InvokeHelper(0xfffffe02, DISPATCH_PROPERTYGET, VT_I4,
					(void*)&result, NULL);
	return result;
}

//====== Наши методы сервера
void COpenGL::GetLightParams(long* pPos)
{
	static BYTE parms[] =
		VTS_PI4;
	InvokeHelper(0x1, DISPATCH_METHOD, VT_EMPTY, NULL,
					parms, pPos);
}

void COpenGL::SetLightParam(short lp, long nPos)
{
	static BYTE parms[] =
		VTS_I2 VTS_I4;
	InvokeHelper(0x2, DISPATCH_METHOD, VT_EMPTY, NULL,
					parms, lp, nPos);
}

void COpenGL::ReadData()
{
	InvokeHelper(0x3, DISPATCH_METHOD, VT_EMPTY, 0, 0);
}

void COpenGL::GetFillMode(DWORD* pMode)
{
	static BYTE parms[] =
		VTS_PI4;
	InvokeHelper(0x4, DISPATCH_METHOD, VT_EMPTY, NULL,
					parms, pMode);
}

void COpenGL::SetFillMode(DWORD nMode)
{
	static BYTE parms[] =
		VTS_I4;
	InvokeHelper(0x5, DISPATCH_METHOD, VT_EMPTY, NULL,
					parms, nMode);
}

void COpenGL::GetQuad(BOOL* bQuad)
{
	static BYTE parms[] =
		VTS_PI4;
	InvokeHelper(0x6, DISPATCH_METHOD, VT_EMPTY, NULL,
					parms, bQuad);
}

void COpenGL::SetQuad(BOOL bQuad)
{
	static BYTE parms[] =
		VTS_I4;
	InvokeHelper(0x7, DISPATCH_METHOD, VT_EMPTY, NULL,
					parms, bQuad);
}



Элемент
Идентификатор
Диалог
IDD_TESTGL_DIALOG
Кнопка Data File
IDC_FILENAME
Кнопка Back Color
IDC_BKCLR
Переключатель Quads
IDC_QUADS
Переключатель Strips
IDC_STRIPS
Выпадающий список Fill Mode
IDC_FILL
Ползунок Light (X)
IDC_XPOS
Кнопка Close
IDOK



//===== Декларация диалогового класса (Файл TestGLDlg.h)
#include "opengl.h"

#pragma once

class CTestGLDlg : public CDialog
{
public:
	CTestGLDlg(CWnd* p = NULL);
	enum { IDD = IDD_TESTGL_DIALOG };

	//======= Объект класса-оболочки
	COpenGL	m_Ctrl;
	//======= Запоминаем способ изображения
	BOOL m_bQuads;

	//======= Реакции на регуляторы в окне диалога
	void OnSelchangeFill(void);
	void OnClickedFilename(void);
	afx_msg void OnHScroll(UINT nSBCode, UINT nPos,
									CScrollBar* pScrollBar);
	void OnClickedBkclr(void);
	void OnClickedQuads(void);
	void OnClickedStrips(void);

protected:
	virtual void DoDataExchange(CDataExchange* pDX);
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	DECLARE_MESSAGE_MAP()
};



#include "stdafx.h"
#include "TestGL.h"
#include "TestGLDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//====== Пустое тело конструктора
CTestGLDlg::CTestGLDlg(CWnd* p)
	: CDialog(CTestGLDlg::IDD, p){}

void CTestGLDlg::DoDataExchange(CDataExchange* pDX)
{
	//====== Связывание переменной с элементом
	DDX_Control(pDX, IDC_OPENGL, m_Ctrl);
	CDialog::DoDataExchange(pDX);
}

//====== Здесь мы убрали ON_WM_PAINT и т. д.
BEGIN_MESSAGE_MAP(CTestGLDlg, CDialog)
	ON_WM_SYSCOMMAND()
		//}}AFX_MSG_MAP
	ON_CBN_SELCHANGE(IDC_FILL, OnSelchangeFill)
	ON_BN_CLICKED(IDC_FILENAME, OnClickedFilename)
	ON_WM_HSCROLL()
	ON_BN_CLICKED(IDC_BKCLR, OnClickedBkclr)
	ON_BN_CLICKED(IDC_QUADS, OnClickedQuads)
	ON_BN_CLICKED(IDC_STRIPS, OnClickedStrips)
END_MESSAGE_MAP()

//===== CTestGLDlg message handlers
BOOL CTestGLDlg::OnInitDialog()
{
	//====== Добываем адрес меню управления окном
	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu)
	{
		//====== Добавляем команду About
		pSysMenu->AppendMenu(MF_SEPARATOR);
		pSysMenu->AppendMenu(MF_STRING,
							IDM_ABOUTBOX,"About...");
	}

	//====== Загружаем стандартный значок  
	HICON hMyIcon = ::LoadIcon(0,(char*)IDI_WINLOGO);
	SetIcon(hMyIcon, TRUE);			// Set big icon
	SetIcon(hMyIcon, FALSE);		// Set small icon

	CDialog::OnInitDialog();

	//====== Начальная установка элементов 
	CComboBox *pBox = (CComboBox*)GetDlgItem(IDC_FILL);
	pBox->AddString("Points");
	pBox->AddString("Lines");
	pBox->AddString("Fill");
	pBox->SetCurSel(2);

	//==== Выясняем состояние режима изображения полигонов
	m_Ctrl.GetQuad(&m_bQuads);
		
	WPARAM w = m_bQuads ? BST_CHECKED : BST_UNCHECKED;

	//===== Устанавливаем состояние переключателя
	GetDlgItem(IDC_QUADS)->SendMessage(BM_SETCHECK, w, 0);
	w = m_bQuads ? BST_UNCHECKED : BST_CHECKED;
	GetDlgItem(IDC_STRIPS)->SendMessage(BM_SETCHECK, w, 0);

	return TRUE;
}

void CTestGLDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CDialog(IDD_ABOUTBOX).DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

//====== Выбор из списка типа Combo-box
void CTestGLDlg::OnSelchangeFill(void)
{
	DWORD sel = ((CComboBox*)GetDlgItem(IDC_FILL))
												->GetCurSel();
	sel = sel==0 ? GL_POINT 
		 : sel==1 ? GL_LINE 
					: GL_FILL;
	m_Ctrl.SetFillMode(sel);
}

//==== Нажатие на кнопку запуска файлового диалога
void CTestGLDlg::OnClickedFilename(void)
{
	m_Ctrl.ReadData();
}

//====== Реакция на сдвиг ползунка
void CTestGLDlg::OnHScroll(UINT nSBCode, UINT nPos,
							CScrollBar* pScrollBar)
{
	//====== Выясняем текущую позицию, которая не во
	//====== всех случаях отражена в параметре nPos
	nPos = ((CSliderCtrl*)GetDlgItem(IDC_XPOS))->GetPos();
	m_Ctrl.SetLightParam (0, nPos);
}

//====== Запускаем стандартный диалог
void CTestGLDlg::OnClickedBkclr(void)
{
	DWORD clr = m_Ctrl.GetFillColor();
	CColorDialog dlg(clr);
	dlg.m_cc.Flags |= CC_FULLOPEN;
	if (dlg.DoModal()==IDOK)
	{
		m_Ctrl.SetFillColor(dlg.m_cc.rgbResult);
	}
}

//====== Запоминаем текущее состояние и 
//====== вызываем метод сервера
void CTestGLDlg::OnClickedQuads(void)
{
	m_Ctrl.SetQuad(m_bQuads = TRUE);
}

void CTestGLDlg::OnClickedStrips(void)
{
	m_Ctrl.SetQuad(m_bQuads = FALSE);
}




