Готовый перевод Ogre3D Intermediate Tutorials / Промежуточные туториалы Ogre3D: Intermediate Tutorial 2: RaySceneQueries and Basic Mouse Usage (Part 1 of 2)

Внимание! Этот перевод, возможно, ещё не готов, так как модераторы установили для него статус «перевод редактируется»

ВВЕДЕНИЕ

В этом уроке мы создадим базис, необходимый для любого редактора сцен. В течении урока мы обсудим:

1. Как использовать трассировку лучей (RaySceneQuery) для предотвращения "проваливания" камеры сквозь ландшафт.

2. Как использовать интерфейсы MouseListener и MouseMotionListener.

3. Использование мыши для выбора реальных координат ландшафта X и Y.

Предварительные условия

Этот урок предполагает, что вы уже знаете, как настроить проект, использующий Ogre и заставить его успешно компилироваться.

Предполагается, что вам знакомы базовые объекты Ogre (узлы сцены, сущности и т.д.).

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

(Ogre также широко использует STL внутри себя, так что если вы не знакомы с этой библиотекой, вы должны потратить немного времени на её изучение.)

Данный урок использует графический интерфейс CEGUI. Вы должны полностью выполнить все шаги в Базовом Руководстве №7 для того, чтобы ваше приложение начало работать правильно.

ДЛЯ НАЧАЛА

Первое, вы должны создать проект и добавить в него следующий код:

Перед продолжением убедитесь, что этот код компилируется.

Поскольку, начиная с версии OGRE 1.7 в его состав не входит графический интерфейс CEGUI, вы должны настроить и сконфигурировать свой проект на использование CEGUI. Не забудьте обновить настроки проекта.

В разделе С/С++, добавьте в раздел Дополнительно подключаемые директории путь к файлам CEGUI.

В опциях Компоновщика укажите пути к библиотекам CEGUI, а также укажите файлы CEGUIOgreRenderer_d.lib CEGUIBase_d.lib в качестве дополнительных зависимостей. Если вы настраиваете конфигурацию "Релиз" - удалите в именах файлов постфикс "_d".

Скопируйте dll библиотеки CEGUI в папку, где их может обнаружить ваш проект или укажите путь к папке BIN в Переменных Окружения.

НАСТРОЙКА СЦЕНЫ

Перейдем к функции создания сцены ITutorial02::createScene.

Этот код должен быть вам знаком.

если вы не понимаете, что здесь происходит, прочитайте API документацию перед тем как продолжать.

Добавьте следующее в функцию создания сцены createScene.

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

Для этого мы будем использовать вызовы методов CEGUI, но перед тем как мы это сделаем, нам необходимо запустить графический интерфейс CEGUI.

Это очень просто - метод bootstrapSystem() сделает для нас все необходимое.

Примеание: этот метод также скажет CEGUI использовать систему управления ресурсами Ogre.

Теперь мы покажем курсор. Я не буду объяснять, что делает следующий код. Мы рассмотрим его в следующих уроках.

Если вы скомпилируете и запустите проект, вы увидите курсор в центре экрана, но он еще не будет двигаться.

Скорее всего вам необходимо будет сообщит системе управления ресурсами Ogre о расположении ресурсов CEGUI.

Если во время исполнения кода возникли не обрабатываемые исключения, попробуйте добавить следующее в ваш файл resources.cfg.

ВВЕДЕНИЕ В МЕНЕДЖЕР КАДРОВ ( FRAMELISTENER )

Этого было достаточно для запуска нашего приложения.

Менеджер кадров сцены или проще говоря FrameListener сложная часть механизма Ogre. Перед тем как мы начнем реализацию нашего проекта, я потрачу некоторое время на объяснение, того, как мы с помощью FrameListenerа можем достичь желаемого результата.

Во-первых, мы хотим "привязать" правую кнопку мыши к обзору камеры. Это довольно неудобно, когда нет возможности использовать мышь для обзора, поэтому мы добавим управление мышью в нашу программу(правда только тогда, когда нажата ее правая клавиша).

Примечание: Заготовка урока уже содержит управление камерой, спасибо за это классу sdkCameraMan функционала OgreBites, но ради обучения мы будем реализовывать управление камерой с нуля.

