Модераторы: Rickert
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Движок на пальцах "От" и "До", Часть #3 
:(
    Опции темы
Rickert
Дата 10.2.2009, 18:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ситхи не пройдут!
****


Профиль
Группа: Комодератор
Сообщений: 3356
Регистрация: 11.7.2006
Где: Лакрима

Репутация: 6
Всего: 52



Цитата
Sic luceat Lux…

После длительного застоя, за который я прошу прощения, мы продолжаем. Заправляйтесь хорошенько, сегодняшний перелёт обещает быть длинным:
  • Напишем класс профилей игроков, для хранения параметров.
  •  Класс системный, для связи окна и OpenGL контекста.
  •  Дадим зелёный свет на рождение солюшена, который будет оборачивать весь наш движок; составим удобную структуру каталогов.
Хочу сразу заметить, что сейчас и далее я буду приводить много «черновых» набросков: т.е. варианты реализации, которые заведомо немаксимально эффективны, но на текущем этапе можно оставить «как есть», а позже будем возвращаться и править. Я призываю всех и каждого давать свои советы и участвовать в обсуждении реализаций, потому что только в споре рождается истина.

Строим кузню для ковки нашего Глемдринга
Сейчас мы создадим рабочее место для нашего движка: чтобы было удобно тестить и дебажить.
Сначала сформируем дерево каталогов. Выберете себе место на диске, где будет заложен наш проект. В этой папке сгенерируйте след. каталоги:
0) inc - тут будут лежать все заголовочные файлы проектов.
1) out - сюда будет бросаться результат всей компиляции.
2) libs - здесь все скомпилированные библиотеки залегать будут.
3) library - тут проекты всех библиотек.
4) prj - здесь проекты, которые будут использовать ту, или иную часть движка.
Что за библиотеки? Мы разобьём весь движок на составные части: класс общего назначения, классы системного типа, классы работы с геометрией, классы работы с миром и объектами. Ну и там ещё по ходу дела будем добавлять другие smile Так вот, каждая из этих частей будет отдельной библиотекой (*.lib), которую можно будет использовать в любом проекте, не прибегая к подключению файлов описания, а используя только заголовочные файлы и либы. Думаю тут вообще нет смысла разжёвывать, читающие и так это должны понимать.

Moving on!

Итак, у вас на руках запущенный Visual Studio (далее VS):
0) File -> New -> Project.
1) Project types: Other project types -> Visual Studio Solutions -> Blank Solution. Имя указываем любое, а местоположение солюшена - наша папка/library.
2) Создаём в солюшене разделы(Add -> New Solution Folder): ProjectsLibrary. В первом будут лежать проекты, которые юзают наш двигл, а во втором сам двигл.
3) Теперь в solution explorer добавляем Add -> New Project...
4) Visual C++ -> Win32 application. Местоположение наша папка/library, имя doCommon.
5) В выпавших предварительных параметрах создаваемого проекта выбираем Static Library и снимаем галку с Precompiled header.
6) Вот у нас девственный проект, который мы сразу же перетаскиваем в раздел Library.
7) Идём в наша папка/library/doCommon и создаём тама папку cpp
Этот проект будет содержать в себе классы общего назначения: aAux, aVector, aString, aList.
Что мы сейчас делаем?
Помните, в первой части я говорил что мы будем держать заголовочные файлы в папке inc, а файлы описаний в cpp? Сейчас возьмите заголовочные файлы перечисленных выше классов и перетащите их в наша папка/inc, а файлы описания в наша папка/library/doCommon/cpp. Вернитесь в VS, и в проекте doCommon создаёте подпапки, каждая из которых будет носить название класса: aAux, aList  и т.п. В каждую из этих папок добавьте файл описания и файл реализации соответствующего класса.
8) Последний шаг - это создание файла, который бы позволял нормально работать с нашей либой. Добавляем новый заголовочный файл doCommon.h, в проект, ложем его в наша папка/inc. Тело у него примерно такое:
Код
//=============================================================================

#ifndef DOCOMMON_H
  #define DOCOMMON_H

//=============================================================================

#include "aAux.h"
#include "aList.h"
#include "aVector.h"
#include "aString.h"

//=============================================================================

#pragma comment(lib, "../../libs/doCommon.lib")

//=============================================================================

#endif

//=============================================================================

