Введение в OgreNewt (Ogre3D + Newton Game Dynamics)

Введение в OgreNewt (Ogre3D + Newton Game Dynamics)

Сегодня мы познакомимся с физическим движком Newton Game Dynamics и графическим Ogre3D. Конкретно – с их связкой OgreNewt, которая написана walaber’ом. Во время написании статьи, я брал OgreNewt из svn. Версия была совместима с Newton 2.15 (последняя – 2.17). Надеюсь, автор и далее будет активно разрабатывать библиотеку. Для примера, разрушения мне придётся писать вручную.

Но начнём с самого начала – скомпилируем библиотеку. В исходниках есть project-файл для Visual Studio и файл для утилиты CMake. Для успешного билда нам нужно будет скачать сам физический движок Newton и прописать путь к нему в настройках OgreNewt. Также в зависимостях есть boost.

Надеюсь, подключить всё это вам не составит труда, иначе браться за физику рановато :). В комплекте также идут 9 демо-приложений. Именно их я хочу расписать поэтапно.

Создайте новый проект с любым названием. Сразу же идём в раздел его настроек и прописываем Additional Include Directories & Libraries. Вот наглядно то, что нужно подключить для успешной работы:

%OgreSDK%include;

%NewtonSDK%;

%somefolder%boost_x_xx_x;

%OgreSDK%includeOIS;

%OgreNewt%include;

%OgreSDK%samplesinclude

%OgreNewt%lib;

%NewtonSDK %x32dll_vs9

%NewtonSDKx32

%OgreSDK%lib;

Эти библиотеки понадобятся нам для работы. Их нужно подключить в разделе Linker->Input->Additional Dependencies: newton. lib OgreMain. lib OIS. lib OgreNewt. lib

Следующий этап – каркас приложения. Создайте 2 h-файла: OgreNewtonApplication. h, OgreNewtonFrameListener. h

OgreNewtonApplication. h

#pragma once

#include <ExampleApplication. h>

#include <OgreNewt. h> class OgreNewtonApplication : public ExampleApplication

{ public:

OgreNewtonApplication(void);

~OgreNewtonApplication(void); protected: void createFrameListener(); void createScene(); private:

OgreNewt::World* m_World;

Ogre::FrameListener* mNewtonListener;

};

OgreNewtonFrameListener. h

#pragma once

#include "ExampleFrameListener. h"

#include <OgreNewt. h> class OgreNewtonFrameListener :public ExampleFrameListener

{ protected:

OgreNewt::World* m_World;

SceneManager* mSceneMgr; int count; public:

OgreNewtonFrameListener(RenderWindow* win, Camera* cam,

SceneManager* mgr, OgreNewt::World* W);

~OgreNewtonFrameListener(void); bool frameStarted(const FrameEvent &evt);

};

Main. cpp

#ifdef WIN32

#define WIN32_LEAN_AND_MEAN

#include <windows. h>

#endif

#include "OgreNewtonApplication. h"

#ifdef WIN32

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )

#else int main(int argc, char **argv)

#endif

{

OgreNewtonApplication app; try { app. go();

} catch( Ogre::Exception& e ) { printf( "Error: %s", e. getFullDescription().c_str() );

} return 0;

}

OgreNewtonFrameListener

#include "OgreNewtonFrameListener. h" using namespace Ogre;

OgreNewtonFrameListener::OgreNewtonFrameListener(RenderWindow* win, Camera* cam, SceneManager* mgr, OgreNewt::World* W) :

ExampleFrameListener(win, cam)

{ m_World = W; mSceneMgr = mgr;

}

OgreNewtonFrameListener::~OgreNewtonFrameListener(void)

{

} bool OgreNewtonFrameListener::frameStarted(const FrameEvent &evt)

{ if (mKeyboard->isKeyDown(OIS::KC_ESCAPE)) return false; return true;

}

OgreNewtonApplication

#include "OgreNewtonApplication. h"

#include "OgreNewtonFrameListener. h"

#include <OgreNewt. h>

#include <OgreNewt_BasicFrameListener. h> using namespace Ogre;