Во-вторых. мы хотим сделать так, чтобы камера не проваливалась сквозь наш ландшафт. Это будет наиболее ожидаемым эффектом работы нашей программы.

В-третьих, мы хотим добавлять объекты в сцену там, где мы кликнули по ландшафту.

Ну и наконец, мы хотим перемещать объекты в сцене.

Это мы сделаем с помощью нажатия и удержания левой кнопки мыши на объекте и перемещения его в нужное нам место.

Отпуская кнопку мыши мы оставляем его там, куда переместили.

Чтобы сделать это, мы будем использовать некоторые защищенные переменные(они уже добавлены в класс).

Переменная mRaySceneQuery хранит копию экземпляра класса RaySceneQuery, который мы будем использовать для определения координат ландшафта.

Переменные mLMouseDown и mRMouseDown будут отслеживать нажатия кнопок мыши (таким образом, значение mLMouseDown будет true, когда пользователь удерживает нажатой левую кнопку и наоборот )

mCount счетчик количества объектов сцены.

mCurrentObject содержит указатель на последний созданный узел сцены(SceneNode). Мы будем использовать эту переменную для перемещения объектов.

Наконец, mGUIRenderer хранит указатель на рендер CEGUI, который мы будем использовать для обновления состояния CEGUI.

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

Настройка менеджера кадров сцены.

Перейдем к методу createFrameListener() и добавим следующий код после вызова метода базового класса BaseApplication::createFrameListener().

Поскольку наш ландшафт небольшого размера, мы уменьшим скорость вращения камеры.

Наконец, нам необходимо создать запрос для обработки трассировки луча - RaySceneQuery. Сделаем это с помощью менеджера сцены.

Это все, что нам необходимо для создания менеджера кадров, но, поскольку мы создали запрос трассировки RaySceneQuery, позже мы должны его удалить. Не удаленный запрос трассировки приводит к утечкам памяти(прим. переводчика)

Перейдем к деструктору приложения ~ITutorial02 и добавим следующую строку:

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

ДОБАВЛЯЕМ ОБЗОР МЫШЬЮ

Мы собираемся "привязать" обзор камеры к правой кнопке мыши. Чтобы сделать это, нам необходимо:

1. Когда двигается мышь, обновить состояния CEGUI (также для того, чтобы наш курсор переместился )

2. Установить значение переменной mRMouseButton = true, когда мы нажали и удерживаем нажатой правую кнопку мыши.

3. Установить значение переменной mRMouseButton = афдыу, когда мы отпустили правую кнопку мыши.

4. Когда "тянем" мышью, изменить положение вида камеры(правая кнопка нажата и мышь перемещается)

5. Во время перемещения вида прятать курсор мыши.

Найдем метод ITutorial02::mouseMoved

Мы добавим код для перемещения курсора каждый раз, когда наша мышь двигается.

Добавьте в вашу функцию следущее:

Теперь перейдем к следующей функции - ITutorial02::mousePressed.

Этот кусок кода скрывает курсор когда нажата правая клавиша мыши, а также устанавливает переменной mRMouseDown значение true.

Далее, мы должны показать курсор мыши и установить переменной mRMouseDown значение false, когда мы отпустим правую кнопку.

Найдите метод mouseReleased и добавьте следующий код:

Мы закончили подготовительные шаги и хотим изменять обзор с помощью правой кнопки мыши.

Все что нам необходимо, это знать дистанцию перемещения мыши с момента предыдущего события.

Мы делаем то же самое, что и в Базовом Уроке №5, когда поворачивали камеру.

Перейдите к методу ITutorial02::mouseMoved и добавьте следующий код перед вызовом оператора return.

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

ОБНАРУЖЕНИЕ СТОЛКНОВЕНИЙ С ЛАНДШАФТОМ

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

Поскольку менеджер кадров базового класса ( BaseApplication::createFrameListener ) уже содержит код управления камерой - мы не будем его трогать.

Вместо этого, после того как менеджер кадров передвинет камеру, мы будем проверять, чтобы расстояние от камеры до ландшафта было не менее 10 единиц.