Теперь, если мы захотим использовать один из наших классов, нам нужно будет подключить эту библиотеку для проекта.

Дальше маза: оттопырить водолаза!

0) Заходим в свойства пока единственного проекта: Configuration Properties -> Librarian -> General.
1) Значение Output file меняем на ..\..\libs\$(ProjectName).lib. Ах да, не забывайте переключить Configuration с Active(Debug) на All Configurations.
2) Теперь попробуйте скомпилировать...
3) Упс?
4) Или не упс?
5) Если 3 - то всё нормально и вам самостоятельно надо только правильно подправить пути в файлах описаний к файлам заголовочным каждого класса.
6) Если 4 - то вы уже это сделали сразу. Молоток!

Теперь что получается? Вся вторичная хренотень лежит у нас в папке с проектом, а чистая откомпиленая статик либа в наша папка/libs
Самоё время её по-пежить!
Давайте добавим тестовый консольный проект, который будет первым, кто  начнёт юзать начало фундамента нашего "движка".

0) В раздел солюшена Projects добавляем консольный Win32 проект, все настройки при создании как обычно, акромя пути куда его ложить: наша папка/prj
1) В папке проекта заводим также папку cpp, куда добавляем файл описания, где будет лежать код консольного приложения.
2) Тыкаем правой кнопкой на нашем консольном проекте в "solution explorer" и "Set as StartUp Project" - теперь, при запуске нашего солюшена (например, по F5) будет стартовать консольное приложение.
3) В том же "solution explorer" правой кнопкой по опять консольному проекту и "Project Dependencies..." - тут ставим галку на нашем "doCommon". Таким образом мы установили зависимость нашего консольного проекта от проекта "doCommon". Зачем? Затем что это определяет последовательность компиляции и теперь VS знает, что сначала надо будет собрать "doCommon", а уже затем консольный проект. И соответственно, если появятся изменения в "doCommon", то он пересоберёт и консольный проект.
4) Идём свойства консольного проекта Configurations properties -> Linker -> General -> Output File меняем на ..\..\out\$(ProjectName).exe. Да, и не забываем переключить Configuration с Active(Debug) на All Configurations.
Теперь идём в наш консольный проект, в его ед. файл описания и заносим исходный код:
Код
//=============================================================================

#include <stdio.h>
#include "../../../inc/doCommon.h"

//=============================================================================

int main(int argc, char** argv)
{
  aString str("Good morning!\n");

  printf(str.asGetStringChar());
  return 0;
}

//=============================================================================

Аминь!

 Всегда помните!
  •  При добавлении новых проектов настраивать их.
  •  Верно указывать пути.
  •  Верно укладывать файлы.
  •  Не забывайте про зависимости проектов.
  •  Регулируйте стартовый проект, когда работаете над чем-то.
Домашнее задание по этому разделу: как вы, наверное, заметили в наша папка/out, кроме конечного результата(*.exe) было брошено ещё два файла, с расширениями *.ilk и *.pdb: сделайте так, чтобы их не было.
Далее мы будем всё делать на основе этого солюшена и именно таким образом. Разберитесь со всем! Что непонятно - спрашивайте!

Начнём с профилей игроков.
Частенько за одной машиной играет несколько человек и у каждого, конечно, свои настройки управления, графики, звука. Лично меня всегда накаляли игры, где не было возможности хоть как-то быстро сохранять/загружать настройки, а приходилось после брата всё перенастраивать. Поэтому наша задача в данный момент написать класс, который по сути был бы контейнером всех системных параметров/настроек клавиатуры/мыши. В идеале он все данные будет получать из файлов, которые будут лежать в определённой папке, но на данный момент мы создадим некоторый набор минимума, чтобы просто инжектить класс в нашу работу, а уже потом мочь наращивать мясо на его костях.
aProfile.h
Код
//=============================================================================

#ifndef APROFILE_H
  #define APROFILE_H

//=============================================================================

#include "../inc/aString.h"

//=============================================================================

enum keyActions
{
  KA_NONE = -128, // no actions
  KA_CANCEL,
  KA_STRAFERIGHT,
  KA_STRAFELEFT,
  KA_MOVEFORWARD,
  KA_MOVEBACKWARD,
  KA_CROUCH,
  KA_JUMP,
};

//=============================================================================

