Создание базового проекта с использованием Ogre3D

После того, как мы установили среду разработки и SDK Ogre3D, самое время приступать к созданию базового приложения.
Данное приложение будет выполнять только необходимые процедуры инициализации и ничего кроме этого делать не будет. Оно будет являться скелетом программы, в который мы будем добавлять остальные блоки игры.

Содержание:

  1. Создание проекта
  2. Добавление файлов
  3. Пояснение кода
  4. Компиляция проекта


Итак запускаем Microsoft Visual C++ и выбираем создание нового проекта:
Меню -> Файл -> Создать -> Проект
В следующем окне заполняем необходимые данные: каталог, в котором будет находиться проект, его название и тип шаблона Win32.

tutorial01_1.jpg

Затем нажимаем ОК и переходим к следующему окну, в котором указываем тип приложения и устанавливаем галочку “Пустой проект”, для того, что бы никаких файлов автоматически система не создавала.

tutorial01_2.jpg

Затем нажимаем кнопку готово и получаем пустой проект, в который можно начинать добавлять файлы :)


Добавление файлов в проект


Для этого выбираем пункт меню:
Меню -> Проект -> Добавить новый элемент
В следующем окне выбираем тип файла: заголовочный файл (.h) и его название. Я назвал Application.h

tutorial01_3.jpg

Ниже я привожу полностью содержимое файла Application.h, а затем остановлюсь подробнее на основных пунктах.

#ifndef __APPLICATION_H__
#define __APPLICATION_H__
	
#include <OgreString.h>
#include <OgreRoot.h>
#include <OgreFrameListener.h>
	
#include <OIS/OISEvents.h>
#include <OIS/OISInputManager.h>
#include <OIS/OISKeyboard.h>
#include <OIS/OISMouse.h>
	
//---------------------------------------------------------
// Основное приложение
//---------------------------------------------------------
class Application: public Ogre::FrameListener,
	OIS::KeyListener, OIS::MouseListener
{
   static Application*    _instance;
   Ogre::Root*            _ogre;
   Ogre::RenderWindow*    _renderWindow;
   Ogre::SceneManager*    _sceneManager;
   Ogre::Camera*          _camera;
   Ogre::Viewport*        _viewport;
   OIS::InputManager*     _inputManager;
   OIS::Keyboard*         _keyboard;
   OIS::Mouse*            _mouse;
	
   bool                   _shutdown;
	
public:
   Application();
   ~Application();
   static Application& getInstance()
   { return *_instance; }
	
   Ogre::Root* GetOgre()
   { return _ogre; }
   Ogre::SceneManager* GetSceneManager()
   { return _sceneManager; }
   Ogre::Camera* GetCamera()
   { return _camera; }
   Ogre::RenderWindow* GetRenderWindow()
   { return _renderWindow; }
   
   void Start();
   void Shutdown()
   { _shutdown = true; }
   
   // События
   bool frameStarted(const Ogre::FrameEvent& evt);
   bool frameEnded(const Ogre::FrameEvent& evt);
   
   bool keyPressed(const OIS::KeyEvent &keyEventRef);
   bool keyReleased(const OIS::KeyEvent &keyEventRef);
   
   bool mouseMoved(const OIS::MouseEvent &evt);
   bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id);
   bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id);
};
#endif

Как видно из комментариев, мы определяем базовый класс нашего приложения. Он наследует свойства нескольких классов (Ogre::FrameListener, OIS::KeyListener, OIS::MouseListener) для того чтобы получать сообщения от мышки, клавиатуры, а также события связанные с формированием изображения. Для их обработки мы определили соответствующие функции.
Дальше добавляем в проект файл Application.cpp, его содержимое я привожу ниже:

#include "Application.h"
	
#include <OgreRoot.h>
#include <OgreRenderSystem.h> 
#include <OgreRenderWindow.h>
#include <OgreWindowEventUtilities.h>
#include <OgreSceneManager.h>
#include <OgreViewport.h>
#include <OgreCamera.h>
	
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
	
Application* Application::_instance = 0;
	
