- •Введение
- •1. Исходные данные на проектирование
- •2. Выбор решения
- •2.1. Определение модулей программы
- •2.2. Создание и редактирование модели заданного объекта средствами Blender
- •3. Библиотека OpenGl
- •Init() – установка начального освещения, позиции камеры, загрузки модели
- •4. Схема программы
- •4.1. Схема работы приложения
- •Инициализация программы void Init() Опрос клавиатуры и мыши Пользователь хочет закрыть окно? Конец Вывод кадра void Main()
- •5.2. Функциональная структура программы
- •Заключение
- •Литература
- •Приложения Приложение 1. Листинг программы
- •Приложение 2. Скриншоты работающей программы
Литература
OpenGL. Руководство по программированию: М. Ву, Т. Девис, Дж. Нейдер, Д. Шрайнер — Москва, Питер, 2010 г.- 624 с.
Графика трехмерной компьютерной игры на основе OpenGL: А. В. Боресков — Санкт-Петербург, Диалог-МИФИ, 2009 г.- 384 с.
Инструментальные средства программирования и отладки шейдеров в DirectX и OpenGL (+ CD-ROM): Станислав Горнаков — Санкт-Петербург, БХВ-Петербург, 2011 г.- 256 с.
Компьютерная графика. Полигональные модели: Е. В. Шикин, А. В. Боресков — Санкт-Петербург, Диалог-МИФИ, 2009 г.- 464 с.
Моделирование объектов с дискретно-распределенными параметрами: А. В. Седов — Санкт-Петербург, Наука, 2010 г.- 440 с.
Разработка и отладка шейдеров (+ CD-ROM): Алексей Боресков — Москва, БХВ-Петербург, 2012 г.- 488 с.
Расширения OpenGL (+ CD-ROM): Алексей Боресков — Санкт-Петербург, БХВ-Петербург, 2009 г.- 688 с.
Свободное программное обеспечение. FREE PASCAL для студентов и школьников (+ CD): Ю. Л. Кетков, А. Ю. Кетков — Санкт-Петербург, БХВ-Петербург, 2011 г.- 376 с.
Эффективное использование С++: Билл Вагнер — Санкт-Петербург, Лори, 2011 г.- 256 с.
Приложения Приложение 1. Листинг программы
// main.cpp
const char * szTitle = "Obj";
#include <gl/gl.h>
#include "../basecode/app.h"
#include "obj.h"
//Переменные
//Работа с мышью
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL (WM_MOUSELAST+1) // Сообщение, которое связано с колесом мыши
#endif
//Кнопки мыши
#define K_MWHEELDOWN 239
#define K_MWHEELUP 240
#define K_MOUSE1 200
#define K_MOUSE2 201
#define K_MOUSE3 202
//Номер сообщения мыши
static UINT MSH_MOUSEWHEEL;
//Управление обзором через мышь
int mouse_buttons;
int mouse_oldbuttonstate;
POINT current_pos;
int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum;
int old_x, old_y;
int mouseactive; // 0 - когда наше окно не имеет фокуса ввода
//Состояние мыши (для восстановления)
int restore_spi;
int mouseinitialized;
int originalmouseparms[3], newmouseparms[3] = {0, 0, 1};
int mouseparmsvalid;
//Размеры окна и указатель окна HWND
int window_center_x, window_center_y;
//Кнопка паузы (0-нет паузы,1-пауза)
int pause = 0;
RECT window_rect;
HWND cl_hwnd;
//---------------------------------------------
//Работа с клавиатурой
//Буфер нажатий кнопок (0-не нажата кнопка, 1-нажата)
char cmd[1024];
//---------------------------------------------
//Работа с позицией камеры
//Углы поворота
float angles[3];
//Позиция камеры
float org[3];
//Скорость камеры
float orgspeed[3];
//Ускорение камеры
float orgvel[3];
//Пузырьки
struct _bubble {
//Координаты
float x,y,z;
//Скорость
float vx,vy,vz;
} bubble[100];
//Координаты ограничения пузырьков
float bubblea,bubbleb;
float bubblec,bubbled;
//Работа с файлами формата *.obj
//Объекты для рендера
CObj g_Obj1,g_Obj2,g_Obj3,g_Obj4,g_Obj5;
//Текстура
CImage g_Skin;
IN_MouseMove - функция обработки движения мыши
void IN_MouseMove ()
{
int mx, my;
//Фильтрация мыши (1-включена, 0-выключена)
int m_filter = 0;
//Чувствительность мыши (1-12)
float sensitivity = 2;
//Чувствительность поворота мыши
float m_yaw=0.22,m_pitch=0.22;
if (!mouseactive)
return;
// find mouse movement
if (!GetCursorPos (¤t_pos))
return;
mx = current_pos.x - window_center_x;
my = current_pos.y - window_center_y;
#if 0
if (!mx && !my)
return;
#endif
if (m_filter==1)
{
mouse_x = (mx + old_mouse_x) * 0.5;
mouse_y = (my + old_mouse_y) * 0.5;
}
else
{
mouse_x = mx;
mouse_y = my;
}
old_mouse_x = mx;
old_mouse_y = my;
mouse_x *= sensitivity;
mouse_y *= sensitivity;
//Взгляд влево-вправо
angles[1]+=m_yaw*mouse_x;
//Ограничиваем взгляд
if(angles[1]<0)
angles[1]+=360.0;
if(angles[1]>360.0)
angles[1]-=360.0;
//Взгляд вверх-вниз
angles[0]+= m_pitch*mouse_y;
//Ограничиваем взгляд вверх-вниз
if(angles[0]<-90.0)
angles[0]=-90.0;
if(angles[0]>90.0)
angles[0]=90.0;
// Ставим курсор мыши в центр (чтобы было место для движения
if (mx || my)
SetCursorPos (window_center_x, window_center_y);
}
Key_Event
Вызывается каждый кадр для события нажатия и отпускания кнопок
void Key_Event (int key, int key1, int down, unsigned time)
{
char *kb;
static int first=1;
int i;
if(first==1){
first=0;
}
// сохраняем статус нажатия кнопки в специальном буфере
if (down) {
//Кнопка нажата
cmd[key1&0x3ff]=1;
} else {
//Кнопка отпущена
cmd[key1&0x3ff]=0;
}
}
IN_MouseEvent - нажатие кнопок мыши
void IN_MouseEvent (int mstate)
{
int i;
if (!mouseinitialized)
return;
// Обработаем кнопки мыши
for (i=0 ; i<mouse_buttons ; i++)
{
if ( (mstate & (1<<i)) &&
!(mouse_oldbuttonstate & (1<<i)) )
{
Key_Event (K_MOUSE1 + i, 0, true, APP->sys_msg_time);
}
if ( !(mstate & (1<<i)) &&
(mouse_oldbuttonstate & (1<<i)) )
{
Key_Event (K_MOUSE1 + i,0, false, APP->sys_msg_time);
}
}
mouse_oldbuttonstate = mstate;
}
//key sensitivity - чувствительность клавиатуры (чем больше тем быстрее
//будем двигаться при нажатии клавиш
float keysens = 0.2;
//Функции для перемещения камеры (влево-вправо-вперед-назад)
void forward(){
org[2]+=cos((float)angles[1]*3.1415926/180.0)*keysens;
org[0]-=sin((float)angles[1]*3.1415926/180.0)*keysens;
}
void backword(){
org[2]-=cos((float)angles[1]*3.1415926/180.0)*keysens;
org[0]+=sin((float)angles[1]*3.1415926/180.0)*keysens;
}
void left(){
org[2]+=sin((float)angles[1]*3.1415926/180.0)*keysens;
org[0]+=cos((float)angles[1]*3.1415926/180.0)*keysens;
}
void right(){
org[2]-=sin((float)angles[1]*3.1415926/180.0)*keysens;
org[0]-=cos((float)angles[1]*3.1415926/180.0)*keysens;
}
IN_ActivateMouse
Вызывается когда окно получает фокус ввода
void IN_ActivateMouse(void)
{
int width, height;
if (!mouseinitialized)
return;
if (mouseactive)
return;
mouseactive = true;
// if (mouseparmsvalid)
restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0);
width = GetSystemMetrics (SM_CXSCREEN);
height = GetSystemMetrics (SM_CYSCREEN);
GetWindowRect ( cl_hwnd, &window_rect);
if (window_rect.left < 0)
window_rect.left = 0;
if (window_rect.top < 0)
window_rect.top = 0;
if (window_rect.right >= width)
window_rect.right = width-1;
if (window_rect.bottom >= height-1)
window_rect.bottom = height-1;
window_center_x = (window_rect.right + window_rect.left)/2;
window_center_y = (window_rect.top + window_rect.bottom)/2;
SetCursorPos (window_center_x, window_center_y);
old_x = window_center_x;
old_y = window_center_y;
SetCapture ( cl_hwnd );
ClipCursor (&window_rect);
while (ShowCursor (FALSE) >= 0)
;
}
IN_DeactivateMouse
Вызывается когда окно теряет фокус ввода
void IN_DeactivateMouse (void)
{
if (!mouseinitialized)
return;
if (!mouseactive)
return;
if (restore_spi)
SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
mouseactive = false;
ClipCursor (NULL);
ReleaseCapture ();
while (ShowCursor (TRUE) < 0)
;
}
//Цвет ламп
float col1[4] = {0.5,0,0,1};
float col2[4] = {0.5,0.5,0.8,1};
float col3[4] = {0.0,0.3,0.8,1};
//- Main
//- Main game loop - Главный цикл программы (отрисовывает 1 кадр)
void Main()
{
//Переменная цикла
int i;
//Угол поворота картинки
static float rot = 0;
//Скорость движения крышки вверх или вниз
static float hatpos = 0.0;
static float hspeed = 0.01f;
static float hatposspeed = hspeed;
//Скорость вращения крышки
static float hatrot = 0;
static float hatrotspeed = 2.0;
//Временная строка
char szDD[2048],temp[2048];
IN_ActivateMouse();
IN_MouseMove();
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLightfv(GL_LIGHT0, GL_DIFFUSE, col1);
glLightfv(GL_LIGHT1, GL_DIFFUSE, col2);
glLightfv(GL_LIGHT2, GL_DIFFUSE, col3);
if(pause == 0){
if(col1[0]<1.0)
col1[0]=col1[0]+0.015;
else
col1[0]=0;
if(col2[1]<1.0)
col2[1]=col2[1]+0.001;
else
col2[1]=0;
if(col3[2]<1.0)
col3[2]=col3[2]+0.02;
else
col3[2]=0;
}
glShadeModel(GL_SMOOTH);
glColor4f(1, 1, 1, 1);
// glColor3f(1, 1, 1);
APP->Print(.75f, .9f, "FPS: %4.2f", APP->GetFPS());
//szDD[0]=0;
//for(i=0;i<1024;i++){
// if(cmd[i]!=0){
// sprintf(temp,"%d,",i);
// strcat(szDD,temp);
// }
//}
// APP->Print(.65f, .9f, "Key: %s", szDD);
// sprintf(szDD,"%f;%f;%f:%f;%f;%f",angles[0],angles[1],angles[2],org[0],org[1],org[2]);
// APP->Print(.015f, .2f, "Angles: %s", szDD);
// glMatrixMode(GL_PROJECTION);
// glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// glPolygonMode(GL_FRONT_AND_BACK, GL_SOLID);
// glMatrixMode(GL_PROJECTION);
// glLoadIdentity();
glRotatef(angles[0], 1, 0, 0);
glRotatef(angles[1], 0, 1, 0);
glRotatef(angles[2], 0, 0, 1);
glTranslatef(org[0], org[1], org[2]); //Translation for the gear model (gear.obj) may have to mess with this
// glRotatef(90*3, 0, 0, 1);
// glCullFace(GL_FRONT);
// glMatrixMode(GL_MODELVIEW);
// glLoadIdentity ();
glRotatef(rot, 0, 1, 0);
// glTranslatef(0.f, -1.f, -4.f); //Translation for the gear model (gear.obj) may have to mess with this
//to get other models to display correctly
//Устанавливаем цвет
glColor4f(1, 1, 1, 1);
//Режим текстурирования
//Режим текстуры
glEnable(GL_TEXTURE_2D);
//1) Выводим комнату
g_Obj4.Render();
//2) Выводим воду
g_Obj2.Render();
//3) Выводим пузырьки
if(hatpos>0.2){
g_Obj5.PartBind();
for(i=0;i<100;i++){
glPushMatrix();
if(pause == 0){
bubble[i].x+=bubble[i].vx;
bubble[i].y+=bubble[i].vy;
bubble[i].z+=bubble[i].vz;
if(bubble[i].y<bubblea){
bubble[i].vy=0.01+0.02*(float)rand()/RAND_MAX;
//Меняем положение
bubble[i].x=1.3*(float)rand()/RAND_MAX-0.75;
bubble[i].y=bubblea+0.01*(float)rand()/RAND_MAX;
bubble[i].z=1.3*(float)rand()/RAND_MAX-1.45;
}
if(bubble[i].y>bubbleb)
bubble[i].vy=-0.01-0.02*(float)rand()/RAND_MAX;
}
glTranslatef(bubble[i].x, bubble[i].y, bubble[i].z);
g_Obj5.RenderPart();
glPopMatrix();
}
}
//Выводим бутылку
g_Obj1.Render();
//Поднять крышку
if(pause == 0){
if(hatposspeed>0){
if(hatpos<3)
hatpos+=hatposspeed;
hatrot+=hatrotspeed;
} else if(hatposspeed<0){
if(hatpos>0)
hatpos+=hatposspeed;
hatrot-=hatrotspeed;
} else {
//Пауза - ничего не делаем!
}
if(hatpos<=0)
hatposspeed=0.005f;
if(hatpos>=3)
hatposspeed=-0.005f;
}
glLoadIdentity();
// glTranslatef(0, hatpos, 0);
glRotatef(angles[0], 1, 0, 0);
glRotatef(angles[1], 0, 1, 0);
glRotatef(angles[2], 0, 0, 1);
glTranslatef(org[0], org[1]+hatpos, org[2]);
glRotatef(rot+hatrot, 0, 1, 0);
g_Obj3.Render();
//Обработка клавиш
if(cmd[65]!=0){
//a-Влево
left();
// orgvel[0]=0.02;
// org[0]+=orgvel[0];
}
if(cmd[68]!=0){
//d-Вправо
right();
// orgvel[0]=0.02;
// org[0]-=orgvel[0];
}
if(cmd[90]!=0){
//z-Вверх
orgvel[1]=0.02;
org[1]+=orgvel[1];
}
if(cmd[88]!=0){
//x-Вниз
orgvel[1]=0.02;
org[1]-=orgvel[1];
}
if(cmd[87]!=0){
//w-Вперед
forward();
// orgvel[2]=0.02;
// org[2]+=orgvel[0];
}
if(cmd[83]!=0){
//s-Назад
backword();
// orgvel[2]=0.02;
// org[2]-=orgvel[0];
}
if(cmd[13]!=0){
//Enter-Старт
}
if(cmd[32]!=0){
//Пробел-Пауза
cmd[32]=0;
pause = 1 - pause;
}
if(pause == 0){
rot += 0.2f;
}
}
//- Init
//- Initialization code - Начальная инициализация ламп, мешей
void Init()
{
int i;
//1
static float LightAmbient1 [] = { 0.2f, 0.2f, 0.2f, 1.0f };
static float LightDiffuse1 [] = { 0.5f, 0.5f, 0.0f, 1.0f };
static float LightPosition1 [] = { 0, 30, 30, 1};
//2
static float LightAmbient2 [] = { 0.2f, 0.2f, 0.2f, 1.0f };
static float LightDiffuse2 [] = { 0.0f, 0.0f, 0.5f, 1.0f };
static float LightPosition2 [] = { 10, -30, -30, 1};
//3
static float LightAmbient3 [] = { 0.2f, 0.2f, 0.2f, 1.0f };
static float LightDiffuse3 [] = { 0.3f, 0.2f, 0.0f, 1.0f };
static float LightPosition3 [] = { -10, 30, 30, 1};
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient1);
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse1);
glLightfv(GL_LIGHT1, GL_POSITION, LightPosition1);
glLightfv(GL_LIGHT2, GL_AMBIENT, LightAmbient2);
glLightfv(GL_LIGHT2, GL_DIFFUSE, LightDiffuse2);
glLightfv(GL_LIGHT2, GL_POSITION, LightPosition2);
glLightfv(GL_LIGHT3, GL_AMBIENT, LightAmbient3);
glLightfv(GL_LIGHT3, GL_DIFFUSE, LightDiffuse3);
glLightfv(GL_LIGHT3, GL_POSITION, LightPosition3);
glEnable(GL_LIGHT1);
glEnable(GL_LIGHT2);
glEnable(GL_LIGHT3);
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glPolygonMode( GL_FRONT_AND_BACK,GL_FILL);
//glPolygonMode( GL_FRONT_AND_BACK,GL_LINE);
//Бутылка
g_Obj1.Load("bottle006.obj","00bottle006.tga");
//Вода в бутылке
g_Obj2.Load("water006.obj","00water006.tga");
//Крышка
g_Obj3.Load("hat006.obj","00hat006.tga");
//Комната
g_Obj4.Load("box006.obj","skin001.jpg");
//Пузырек
g_Obj5.Load("bubble006.obj","00Bubble006.tga");
//Устанавливаем начальную позицию камеры
angles[0]=0;
angles[1]=0;
angles[2]=0;
org[0]=0.f;
org[1]=-4.f;
org[2]=-12.f;
orgspeed[0]=0;
orgspeed[1]=0;
orgspeed[2]=0;
orgvel[0]=0;
orgvel[1]=0;
orgvel[2]=0;
//Задаем начальную позицию пузырьков
bubblea = -1.95;
bubbleb = bubblea+0.2;
bubblec = -3.0;
bubbled = 3.0;
for(i=0;i<100;i++){
bubble[i].x=1.3*(float)rand()/RAND_MAX-0.75;
bubble[i].y=bubblea+0.2*(float)rand()/RAND_MAX;
bubble[i].z=1.3*(float)rand()/RAND_MAX-1.45;
bubble[i].vx=0.0;
bubble[i].vy=0.01;
bubble[i].vz=0.0;
}
//Включаем освещение
glEnable(GL_LIGHTING);
//Прозрачный рендеринг
glEnable(GL_BLEND);
//Режим текстуры
glEnable(GL_TEXTURE_2D);
}
//- WinMain
//- Entry point for the application - Точка входа в программу (main или WinMain)
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
int i;
//Сбрасываем установки мыши
mouseinitialized = true;
mouse_buttons = 3;
//Очищаем буфер клавиатуры
for(i=0;i<1024;i++)
cmd[i]=0;
APP->Init(Main);
APP->CreateWnd();
Init();
APP->CreateFnt("Arial", 16);
APP->Run();
APP->Destroy();
return 0;
}
//- WndProc
//- Windows Procedure - главная процедура обработки сообщений окна
LRESULT CALLBACK CWindow::WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if ( uiMsg == MSH_MOUSEWHEEL )
{
if ( ( ( int ) wParam ) > 0 )
{
Key_Event( K_MWHEELUP, 0, true, APP->sys_msg_time );
Key_Event( K_MWHEELUP, 0, false, APP->sys_msg_time );
}
else
{
Key_Event( K_MWHEELDOWN, 0, true, APP->sys_msg_time );
Key_Event( K_MWHEELDOWN, 0, false, APP->sys_msg_time );
}
return DefWindowProc (hWnd, uiMsg, wParam, lParam);
}
switch(uiMsg)
{
case WM_MOUSEWHEEL:
if ( ( short ) HIWORD( wParam ) > 0 )
{
Key_Event( K_MWHEELUP, 0, true, APP->sys_msg_time );
Key_Event( K_MWHEELUP, 0, false, APP->sys_msg_time );
}
else
{
Key_Event( K_MWHEELDOWN, 0, true, APP->sys_msg_time );
Key_Event( K_MWHEELDOWN, 0, false, APP->sys_msg_time );
}
break;
case WM_CREATE:
{
cl_hwnd=hWnd;
SetWindowText(hWnd, szTitle);
MSH_MOUSEWHEEL = RegisterWindowMessage("MSWHEEL_ROLLMSG");
break;
}
case WM_SYSCOMMAND:
{
switch(wParam)
{
//Отключаем скринсейверы
case SC_SCREENSAVE:
case SC_MONITORPOWER:
return 0;
}
break;
}
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
case WM_SIZE:
{
Resize(LOWORD(lParam), HIWORD(lParam));
return 0;
}
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
{
int temp;
temp = 0;
if (wParam & MK_LBUTTON)
temp |= 1;
if (wParam & MK_RBUTTON)
temp |= 2;
if (wParam & MK_MBUTTON)
temp |= 4;
IN_MouseEvent (temp);
}
break;
case WM_SYSKEYDOWN:
if ( wParam == 13 )
{
//Enter
return 0;
}
// Падаем дальше
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
{
PostQuitMessage(0);
break;
}
}
Key_Event( lParam, wParam, true, APP->sys_msg_time);
break;
case WM_SYSKEYUP:
case WM_KEYUP:
Key_Event( lParam, wParam, false, APP->sys_msg_time);
break;
}
return (DefWindowProc(hWnd, uiMsg, wParam, lParam));
}