Это перечисление содержит набор действий, которые будут вешаться на клавиши. Сейчас мы их использовать не будем, но когда прикрутим камеру, то обязательно найдём им применение smile
Код
class aKeys
{
  public:
    aKeys()
    {
      act = NONE;
      key = -1;
    }

  public:
    keyActions act;
    int key;
};

//=============================================================================

Класс клавиши, чтобы хранить какой кнопе какое действие соответствует.
Код
class aProfile
{
  private:
    static const unsigned int keysCount = 128;

  public:
    aProfile();
      keyActions apGetAction(int key)const;
    ~aProfile();

  private:
    aString caption;
    aKeys keys[keysCount];
};

//=============================================================================

#endif

//=============================================================================

Основной класс профиля.
“caption” – ник игрока
Единственную фукнцию опишем далее, в реализации aProfile.cpp:
Код
//=============================================================================

#include <windows.h>
#include "../../../../inc/aProfile.h"

//=============================================================================

aProfile::aProfile()
{
  caption = L"Default";
  
  //keys sets;
  keys[0].key = 0/*найдите код клавиши escape*/;
  keys[0].act = KA_CANCEL;
  keys[1].key = 0/*Так же дайте назначение всем действиям*/;
  keys[1].act = KA_STRAFELEFT;
  keys[2].key = 0/*Так же дайте назначение всем действиям*/;
  keys[2].act = KA_STRAFERIGHT;
  keys[3].key = 0/*Так же дайте назначение всем действиям*/;
  keys[3].act = KA_MOVEFORWARD;
  keys[4].key = 0/*Так же дайте назначение всем действиям*/;
  keys[4].act = KA_MOVEBACKWARD;
  keys[5].key = 0/*Так же дайте назначение всем действиям*/;
  keys[5].act = KA_CROUCH;
  keys[6].key = 0/*Так же дайте назначение всем действиям*/;
  keys[6].act = KA_JUMP;
}

//=============================================================================

Ну тут думаю всё ясно: “key ” параметр – это код клавиши, на которую будет повешено дейсвтие “act”.
Код
keyActions aProfile::apGetAction(int key) const
{
  for (unsigned int i = 0; i < keysCount; ++i)
    if (keys[i].key == key)
      return keys[i].act;

  return KA_NONE;
}

//=============================================================================

Тут тупо проверяем какая клавиша сейчас нажата и возвращаем результат. Чем плох такой метод? Медленный и + если вешать его на событие окна «нажатие клавиши», то тут больше одной клавиши за раз не нажмёшь. Один из тех самых «черновых» моментов.

Код
 aProfile::~aProfile()
{
}

//=============================================================================

Очень сложный код, подумайте над ним…

Системный синглтон для связи окна и OGL контекста
Сейчас набросаем основной системный класс, который завяжет настройки текущего профиля (изначально единственного) и настройки OpenGL + будет ответственный за все основные события: типа отрисовки сцены, обработки надавливания кнопок и прочего…
Задание: Помните как мы создали статик либу doCommon.lib ? Вернитесь, перечитайте и добавьте новый проект библиотеки под названием doSystem – тут у нас будут все классы, так сказать «системного назначения». Добавляем туда след. файл, не забываем делить файлы на папки.
Поехали - aSystem.h
Код
//=============================================================================

#ifndef ASYSTEM_H
  #define ASYSTEM_H

//=============================================================================

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include "aProfile.h"
#include "doCommon.h"

//=============================================================================

#define SYS aSystem::asGetInstance()

//=============================================================================

Подрубаем необходимые библиотеки + пару наших. Заводим опеределение для “SYS”: так проще работать с синглтоном.
Задание:  Вернитесь к описанию класса aAux и заведите такой же define для него.
Код
enum asmRet
{
  SR_WELL = -128,
  SR_NULLED_PATH_TO_PROFILES_FOLDER,
};

//=============================================================================

Если функция должна возвращать что-то, то все значения должны описываться через опред. перечисление.
Код
class aSystem
{
  private:
    aSystem();//нельзя создавать объекты данного типа
    ~aSystem();