//---------------------------------------------------------
// Вход в программу
//---------------------------------------------------------
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
int main(int argc, char **argv)
#endif
{
   try
    {
      Application app;
      app.Start();
   }
   catch(std::exception& e)
    {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        MessageBoxA(NULL, e.what(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
        fprintf(stderr, "An exception has occurred: %sn", e.getFullDescription().c_str());
#endif
    }
	
    return 0;
}
	
//---------------------------------------------------------
// Конструктор
//---------------------------------------------------------
Application::Application()
{
   _ogre = 0;
   _renderWindow = 0;
   _inputManager = 0;
   _mouse = 0;
   _keyboard = 0;
   _instance = this;
}
	
//---------------------------------------------------------
// Деструктор
//---------------------------------------------------------
Application::~Application()
{
   delete _keyboard;
   delete _mouse;
   OIS::InputManager::destroyInputSystem(_inputManager);
   delete _ogre;
   _instance = 0;
}
	
//---------------------------------------------------------
// Старт программы
//---------------------------------------------------------
void Application::Start()
{
   // Инициализация Ogre
   _ogre = new Ogre::Root("", "", "Game.log");
   _ogre->addFrameListener(this);
	
   // Загрузка плагинов
#if defined(_DEBUG)
   _ogre->loadPlugin("RenderSystem_Direct3D9_d");
#else
   _ogre->loadPlugin("RenderSystem_Direct3D9");
#endif
	
   // Выбор системы рендеринга
   Ogre::RenderSystemList* renderSystems = _ogre->getAvailableRenderers();
   Ogre::RenderSystem* renderSystem = 0;
    for(Ogre::RenderSystemList::iterator it = renderSystems->begin(); it != renderSystems->end(); ++it) {
      if((*it)->getName().find("Direct3D") != Ogre::String::npos)  {
         renderSystem = *it;
         break;
      }
    }    
	
   if(renderSystem == 0) exit(1);
    _ogre->setRenderSystem(renderSystem);
 
   // Конфигурация рендеринга
   renderSystem->setConfigOption("Full Screen", "No");  
   renderSystem->setConfigOption("Video Mode", "800 x 600 @ 32-bit colour");
	
   // Создание окна рендеринга
   _renderWindow = _ogre->initialise(true, "Game"); 
	
   // Создание менеджера сцены
   _sceneManager = _ogre->createSceneManager(Ogre::ST_GENERIC, "SceneManager");
	
   // Создание сферы
   Ogre::Entity* sphere = _sceneManager->createEntity("Sphere", Ogre::SceneManager::PT_SPHERE);
   Ogre::SceneNode* node = _sceneManager->getRootSceneNode()->createChildSceneNode("SphereNode");
   node->attachObject((Ogre::MovableObject*)sphere);
	
   // Создание камеры и окна видимости
   _camera = _sceneManager->createCamera("Camera");
    _camera->setPosition(-400, 300, -400);
    _camera->lookAt(0,0,0);  
   _camera->setNearClipDistance(1);
    _viewport = _renderWindow->addViewport(_camera);
   _camera->setAspectRatio(Ogre::Real(_viewport->getActualWidth()) / 
      Ogre::Real(_viewport->getActualHeight()));
	
   // Создание и настройка менеджера ввода
   unsigned long hWnd = 0;
   OIS::ParamList paramList;
   _renderWindow->getCustomAttribute("WINDOW", &hWnd);
   paramList.insert(OIS::ParamList::value_type("WINDOW", Ogre::StringConverter::toString(hWnd)));
	
   _inputManager = OIS::InputManager::createInputSystem(paramList);
   _keyboard = static_cast<OIS::Keyboard*>(_inputManager->createInputObject(OIS::OISKeyboard, true));
   _mouse = static_cast<OIS::Mouse*>(_inputManager->createInputObject(OIS::OISMouse, true));
    
   _mouse->getMouseState().height	= _renderWindow->getHeight();
   _mouse->getMouseState().width	= _renderWindow->getWidth();
	
   _keyboard->setEventCallback(this);
   _mouse->setEventCallback(this);
	
   // Основной цикл
   _shutdown = false;
   
   while(!_shutdown) 
   {
      if(_renderWindow->isClosed()) _shutdown = true;
	
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
      Ogre::WindowEventUtilities::messagePump();
#endif
      if(_renderWindow->isActive())
      {
         // Захват ввода
         _keyboard->capture();
         _mouse->capture();
	
         // Рендеринг
         _ogre->renderOneFrame();
      }
      else
      {
         Sleep(1000);
      }
   }
}
	
//---------------------------------------------------------
// Обработка нажатия клавиши
//---------------------------------------------------------
bool Application::keyPressed(const OIS::KeyEvent &evt)
{
   if(evt.key == OIS::KC_ESCAPE) {
      Shutdown();
   }
	
   return true;
}
	
//---------------------------------------------------------
// Обработка отпускания клавиши
//---------------------------------------------------------
bool Application::keyReleased(const OIS::KeyEvent &evt)
{
   return true;
}
	
//---------------------------------------------------------
// Обработка движения мышки
//---------------------------------------------------------
bool Application::mouseMoved(const OIS::MouseEvent &evt)
{
   return true;
}
	
//---------------------------------------------------------
// Обработка нажатий клавиш мышки
//---------------------------------------------------------
bool Application::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
   return true;
}
	
//---------------------------------------------------------
// Обработка отпусканий клавиш мышки
//---------------------------------------------------------
bool Application::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
   return true;
}
	