Если расстояние будет меньше, мы просто установим камеру на расстоянии 10 единиц. Пожалуйста, в точности повторите следующий код.

В рамках этого урока мы будем использовать некоторые возможности Запроса Трассировки Луча (RaySceneQuery). На этом знакомство с этим инструментарием закончится.

Перейдите к методу ITutorial02::frameRenderingQueued и удалите весь код из этого метода.

Первое что мы должны сделать это вызвать базовый метод BaseApplication::frameRenderingQueued для обеспечения правильной работы приложения.

Если базовый метод вернет значение false, мы также аернем значение false

Поскольку базовый метод BaseApplication::frameRenderingQueued() содержит в себе методы обновления окон информационного менеджера (TrayManager)( показывает FPS и логотип Ogre) мы должны добавить вызов базового метода в самом начале нашей функции frameRenderingQueued. Также нам необходимо будет выполнить собственные действия.

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

Это называется Запрос Трассировки Луча (RaySceneQuery) и этот запрос даст нам информацию о высоте камеры над ландшафтом.

После получения координат камеры, мы должны создать луч.

Конструктор класса Ray принимает в своих параметрах начальную точку( начало луча) и его направление.

поскольку мы хоим выпустить луч строго вниз, то в нашем случае направлением будет отрицательное направление по оси Y ( NEGATIVE_UNIT_Y ).

Как только луч создан, мы говорим запросу, чтобы он использовал данный луч для трассировки.

Отмечу, что мы использовали высоту в 5000 единиц, а не реальную высоту камеры.

Мы сделали это, чтобы не допустить случая, когда камера будет находиться под ландшафтом и величина ее высоты будет отрицательной. При таком случае наш луч никогда бы не попал в ландшафт.

Теперь выполним запрос и получим результаты.

Доступ к данным результата запроса осуществляется с помощью итераторов STL, которые я я кратко опишу.

В основном результат запроса (в нашем случае очень упрощенно) это список мировых фрагментов сцены и список перемещаемых объектов - movables ( мы рассмотрим перемещаемые объекты movables в следующих уроках.)

Если вы не знаете как работать с итераторами STL, просто знайте с помощью вызова метода begin() мы получаем первый элемент контейнера.

Если выполняется условие result.begin() == result.end() - это значит, что данные для представления отсутсвуют и мы находимся в конце контейнера.

В следующих уроках мы будем использовать обработку нескольких значений результата запроса.

А сейчас мы просто двинемся дальше.

Следующая проверка гарантирует нам, что результат запроса содержит как минимум одно значение ( itr != result.end() ) и то, что этот объект - ландшафт ( itr->worldFragment )

Структура мирового фрагмента содержит в переменной singleIntersection информацию о том, где луч пересек ландшафт в формате Vector3.

Получим значение высоты точки ландшафта путем путем присваивания Y компоненты этого вектора нашей локальной переменной.

Как только мы получили высоту, мы сравниваем ее с высотой камеры. Если высота камеры меньше полученной, то мы перемещаем камеру вверх в нужную нам позицию.

Теперь мы установили камеру на высоту 10 единиц от ландшафта.

Это гарантирует нам, что мы не сможем посмотреть сквозь ландшафт даже будучи на небольшом расстоянии от него.

Далее, мы вернем значение true для продолжения цикла рендера. На этом этапе вы можете скомпилировать программу и опробовать полученные результаты.

ВЫДЕЛЕНИЕ ЛАНДШАФТА

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

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

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

Для этого нам необходимо изменить функцию mousePressed так, чтобы происходило какое-либо действие при нажатии на левую кнопку мыши.

Найдите следующий код в ITutorial02::mousePressed. Добавим код внутрь следующего if-оператора.

Первый кусок кода будет выглядеть очень знакомо.

Мы создадим и настроим Ray (луч) для использования с mRaySceneQuery объектом.

Ogre предоставляет нам отличную функцию Camera::getCameraToViewportRay для перевода клика на экране (x и y координаты) в Ray (луч), который мы будем использовать с RaySceneQuery объектом.

Далее мы выполним запрос и убедимся, что он возвращает результат.