  public:
    inline static aSystem& asGetInstance()    {static aSystem sys; return sys;}
    inline void asSetWnd(const HWND wnd)      {if (wnd != NULL) hWnd = wnd;}//устанавливаем окно
    inline const HWND asGetWnd()const         {return hWnd;}//получаем окно
    inline GLsizei asGetWidth()const          {return width;}//получаем длинну окна
    inline GLsizei asGetHeight()const         {return height;}//получаем высоту окна
    inline void asSetDC(const HDC dc)         {if (dc != NULL) hDc = dc;}//устанавливаем контекст
    inline const HDC asGetDC()const           {return hDc;} //получаем контекст окна
    inline void asSetRC(const HGLRC gl)       {if (gl != NULL) glRc = gl;}//ставим контекст OGL
    inline const HGLRC asGetRC()const         {return glRc;}//получаем его же

    bool asOpenGLInit();//инициализация OpenGL
    void asResizeOpenGL(GLsizei w, GLsizei h);//изменение размеров окна
    void asPaintTheWorld();//начинаем отрисовку сцены
    void asSetDisplayToFullscreen();//переходим в полноэкранный режим
    void asPrintLastError()const;//получить послед. Ошибку от винды
    void asKeyDown(WPARAM wp);//обработка нажатых кнопок
    void asGetFPS();//Считать кол-во кадров в секунду
    void asParseParameters(const aString& str);//разбираем строку параметров

  private:
    aProfile useProfile;//Текущий профиль (потом будет список)
    HGLRC glRc;//Контекст отображения OpenGL
    HDC hDc;//Контекст отображения окна
    HWND hWnd;//Хэндл окна
    GLsizei width, height;//Длинна, высота окна
    GLdouble fov;//Угол обзора
    GLdouble aspect;//Отношение длинны к высоте окна
    GLdouble nearClip;//Ближайшая плоскость отсечения
    GLdouble farClip;//Дальняя плоскость отсечения
    GLbitfield clearField;//Отчистка кадра
   //aCamera eyes;//Камера. В будущем 8)
    float framesInterval;//Интервал между кадрами
    float motionSpeed;//Скорость движения в мире. Надо будет для физики
    
    // for debug only
    const float debug_cameraSpeed; //Пока что на будущее для скорости камеры
};

//=============================================================================

#endif

//=============================================================================

Кто не работал с графикой, некоторые переменные не знает. Пока забейте, сейчас будем проводить инициализацию – я всё раскажу подробно.
По идее теперь надо было бы дать описание aSystem.cpp, но на этот раз я сначала опишу другой файл.

Рожаем окно.
0) Итак, пришло время добавить в наш солюшен второй проект, который будет пежить движок (первым был тестовый - консольный). Добавялем в подраздел Projects - win32 Application. В параметрах создания выбираем Empty Project.
1) Теперь сделаем этот проект стартовым.
2) Далее тыкаем на нашем консольном проекте правой кнопкой и выбираем пункт меня Unload Project. Таким образом проект остаётся включёным в слюшен, но никак не участвует в компиляции – не нужен нам лишний баласт.
3) В папке вновь созданного проекта добавляем файл описания. Этот самый файл будет содержать инициализацию окна, основной рабочий цикл. По-сути он будет единственным файлом, и он же будет брать всё из движка, поэтому мы не будем создавать папку cpp, а запишем файл main.cpp прямо в папку с проектом win32 Application, который только что создали.
4) Конечно название может быть любым.
5) Пристегнитесь, взлетаем main.cpp:
Код
 //=============================================================================

#include "../../inc/doSystem.h"

//=============================================================================

