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

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

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

Ниже я привожу полностью содержимое файла 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 .
Если вы все сделали верно, то должны получить вот такую удивительную картинку:

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