Теперь, когда мы имеем worldFragment (и, следовательно, позицию, по которой был сделан клик), мы хотим создать объект и разместить его на этой позиции.

Наша первая трудность заключается в том, что каждые сущность (Entity) и узел сцены (SceneNode) в Ogre нуждаются в уникальном имени.

Поэтому мы будем называть каждую сущность (Entity) "Robot1", "Robot2", "Robot3" и т.д., а каждый узел сцены (SceneNode) - "Robot1Node", "Robot2Node", "Robot3Node" и т.д.

Сначала мы создадим имя (см. ссылку на C для получения более подробной информации по sprintf).

Далее мы создадим сущность (Entity) и узел сцены (SceneNode). Обратите внимание, что мы используем itr->worldFragment->singleIntersection для позиции робота по умолчанию.

Также мы масштабируем его до 1/10 первоначального размера, потому что используем небольшой ландшафт.

Обратите внимание, что мы присваиваем этот вновь созданный объект переменной mCurrentObject.

Мы будем использовать ее в следующем разделе.

Теперь скомпилируйте и запустите демо.

Теперь мы можете поместить робота на сцену, нажав кнопкой мыши на любую точку ландшафта.

Нам осталось добавить перетаскивание объектов, чтобы закончить нашу программу.

Добавим код внутрь следующего if-оператора:

Следующий кусок кода должен быть понятен уже сейчас.

Мы создаем луч (Ray) на основе текущего положения мыши, выполняем запрос RaySceneQuery и перемещаем объект на новую позицию.

Обратите внимание, что нам не нужно проверять mCurrentObject на валидность, потому что mLMouseDown не будет возвращать истину, если mCurrentObject не был установлен с помощью mousePressed.

Скомпилируйте и запустите программу. Вот теперь мы закончили!

Примечание: Чтобы получить пересечение луча с поверхностью, вы ( начальная точка луча ) должны находится над ландшафтом перед началом запроса.

Примечание: если вы используете ваш собственный фреймворк, убедитесь, что ваш запрос имеет доступ к слушателю кадров (frame listener), например, в вашем frameStarted() методе. В противном случае, если вы используете его в функции init(), вы можете не получить никаких результатов.

Задания для самоподготовки

Легкие упражнения

Для ограничения проваливания камеры сквозь ландшафт, мы выбрали величину в 10 единиц высоты над ландшафтом. Эта величина была выбрана произвольно.

Можем ли мы уменьшить это значение и быть ближе к ландшафту не пересекая его?

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

Иногда мы должны иметь возможность проходить сквозь ландшафт, особенно если мы работаем с редактором сцен.

Создайте переменную-флаг, которая будет включать/выключать определение столкновения с ландшафтом и назначьте горячую клавишу для управления этой переменной.

Убедитесь в том, что вы не создаете запрос трассировки, если у вас выключено определение столкновений.

Средние упражнения

На текущий момент мы делает запрос SceneQuery каждый кадр, независимо от того, перемещалась камера или нет.

Исправьте эту проблему и делайте запрос SceneQuery только тогда, когда камера перемещается.

Подсказка: Найдите смещение вектора камеры в ExampleFrameListener после вызова функции отрисовки кадра и сравните это смещение с нулевым вектором Vector3::ZERO

Сложные упражнения

Замечание: у нас есть множество повторений кода каждый раз, когда мы вызываем запрос к сцене.

Инкапсулируйте все, что относится к запросам к сцене в protected функции.

Убедитесь, что вы предусмотрели тот случай, когда у нас не будет ни одного пересечения с ландшафтом.

Экспертные упражнения.

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

Возьмите код из Урока №1 и выполните разделы "Сложные упражнения" и "Экспертные упражнения".

Затем, адаптируйте код так, чтобы робот ходил по нашему ландшафту, а не по пустой поверхности.

Добавьте код, чтобы при щелчке мыши на поверхности, робот начинал идти к этой точке.

Внимание! Этот перевод, возможно, ещё не готов.
Его статус: перевод редактируется

Переведено на Нотабеноиде
http://notabenoid.org/book/20063/65474

Переводчики: H0tHead, oYASo, Mingun, proVIDec

Настройки

Готово:

100.00% КП = 1.1