//---------------------------------------------------------
// Обработка начала очередного рендеринга
//---------------------------------------------------------
bool Application::frameStarted(const Ogre::FrameEvent& evt)
{
   return true;
}
	
//---------------------------------------------------------
// Обработка окончания очередного рендеринга
//---------------------------------------------------------
bool Application::frameEnded(const Ogre::FrameEvent& evt)
{
   return true;
}


Пояснение кода программы


Основной интерес для нас представляет здесь функция Application::Start(), которая отвечает за инициализацию приложения. Поэтому мы рассмотрим её подробнее:

_ogre = new Ogre::Root("", "", "Game.log");
_ogre->addFrameListener(this);

В этих строчках кода мы создаем корневой объект Ogre::Root, посредством которого будем затем обращаться ко всем классам и методам 3D движка. Также мы регистрируем себя как получателя сообщений о начале и окончании рендеринга изображения.

   // Загрузка плагинов
#if defined(_DEBUG)
   _ogre->loadPlugin("RenderSystem_Direct3D9_d");
#else
   _ogre->loadPlugin("RenderSystem_Direct3D9");
#endif

Далее мы загружаем необходимые для работы плагины, пока здесь только рендеринг посредством Direct3D, но позже мы добавим сюда другие. Также обратите внимание на директивы предпроцессора, благодаря которым, в режиме отладки загружается соответствующая версия плагина.
Хочу также отметить, что загрузка плагинов и вообще инициализация системы в примерах, которые идут вместе с Ogre3D SDK осуществляется иначе: с использованием конфигурационных файлов и начального окна диалога. Но в реальной игре мне кажется это неудобным и громоздким.

   // Выбор системы рендеринга
   Ogre::RenderSystemList* renderSystems = _ogre->getAvailableRenderers();
   Ogre::RenderSystem* renderSystem = 0;
    for(Ogre::RenderSystemList::iterator it = renderSystems->begin(); it != renderSystems->end(); ++it) {
      if((*it)->getName().find("Direct3D") != Ogre::String::npos)  {
         renderSystem = *it;
         break;
      }
    }    
	
   if(renderSystem == 0) exit(1);
    _ogre->setRenderSystem(renderSystem);
 
   // Конфигурация рендеринга
   renderSystem->setConfigOption("Full Screen", "No");  
   renderSystem->setConfigOption("Video Mode", "800 x 600 @ 32-bit colour");

Далее, мы выбираем из доступных систем рендеринга, а затем настраиваем разрешение и режим экрана. Вообще мы просто находим здесь систему Direct3D и используем дальше только её. Если бы до этого мы загружали еще и плагин для поддержки OpenGl, то могли бы выбирать между OpenGl и Direct3D, но для простоты я это опустил. В принципе можно вообще использовать только Direct3D и забыть про OpenGl, особенно если наше приложение будет работать только под Windows.

   // Создание окна рендеринга
   _renderWindow = _ogre->initialise(true, "Game"); 
	
   // Создание менеджера сцены
   _sceneManager = _ogre->createSceneManager(Ogre::ST_GENERIC, "SceneManager");

Затем мы создаем собственно главное окно нашей игры, в котором все и будет происходить. А также менеджер сцены..
Нужно пояснить, что в Ogre3D менеджер сцены - это основной класс, который отвечает за расположение объектов и их дальнейшее отображение. С помощью него происходит создание и удаление любых игровых объектов (источников света, камер, 3d-моделей, изображений и т.д.).

   // Создание сферы
   Ogre::Entity* sphere = _sceneManager->createEntity("Sphere", Ogre::SceneManager::PT_SPHERE);
   Ogre::SceneNode* node = _sceneManager->getRootSceneNode()->createChildSceneNode("SphereNode");
   node->attachObject((Ogre::MovableObject*)sphere);