Подключили файл библиотеки, которого ещё не создали. Создадим, не торопитесь компилить smile
Код
LRESULT CALLBACK WndProc(HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
{
  GLuint PixelFormat;
  PIXELFORMATDESCRIPTOR pfd =
  { sizeof(PIXELFORMATDESCRIPTOR),
    1,
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    16, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0,
    16, 0, 0,
    PFD_MAIN_PLANE,
    0, 0, 0, 0
  };

Эта функция принимает и обрабатывает все приходящие окну сообщения. Первый параметр функции – хэндл окна, второй – само сообщение, ну и собственно два параметра этого сообщения. Далее идёт заведение переменной PixelFormat – Задание: глянуть в msdn по функции ChosePixelFormat и понять что же это такое.
Затем мы заполняем структуру дескриптора для нашего OpenGL контекста отображения. Так же поройтесь и разберитесь что это такое!
Код
  switch(message)
  {
    case WM_CREATE:
    {
      SYS.asSetDC(GetDC(hWnd));//получаем контекст нашего окна.
      PixelFormat = ChoosePixelFormat(/*какие тут должны быть параметры? Я уже просил вас почитать про эту функцию в msdn.*/);

      if (PixelFormat == NULL)
      {
        MessageBox(0, L"Can't find suitable pixel format.", L"Window creation error", MB_OK | MB_ICONERROR);
        PostQuitMessage(0);//Ошибка – выход.
      }

      if (SetPixelFormat(SYS.asGetDC(), PixelFormat, &pfd) == NULL)
      {
        MessageBox(0, L"Can't set pixel format.", L"Window creation error", MB_OK | MB_ICONERROR);
        PostQuitMessage(0);
      }

      SYS.asSetRC(wglCreateContext(SYS.asGetDC()));//Создаём контекст OGL для вывода на него кадров.

      if (SYS.asGetRC() == NULL)
      {
        MessageBox(0, L"Can't create OpenGL context.", L"Window creation error", MB_OK | MB_ICONERROR);
        PostQuitMessage(0);
      }

      if (wglMakeCurrent(SYS.asGetDC(), SYS.asGetRC()) == NULL)//Делаем наш контекст активным
      {
        MessageBox(0, L"Can't set OpenGL context as current.", L"Window creation error", MB_OK | MB_ICONERROR);
        PostQuitMessage(0);
      }

      if (!SYS.asOpenGLInit())//Инициализируем основные параметры OpenGL
      {
        MessageBox(0, L"Can't correctly init OpenGL.", L"OpenGL init error", MB_OK | MB_ICONERROR);
        PostQuitMessage(0);
      }
        
      break;
    }

Наше первое сообщение. Как ясно из названия: сообщение создания окна.
Код
    case WM_CLOSE:
    {
      ChangeDisplaySettings(NULL, 0);//Если мы меняли настройки экрана, то надо их вернуть в зад.
      wglMakeCurrent(SYS.asGetDC(), SYS.asGetRC());
      wglDeleteContext(SYS.asGetRC());
      ReleaseDC(SYS.asGetWnd(), SYS.asGetDC());
      PostQuitMessage(0);

      break;
    }
    case WM_PAINT://Сообщение об отрисовке окна.
    {
      SYS.asPaintTheWorld();//Рисуем наш мир
      SwapBuffers(SYS.asGetDC());//Выводим кадр на экран

      break;
    }
    case WM_KEYDOWN:
    {
      SYS.asKeyDown(wParam);//Обрабатываем нажатие клавиши

      break;
    }
    case WM_MOUSEMOVE:
    {
      //потом тут будет обработка движения мышкой. Будем вертеть балдой ^_^
      break;
    }
    default:
    {
      return DefWindowProc(hWnd, message, wParam, lParam);//Остальные сообщения обрабатываем default’овым способом.
    }
  }

  return 0;
}

//=============================================================================

Далее идёт самая заглавная функция. Аля int main() в консольных приложениях. Начало конца короче 8)
Код
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR cmdLine, int showCmd)
{
  MSG msg;
  WNDCLASS wc;

  wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = NULL;
  wc.lpszMenuName = NULL;
  wc.lpszClassName = L"Springy";//Создаём класс окна и выставляем ему параметры.

  SYS.asParseParameters(cmdLine);//Передаём строку параметров, на парсинг нашей системе.
  
  if (!RegisterClass(&wc))//Регистрируем класс
  {
    SYS.asPrintLastError();//Если что – выводим ошибку.

    return FALSE;
  }

  SYS.asSetWnd(CreateWindow(wc.lpszClassName, L"Springy jump!", WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS/*WS_OVERLAPPEDWINDOW*/, 0, 0, SYS.asGetWidth(), SYS.asGetHeight(), NULL, NULL, hInstance, NULL));//Создаём окно. Обратите внимание на закомментированные параметры. Потом попробуйте оставить их, а закомментировать ныне стоящие.
  if (SYS.asGetWnd() == NULL)//Если окно родился нормальным
  {
    SYS.asPrintLastError();

    return FALSE;
  }

  ShowWindow(SYS.asGetWnd(), SW_SHOW);//… то будет нам спартанец. Покажем его миру!
  UpdateWindow(SYS.asGetWnd());
  SetFocus(SYS.asGetWnd());

  while(GetMessage(&msg, NULL, 0, 0))//Сидим в конце очереди сообщений и хватаем последнее.
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return TRUE;
}