OgreNewtonApplication::OgreNewtonApplication(void)

{

// Создаём OgreNewt World – это нужно сделать перед любим действием с физикой.

M_World = new OgreNewt::World();

}

OgreNewtonApplication::~OgreNewtonApplication(void)

{ delete m_World;

} void OgreNewtonApplication::createScene()

{ mCamera->setPosition(0.0, 90.0, 100.0); mCamera->lookAt(0.0, 20.0, 0.0);

} void OgreNewtonApplication::createFrameListener()

{

// Это базовый frame listener, который идёт вместе с OgreNewt. Его задача – обновлять физику

// на нужном фреймрейте (100 фпс в нашем случае).

MNewtonListener = new OgreNewt::BasicFrameListener( mWindow, m_World, 100 ); mRoot->addFrameListener(mNewtonListener);

// А это уже наш frame listener, где мы можем творить чудеса над физикой J mFrameListener = new OgreNewtonFrameListener( mWindow, mCamera, mSceneMgr, m_World); mRoot->addFrameListener(mFrameListener);

}

С самым занудным справились. Теперь начнём работать конкретно над физикой. Для начала, небольшое отступление от кода, поговорим о базовых вещах для Newton.

(World) OgreNewt::World Это пространство, в котором существуют и взаимодействуют между собой все физические тела. Можно создавать несколько физических миров.

(Твёрдое тело) OgreNewt::Body Представляет собой базовый объект в физическом мире. Взаимодействует с другими телами в мире. Имеет массу, размер и форму. Всё, что нуждается в физической обработке должно иметь Rigid Body.

(Коллизия) OgreNewt::Collision Этот объект – физическая форма тела. Rigid Body нуждается в Collision для образования своей формы. Существуют несколько объектов, которые представляют этот класс:

Primitive Shapes: содержат боксы, эллипсоиды, цилиндры, капсулы.

Convex Hulls: принимает несколько точек в физическом мире и создает оптимальную, наименьшую физическую форму для тела. В основном, вы будете использовать вершини вашей модели для создания физической сетки.

Tree Collisions: специальная форма физической сетки, используется только для статистических объектов. Тела, форма которых созданна этой коллизией автоматически имеют бесконечную массу. Их лучше всего использовать для поверхностей мира.

Вы можете совмещать виды коллизий для достижения более реальной сетки.

(Связь) OgreNewt::Joint Joint – связь между 2 физическими телами. Используется, например, для создания дверей.

(Материал) OgreNewt::MateriallD & OgreNewt::MaterialPair) Материалы помогают физическому движку правильно обработать коллизию между телами. Для примера, материал может задать коэфициент трения объекту. Использовать их очень легко. Создаем нужное количество MateriallD объектов, которые представляют своих физические свойства: wood_material, metal_material, …

Далее мы создаем объект MaterialPair и “цепляем” ему 2 материала(MateriallD-объекта), а в обработчике мы решаем поведение мира во время столкновения объектов с такими материалами. Чтобы было понятней, представьте, что у вас есть материал железа и асфальта. Вам нужно обработать столкновение их и добавить специальные эффекты. Вы в MaterialPair обрабатываете эти 2 материала. Для примера, производите характерный звук столкновения и рисуете искры в нужном месте. Также существует ещё 2 важных объекта: RagDall, Vehicle.

Хватит с нас теории, переходим к программированию. Давайте определим то, что нам нужно для создания физического объекта:

Создаём Entity & Node для нужного объекта

Получаем каким-то образом Collision (по вашему усмотрению)

Создаём физическое тело (Rigid Body) под нашу коллизию (не забывайте, коллизия задаёт форму тела).

“Цепляем” Entity к нашему физическому телу

Установка массы, позиции, инерции, материалов, приложение сил и т. д.

Стоит отметить, что задавать позицию объекта нужно в физическом движке, а не графическом.

Итак, создадим поверхность, где будем развлекаться. Перейдите к методу createScene. Код можно писать сразу после инициализации камеры.

