MerGC_Team | Дата: Пятница, 28.02.2014, 22:26 | Сообщение # 1 |
Веселый админ
Группа: Администраторы
Сообщений: 32
Статус: Оффлайн
| Этот туториал описывает как создать свою собственную ноду и разместить ее на сцене. Это может понадобится, если те возможности движка, которые вам нужны, еще не реализованы. Для примера, если вы хотите написать какой-нибудь особенный портальный рендерер или ноду реализующую генерацию ландшафта по каким-то вашим собственным законам. Внедрить ваши желания в движок IrrLicht очень легко посредством создания своих типов узлов(нод) для сцены. Я постараюсь сделать туториал доступным: очень коротким, умещенным в один .cpp файл, с сохранением стиля предыдущих туториалов. В начале включаем в проект заголовочные файлы, указываем, что используем пространство имен ‘irr’.
Код #include <irrlicht.h> #include "driverChoice.h" #ifdef _MSC_VER #pragma comment(lib, "Irrlicht.lib") #endif using namespace irr; using namespace video;
Здесь начинается самая главная часть туториала: мы создаем класс своей собственной ноды. Для простоты, это не будет рендерер с какой-либо хитрой техникой, а функционал рисующий тетраэдр – 3d объект включающий 4 вершины и не более. Замечу что для этого случая нет необходимости создавать свои собственные реализации для нод, достаточно создать модель в 3d редакторе и загрузить ее геометрию в irr::scene::IMeshSceneNode. Задача этого примера на “пальцах” проиллюстрировать создание своих собственных нод для сцены.
Для того чтобы мы могли нашу ноду добавить на сцену, она должна быть унаследована от интерфейсного класса irr::scene::ISceneNode и переопределить некоторые его методы.
Код class CSampleSceneNode : public scene::ISceneNode {
Для начала, объявим несколько переменных: границы объема(bounding box), 4 вершины и материал тетраэдера.
Код core::aabbox3d Box; video::S3DVertex Vertices[4]; video::SMaterial Material;
Параметрами конструктора будут нода родитель, указатель на менеджер сцены и уникальный номер ноды. В конструкторе мы вызовем конструктор родительского класса, установим свойства материала и создадим 4 вершины тетраэдера, который позже будем рисовать.
Код public: CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id): scene::ISceneNode(parent, mgr, id) { Material.Wireframe = false; Material.Lighting = false; Vertices[0] = S3DVertex(0,0,10, 1,1,0, SColor(255,0,255,255), 0, 1); Vertices[1] = S3DVertex(10,0,-10, 1,0,0, SColor(255,255,0,255), 1, 1); Vertices[2] = S3DVertex(0,20,0, 0,1,1, SColor(255,255,255,0), 1, 0); Vertices[3] = S3DVertex(-10,0,-10, 0,0,1, SColor(255,0,255,0), 0, 0);
Движку Irrlicht необходимо знать bounding box ноды. Эта информация используется для автоматического отсечения невидимых поверхностей и др. Отсюда, нам необходимо определить bounding box для наших 4х вершин. Если у вас нет необходимости в использовании bounding box для автоматического отсечения и/или не хотите его использовать эту функцию вообще, вы можете вызвать irr::scene::ISceneNode::setAutomaticCulling() со значением irr::scene::EAC_OFF.
Код Box.reset(Vertices[0].Pos); for (s32 i=1; i<4; ++i) Box.addInternalPoint(Vertices[i].Pos); }
В движке прежде чем любая нода нарисуется, менеджером сцены будет вызван ее обработчик irr::scene::ISceneNode::OnRegisterSceneNode() . В этом обработчике нода определяет когда она хочет отобразится и хочет ли отобразиться вообще. Это необходимо, когда нужно изменить порядок рендеринга ноды, т.е. когда менеждер сцены вызовет ее метод irr::scene::ISceneNode::render(). Для примера, ноды отрисовываются последовательно одна за другой отсортированные по типам. Сначала рендерятся ноды камер, потом источников света и т.д. Сейчас мы не будем указывать движку когда рендерить нашу ноду, но если вам захочеться вокнуть отрисовку вашей ноды на этап отрисовки камеры вы можете вызвать SceneManager->registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA); А уже после метод irr::scene::ISceneNode::OnRegisterSceneNode() родительского класса.
Код virtual void OnRegisterSceneNode() { if (IsVisible) SceneManager->registerNodeForRendering(this); ISceneNode::OnRegisterSceneNode(); }
В методе render() происходит следующее – нода рисует сама себя, мы переопределим этот метод своим кодом рисования тетраэдера.
Код virtual void render() { u16 indices[] = { 0,2,3, 2,1,3, 1,0,3, 2,0,1 }; video::IVideoDriver* driver = SceneManager->getVideoDriver(); driver->setMaterial(Material); driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); driver->drawVertexPrimitiveList(&Vertices[0], 4, &indices[0], 4, video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT); }
По завершению добавим еще тройку нужных методов. irr::scene::ISceneNode::getBoundingBox() возвращает bounding box ноды, irr::scene::ISceneNode::getMaterialCount() возвращает количество материалов использованных в ноде (в нашем случае вернет 1) и irr::scene::ISceneNode::getMaterial() возвращающий материал относительно запрошенного индекса, а так как материал у нас один, то независимо от индекса мы будет возвращать значение переменной Material.
Код virtual const core::aabbox3d& getBoundingBox() const { return Box; } virtual u32 getMaterialCount() const { return 1; } virtual video::SMaterial& getMaterial(u32 i) { return Material; } };
Это все. Разработка своей собственной ноды для сцены закончена. Осталось только стартануть движок, добавить камеру и поглядеть на результат.
Код int main() { // запрашиваем драйвер, типа directx, opengl и т.д. video::E_DRIVER_TYPE driverType=driverChoiceConsole(); if (driverType==video::EDT_COUNT) return 1; // ничего не выбрано - выходим из программы. // создаем корневой объект IrrlichtDevice *device = createDevice(driverType, core::dimension2du(640, 480), 16, false); // ошибка создания движка для выбранного драйвера, выходим из программы. if (device == 0) return 1; // определяем заголовок окна, пару вспомогательных указателей, добавляем камеру device->setWindowCaption(L"Custom Scene Node - Irrlicht Engine Demo"); video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager(); smgr->addCameraSceneNode(0, core::vector3df(0,-40,0), core::vector3df(0,0,0));
Создаем ноду на основе нашего собственного класса. Я не проверяю результат работы new, т.к. он сгенерит исключительную ситуацию раньше чем вернет 0 при сбое. При нормальном ходе конструктор ноды создав ноду установить счетчик ссылок в 1 и далее родительский конструкто увеличит его еще на 1 прицепив к сцене, поэтому необходимо вызвать drop. Вообще, хорошая практика вызывать drop после того как вы закончили работать с объектом и отдали его на попечение движка.
Код CSampleSceneNode *myNode = new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666);
Чтобы было чем полюбоваться кроме утомительного наблюдения за “мертвым” тетраэдером, оживим его добавив аниматор, который будет вращать нашу ноду irr::scene::ISceneManager::createRotationAnimator() .
Код scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator( core::vector3df(0.8f, 0, 0.8f)); if(anim) { myNode->addAnimator(anim); // присоединяем аниматор к ноде
После присоединения надо вызвать irr::IReferenceCounted::drop() для аниматора, т.к. аниматор создан одной из функций типа createFoo() ну и не плохо было бы почитать эту статью в ней подробно описано что и для чего метод drop().
Код anim->drop(); anim = 0; // обнуляем указатель на всякий пожарный }
Теперь мы закончили с созданием CSampleSceneNode и должны сбросить счетчик. Это не удалит объект, т.к. он присоединен к сцене, пока вы сами не удалите сцену целиком или именно эту ноду вызвав для нее метод remove().
Код myNode->drop(); myNode = 0; // обнуляем указатель на всякий пожарный
Теперь запускаем главный цикл.
Код u32 frames=0; while(device->run()) { driver->beginScene(true, true, video::SColor(0,100,100,100)); smgr->drawAll(); driver->endScene(); if (++frames==100) { core::stringw str = L"Irrlicht Engine ["; str += driver->getName(); str += L"] FPS: "; str += (s32)driver->getFPS(); device->setWindowCaption(str.c_str()); frames=0; } } device->drop(); return 0; }
Все. Компилируем и запускаем.
|
|
| |