//=============================================================================

Как вы думаете есть ошибки? Наверное - есть smile Ваша задача – устранить 8)
Вы уже порядочно утомились и хотите увидеть уже хоть что-нибудь, я прав? Вот сейчас мы создадим последний класс описания, который пропсутили и будет вам ваша маленькая вселенная, где вы сможете сотворить всё, aSystem.cpp (незабываем куда его надо ложить!):
Код
//=============================================================================

#include "../../../../inc/aSystem.h"

//=============================================================================

aSystem::aSystem() : debug_cameraSpeed(1.0f)
{
  glRc = 0;
  hDc = 0;
  hWnd = 0;
  width = 800;
  height = 600;
  fov = 45.0;

Угол обзора. Представьте что ваши глаза – это вершина пирамиды, идущая от вас и основание которой лежит параллельно плоскости экрана. Так вот fov – это угол расхождения граней от вершины к основанию, которые характеризуют области видимости камеры.
Код
  aspect = GLdouble(width)/GLdouble(height);
  nearClip = 0.01;
  farClip = 1000.0;

В OpenGL есть две плоскости параллельные вашему экрану, которые ограничивают сцену. За ними ничего нет, есть только то, что между ними. Переменная nearClip – задаёт расстояние между ближней плоскостью отсечения и камерой, а farClip – далней и камерой.
Код
  clearField = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;

После каждого кадра нам нужно отчищать буферы, коих несколько. Тут мы указываем, что сейчас будем отчищать буфер цвета (по сути сбрасывать всё что нарисованно) и буфер глубины.
Код
  framesInterval = 0.0f;
  motionSpeed = 1;
}

//=============================================================================

bool aSystem::asOpenGLInit()
{
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_POINT_SMOOTH);

Инициализация OpenGL. Разрешаем тест глубины, чтобы OpenGL  мог оценить: какой объект ближе, а какой дальше. Включаем возможность использования текстур. И делаем точки сглаженными (будут выглядить как круглешки. Создаёт доп. нагрузку, но зато прикольно. Пусть пока будет: потом отключем 8))
Код
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);

Вся наша геометрия будет выводи ться через массивы отображения. Что это такое и с чем всё это курят – будем разбираться, когда дойдём до вывода моделей (думаю это будет где-то в 5 части).
Код
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);//Устанавливаем цвет, которым будет заливаться каждый кадр.
  glClearDepth(1.0f);//Почитайте
  glDepthFunc(GL_LESS);//любимый
  glShadeModel(GL_SMOOTH);//msdn
  SetCursorPos(width/2, height/2);

  asResizeOpenGL(width, height);

  return 1;
}

//=============================================================================

void aSystem::asResizeOpenGL(GLsizei w, GLsizei h)//если вдруг решили изменить размер окна
{
  if (w <= 0)
    w = 1;

  if (h <= 0)
    h = 1;

  width = w;
  height = h;
  aspect = GLdouble(w)/GLdouble(h);

  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(fov, aspect, nearClip, farClip);//Устанавливаем наши параметры.
  glMatrixMode(GL_MODELVIEW);
}

//=============================================================================

void aSystem::asPaintTheWorld()
{
  glClear(clearField);
  glLoadIdentity();
  gluLookAt(3.0, 5.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

Отчистили экран, сбросили матрицу вида в ноль и устоновили камеру в координаты (3.0, 5.0, 4.0), заставили смотреть в начало координат. Последние три параметра – указывают ось (абсцисс, ординат, аппликат) которая будет направлена вверх. Сейчас мы сказали, что это - ось Y(0.0, 1.0, 0.0).
Код
  glDisable(GL_TEXTURE_2D);//отключаем текстуры, потому что если у нас до этого отображались модели с текстурами (на будущее) – у нас не будет корректно отображены примитивы типа точек, линий и прочего.
  glBegin(GL_LINES);// Начинаем рисовать линии.
    glColor3ub(255, 0, 0);// Красный цвет
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(1.0f, 0.0f, 0.0f);

    glColor3ub(0, 255, 0);// Система цвета - RGB
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 1.0f, 0.0f);

    glColor3ub(0, 0, 255);// Цветовой диапозон:  [0…255]
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 1.0f);
  glEnd();
  glEnable(GL_TEXTURE_2D);

  asGetFPS();//Проведём подсчёт кадров в секунду.
}