Entity *realFloor; SceneNode *realFloorNode; realFloor = mSceneMgr->createEntity("RealFloor", "simple_terrain. mesh"); realFloorNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("RealFloorNode"); realFloorNode->attachObject(realFloor); realFloor->setMaterialName("Examples/OffsetMapping/Specular");

Это мы создали объект в графическом мире, инициализируем его в физическом:

OgreNewt::CollisionPrimitives::TreeCollisionSceneParser *tree_col = new OgreNewt::CollisionPrimitives::TreeCollisionSceneParser(m_World); tree_col->parseScene(realFloorNode, true, 0);

Не забывайте, что для неподвижных поверхностей лучше использовать TreeCollision. В нашем случае мы создали такой объект и передали ему указатель на нашу поверхность. parseScene автоматически построит сетку коллизии по мешу. Далее нам нужно создать Rigid Body для нашей коллизии.

OgreNewt::Body *body = new OgreNewt::Body(m_World, OgreNewt::CollisionPtr(tree_col)); body->attachNode(realFloorNode); body->setPositionOrientation( Ogre::Vector3(0.0,0.0,0.0), Ogre::Quaternion::IDENTITY );

Мы создали Rigid Body, передали ему ссылку на форму нашего физического объекта, прицепили графический объект к физическому(вторая строка) и определили положение нашей поверхности.

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

Попытаемся оживить сцену и создать куб, который будет падать сверху. Ниже предыдущего кода дописываем:

Vector3 size(10.0, 10.0, 10.0); realFloor = mSceneMgr->createEntity("box", "box. mesh"); realFloor->setMaterialName("Examples/OffsetMapping/Specular"); realFloorNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("boxnode"); realFloorNode->attachObject(realFloor); realFloorNode->setScale(size);

Не забывайте, что мы можем использовать одни и те же объекты Entity & SceneNode & Collision. Выше мы определили размер нашего кубика, чтобы позже передать физическому телу.

OgreNewt::ConvexCollisionPtr col = OgreNewt::ConvexCollisionPtr( new OgreNewt::CollisionPrimitives::Box( m_World, size, 0 ));

OgreNewt::Body* bod = new OgreNewt::Body( m_World, col );

Теперь мы создаем объект коллизии по примитиву Box. Передаём в параметры размер графического кубика. Следующий этап – тело объекта. Работаетаем по тому же принципу, что и с поверхностью.

А теперь одна из самых интересных частей:

Ogre::Real mass = size. x * size. y * size. z * 2.5;

Выбираем массу физического объекта. Я бы вам посоветовал в своих проектах использовать такой же принцип задания массы: размеры*коэфициент, тогда вы сможете достичь реального взаимодействия между телами.

Ogre::Vector3 inertia, offset; col->calculateInertialMatrix(inertia, offset);

Вычисляем центр объекта и его инерцию. Думаю, что такое центр объекта и зачем он объяснять не нужно, а вот инерция штука сложная. Объяснять я вам здесь не буду, так как цель статьи другая.

Bod->attachNode( realFloorNode ); bod->setMassMatrix(mass, mass*inertia); bod->setCenterOfMass(offset);

Цепляем графический объект к физическому, задаём массу и инерцию(по массе), центр объекта. И последнее, что нужно для создания объекта – задание позиции: bod->setPositionOrientation(Vector3(10.0, 90.0, -15.0), Ogre::Quaternion::IDENTITY);

Координаты я подобрал, чтобы кубик красивей падал на поверхность. Теперь смело можете компилировать и запускать. Но, ничего не произойдёт. Вы будете видеть “застывший” кубик над нашей поверхностью. Чтобы он что-то сделал, нужно приложить к нему силу. Вспомните курс школьной физики. Какая сила действует на абсолютно все физические объекты? Сила притяжения, которая равняется 9.8 м/с^2 для нашей планеты. Чтобы на тело действовала эта сила, нужно просто вызвать, заранее созданный в OgreNewt, коллбэк(callback): bod->setStandardForceCallback();

После запуска наш кубик упадёт на поверхность:


Карта сайта


Информационный сайт Webavtocat.ru