Далее мы создаем сферу и размещаем её в сцене. Она нужна просто для демонстрации, иначе мы просто бы увидели черный экран. В дальнейшем эту сферу мы уберем.
В Ogre3D любой объект в сцене не имеет таких параметров, как положение или ориентация. За это отвечает узел сцены. Для того чтобы наша сфера стала видна, нужно создать узел сцены и прикрепить ее к нему. Таким образом к одному узлу можно прикрепить множество объектов, естественно объекты, прикрепленные к одному узлу имеют одинаковое положение и ориентацию в пространстве. Вообще организация сцены имеет древовидную структуру, каждый узел сцены может иметь неограниченное количество потомков. Положение узла потомка рассчитывается относительно положения родителя. Т.е. если мы создаем планету, а затем прикрепляем к ней спутник, положение последнего задается относительно планеты :)

   // Создание камеры и окна видимости
   _camera = _sceneManager->createCamera("Camera");
   _camera->setPosition(-400, 300, -400);
   _camera->lookAt(0,0,0);  
   _camera->setNearClipDistance(1);
   _viewport = _renderWindow->addViewport(_camera);
   _camera->setAspectRatio(Ogre::Real(_viewport->getActualWidth()) / 
      Ogre::Real(_viewport->getActualHeight()));

Здесь, мне кажется все очевидным: создаем и настраиваем камеру, через которую мы будем видеть сцену.

   // Создание и настройка менеджера ввода
   unsigned long hWnd = 0;
   OIS::ParamList paramList;
   _renderWindow->getCustomAttribute("WINDOW", &hWnd);
   paramList.insert(OIS::ParamList::value_type("WINDOW", Ogre::StringConverter::toString(hWnd)));
	
   _inputManager = OIS::InputManager::createInputSystem(paramList);
   _keyboard = static_cast<OIS::Keyboard*>(_inputManager->createInputObject(OIS::OISKeyboard, true));
   _mouse = static_cast<OIS::Mouse*>(_inputManager->createInputObject(OIS::OISMouse, true));
    
   _mouse->getMouseState().height	= _renderWindow->getHeight();
   _mouse->getMouseState().width	= _renderWindow->getWidth();
	
   _keyboard->setEventCallback(this);
   _mouse->setEventCallback(this);

Менеджер ввода отвечает за работу с устройствами ввода, в данном случае с мышкой и клавиатурой. Как я уже указывал при рассмотрении заголовочного файла, наш основной класс приложения регистрируется для обработки событий связанных с мышкой или клавиатурой. Сейчас никакой обработки событий мы производить не будем, за исключением того чтобы завершать программу, при нажатии клавиши “Escape”.

На этом инициализацию можно считать законченной и дальше идет основной цикл нашей программы.

   // Основной цикл
   _shutdown = false;
   
   while(!_shutdown) 
   {
      if(_renderWindow->isClosed()) _shutdown = true;
	
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
      Ogre::WindowEventUtilities::messagePump();
#endif
      if(_renderWindow->isActive())
      {
         // Захват ввода
         _keyboard->capture();
         _mouse->capture();
	
         // Рендеринг
         _ogre->renderOneFrame();
      }
      else
      {
         Sleep(1000);
      }
   }

В этом цикле мы находимся пока не будет установлен признак завершения программы _shutdown. А он устанавливается при обработке сообщений от клавиатуры:

bool Application::keyPressed(const OIS::KeyEvent &evt)
{
   if(evt.key == OIS::KC_ESCAPE) {
      Shutdown();
   }
   return true;
}


Компиляция проекта


Вот и все наша первая программа готова, можно компилировать :). Хотя компилятор сейчас выдаст ошибку, т.к. мы не указали пути к необходимым для сборки библиотекам. (собственно сам Ogre3D SDK)

Для этого открываем свойства проекта: Меню->Проект->Свойства
В дереве свойств открываем нужную ветку: Свойства конфигурации->C/C++->Общие
и добавляем дополнительные каталоги включения. У меня это каталог “c:/Work/Libs/OgreSDK-1.6.4/include”, у вас может быть другой, зависит от того, куда вы установили Ogre3D SDK.
Затем открываем ветку: Свойства конфигурации->Компоновщик->Общие
и также добавляем каталог библиотеки. У меня соответственно “c:/Work/Libs/Ogre/SDK-1.6.4/lib”.
Также в Свойства конфигурации->Компоновщик->Ввод
указываем нужные нам библиотеки, пока это только OgreMain_d.lib, OIS_d.lib. Теперь компиляция должна проходить без ошибок.

Все, можно запускать нашу программу, не забыв предварительно скопировать нужные *.dll файлы в каталог с программой. Это файлы OgreMain_d.dll, OIS_d.dll, RenderSystem_Direct3D9_d.dll .

Если вы все сделали верно, то должны получить вот такую удивительную картинку:
tutorial01_4.jpg

Поздравляю! Первый шаг сделан.
Прикрепляю также исходные файлы нашего небольшого проекта tutorial.zip.