//=============================================================================

void aSystem::asSetDisplayToFullscreen()//Переход в полноэкранный режим.
{
  DEVMODE dm;

  memset(&dm, 0, sizeof(DEVMODE));
  dm.dmPelsWidth = width;
  dm.dmPelsHeight = height;
  dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
  dm.dmSize = sizeof(DEVMODE);//Устанавливаем параметры
  ChangeDisplaySettings(&dm, CDS_FULLSCREEN);//Меняем разрешение нашего дисплея на необходимые. По сути: мы не окно растягиваем на весь экран, а экран уменьшаем до размера нашего окна. Если бы оно находилось не в (0, 0), то мы это заметили.
}

//=============================================================================

void aSystem::asPrintLastError()const
{
  LPVOID lpMsgBuf;
  
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL, GetLastError(),
                0, (LPTSTR) &lpMsgBuf,
                0, NULL);
  MessageBox(0, (LPCTSTR)lpMsgBuf, L"Error", MB_OK | MB_ICONERROR);
  LocalFree(lpMsgBuf);
}

//=============================================================================

void aSystem::asKeyDown(WPARAM wp)//Пока что никаких клавиш мы не обрабтываем :)
{
  switch(useProfile.apGetAction(wp))
  {
    default:
    {
      break;
    }
  }
}

//=============================================================================

void aSystem::asGetFPS()
{
  static int fps = 0;
  static float frameTime = 0.0f;
  static float lastTime = 0.0f;
  float currentTime = GetTickCount()/1000.0f;

  framesInterval = currentTime - frameTime;
  if (AUX.aaCompareFloat(frameTime, 0.0f))
    AUX.dt = 0.0f;
  else
    AUX.dt = framesInterval/motionSpeed;

 Подсчёт времени между кадрами. Необходимо для контроля за работой физики, исходя из скорости работы машины. Кстати, мы не заводили переменной dt типа float, у синглтона aAux?

Код
  frameTime = currentTime;
  ++fps;//Собственно само кол-во кадров в секунду на данный момент.
  
  if ((currentTime - lastTime) > 1.0f)
  {
    lastTime = currentTime;
    fps = 0;
  }
}

//=============================================================================

void aSystem::asParseParameters(const aString& str)
{
  aList<aString> list;

  str.asGetWords(list);

  aItem<aString>* tmpItem = list.alGetFirstItem();

  while(tmpItem)
  {
    tmpItem = tmpItem->next;
  }
}

//=============================================================================

У вас вопрос по поводу второй строки последней функции? Вроде у нас нет такого метода aString::asGetWords(aList<aString>& list) ?
Этот метод разбирает строку на слова и записывает их в список, который передан в качестве параметра. Задание: реализуйте этот метод. Подсказка: код пробела – 32.
Код
aSystem::~aSystem()
{
}

//=============================================================================

«Лев два скачка сделал, и сел под пальму отдыхать…»
А лошадь-то всё бежит!
Последних выдох – doSystem.h:
Код
//=============================================================================

#ifndef DOSYSTEM_H
  #define DOSYSTEM_H

//=============================================================================

#include "aSystem.h"
#include "aProfile.h"

//=============================================================================

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "../../libs/doSystem.lib")

//=============================================================================

#endif

//=============================================================================

Наверное вы ещё не сразу запустите приложение, потому что придётся сделать задания, придётся найти ошибки, самим подумать, почитать, разобраться, что-то спросить у меня.
Дорогу осилит идущий!
Вот теперь всем и каждому ещё одно задание – начать читать перевод красной книги OpenGL на progz.ru – все основные функции разбираются до мелочей, с кучей примеров. Ссылку я давал в нулевой части цикла, которая закреплена в топе раздела.
Всё: «И да прибудет с вам великая сила!»


Это сообщение отредактировал(а) Rickert - 10.2.2009, 18:55


--------------------
Ни что не внушает сна крепче, чем день приисполненный трудов!
PM MAIL WWW Skype GTalk   Вверх
Rpahut
Дата 11.2.2009, 00:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


0xdeadbeef
*


Профиль
Группа: Участник
Сообщений: 243
Регистрация: 9.4.2006

Репутация: 5
Всего: 7



Я бы создание окна и инициализацию ogl спрятал в отдельный класс. Вообще-то, именно так я и делаю smile ... В случае с aSystem можно было бы выкинуть все asSet*() и половину asGet*() и разгрузить его немного.
--------------------
C/C++ GameDevRSS Раздела программирования игрOpenGL - уроки от NeHeКак продать идею?
PM MAIL   Вверх
Rickert
Дата 11.2.2009, 02:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ситхи не пройдут!
****


Профиль
Группа: Комодератор
Сообщений: 3356
Регистрация: 11.7.2006
Где: Лакрима

Репутация: 6
Всего: 52



Rpahut, они ведь inline - на производительность не влияют, зато держат интерфейс. Было много дискуссий о том КАК надо писать код. В конечном итоге, когда устриваешься на работу, на твой код смотрят и если он оформлен как-то "не по - правилам" - тебе же минус. Я б рад сделать многие параметры открытыми и избавиться от тучи функций.

Это сообщение отредактировал(а) Rickert - 11.2.2009, 02:27


--------------------
Ни что не внушает сна крепче, чем день приисполненный трудов!
PM MAIL WWW Skype GTalk   Вверх
Rpahut
Дата 11.2.2009, 11:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


0xdeadbeef
*


Профиль
Группа: Участник
Сообщений: 243
Регистрация: 9.4.2006

Репутация: 5
Всего: 7



Я имел ввиду - если создание окна поместить внутри aSystem, или что еще лучше, в aOGLWindow. asSetWnd() например будет в таком случае не нужен, asGetWnd() мало полезен.
Если движок будет расти, все равно то, что сейчас лежит в WinMain(), придется куда-то заворачивать.
--------------------
C/C++ GameDevRSS Раздела программирования игрOpenGL - уроки от NeHeКак продать идею?
PM MAIL   Вверх
Rickert
Дата 11.2.2009, 11:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ситхи не пройдут!
****


Профиль
Группа: Комодератор
Сообщений: 3356
Регистрация: 11.7.2006
Где: Лакрима

Репутация: 6
Всего: 52



А, теперь понял твою мысль.
Цитата(Rpahut @  11.2.2009,  11:04 Найти цитируемый пост)
Если движок будет расти, все равно то, что сейчас лежит в WinMain(), придется куда-то заворачивать.

Возможно, увидим smile 


--------------------
Ни что не внушает сна крепче, чем день приисполненный трудов!
PM MAIL WWW Skype GTalk   Вверх
arilou
Дата 12.2.2009, 18:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Великий МунаБудвин
****


Профиль
Группа: Экс. модератор
Сообщений: 2646
Регистрация: 15.7.2004
Где: город-герой Минск

Репутация: 6
Всего: 61



Rpahut, +1. 


Rickert, следи, чтобы классы не были перегружены функционалом  smile 


--------------------
user posted imageuser posted image
PM WWW ICQ   Вверх
Rickert
Дата 13.2.2009, 03:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ситхи не пройдут!
****


Профиль
Группа: Комодератор
Сообщений: 3356
Регистрация: 11.7.2006
Где: Лакрима

Репутация: 6
Всего: 52



Да, стараюсь. Будем потом модернизировать. Вот, первые предложения пошли smile 


--------------------
Ни что не внушает сна крепче, чем день приисполненный трудов!
PM MAIL WWW Skype GTalk   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Программирование игр, графики и искуственного интеллекта"
Rickert

НА ЗЛОБУ ДНЯ: Дорогие посетители, прошу обратить внимание что новые темы касающиеся новых вопросов создаются кнопкой "Новая тема" а не "Ответить"! Любые оффтопиковые вопросы, заданные в текущих тематических темах будут удалены а их авторы, при рецедиве, забанены.

  • Литературу, связанную с программированием графики, обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы связанные с программированием графики и мультимедии на языках С++ и Delphi
  • Вопросы по реализации алгоритмов рассматриваются здесь

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Rickert.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Программирование игр, графики и искусственного интеллекта | Следующая тема »


 




[ Время генерации скрипта: 0.1039 ]   [ Использовано запросов: 20 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.