Готовый перевод Ogre3D Intermediate Tutorials / Промежуточные туториалы Ogre3D: Intermediate Tutorial 3: Mouse Picking (3D Object Selection) and SceneQuery Masks (Part 2 of 2)

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

ВВЕДЕНИЕ

В этом уроке мы продолжаем предыдущий.

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

ПРЕДПОСЫЛКИ

Этот урок рассчитан на то что вы уже прошли предыдущие предыдущие туториалы Ogre3D.

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

ДЛЯ НАЧАЛА

Если вы хотите следовать уроку полностью, или просто копировать и вставлять, убедитесь, что все .h файлы выглядят примерно так:

Вы также должны убедиться, что IntermediateTutorial3.cpp выглядит так.

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

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

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

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

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

ПОКАЗЫВАЕМ КАКОЙ ОБЪЕКТ ВЫДЕЛЕН

В этом уроке мы будем делать так, чтобы вы могли "выбирать" и перемещать объекты после того, как разместили их.

Мы бы хотели иметь способ дать пользователю понять с каким объектом в данный момент производят манипуляции.

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

Наша основная идея - это выключить отображение bounding box предыдущего объекта и показать bounding box нового объекта.

Чтобы сделать это мы добавим следующий код в начало mousePressed функции:

Затем этот код в самый конец той же функции:

Теперь mCurrentObject всегда выделен.

ДОБАВЛЯЕМ НИНДЗЯ

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

У нас будет "Режим Робот"("Robot Mode") и "Режим Ниндзя"("Ninja Mode"), которые будут определять какой объект мы хотим разместить в сцене.

Сделаем пробел клавишей переключения режимов.

Для начала настроим IntermediateTutorial так, чтобы он был в Режиме Робот с самого начала.

Нам понадобится переменная, содержащая статус объекта (размещаем робота или ниндзю?).

В секцию protected урока IntermediateTutorial добавьте следующую переменную:

Теперь добавьте этот код в конструктор IntermediateTutorial3:

Теперь мы в Режиме Робота!

Если бы это было так просто.

Теперь, в зависимости от значения переменной bRobotMode, мы должны создать объект Робот или объект Нинзя.

Найдите этот код в функции mousePressed:

Замена должна быть предельно понятной.

В зависимости от статуса bRobotMode, мы размещаем либо робота, либо ниндзю, и называем его соответсвующе:

Почти закончили.

Последняя вещь, которую нам надо сделать, это "повесить" смену режимов на пробел.

Вернитесь к файлу IntermediateTutorial3.h и переопределите функцию keyPressed () как показано здесь:

Теперь давайте заполним функцию в IntermediateTutorial3.cpp.

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

Так как нам это сделать? Просто, на самом деле тут всего несколько строчек:

Как видите, мы проверили нажатие на пробел и переключили режим.

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

Ну вот и все! Компилируйте и запускайте.

Теперь вы можете выбирать какой объект поместить, нажимая пробел.

ВЫБИРАЕМ ОБЪЕКТЫ

Теперь мы подходим к самой сути урока: использованию RaySceneQueries для выделения объектов в сцене.

Перед тем как мы начнем вносить изменения в код, я объясню RaySceneQueryResultEntry более детально.

(Пожалуйста, пройдите по ссылке и кратко просмотрите структуру.)

RaySceneQueryResult возвращает итератор структуры RaySceneQueryResultEntry.

Эта структура содержит три переменных.

Переменная distance показывает как далеко находится объект в направлении луча.

Одна из двух оставшихся переменных будет не нулевой.

Переменная movable будет содержать MovableObject(перемещаемый объект) если Луч его пересекает.

WorldFragment будет содержать WorldFragment объекты, если он попал в фрагмент мира (например террайн).

MovableObjects(Перемещаемые объекты) это практически любые объекты, которые вы будете добавлять в сцену(такие как Сущности, Источники Света и т.д.)

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

Обычно RaySceneQueries применяют для выбора и манипулирования либо MovableObject, по которому вы кликаете, либо SceneNode, к которому MovableObject присоединены.

Чтобы получить имя MovableObject (Перемещаемый объект), вызовите метод getName (получить имя).

Чтобы получить SceneNode (имя узла) или Node (узел), к которому присоединен объект, вызовите getParentSceneNode (получить родительский узел сцены) или getParentNode (получить родительский узел).

Переменная RaySceneQueryResultEntry будет равна NULL, если результат не является MovableObject (перемещаемый объект).

WorldFragment это различные фрагменты все вместе.

Если установлен worldFragment, член RaySceneQueryResult, то это означает, что результатом будет являться часть мировой геометрии, созданной SceneManager (менеджер сцены).

Тип мирового фрагмента, который возвращается, является базовым в SceneManager.

Реализация WorldFragment структуры включает в себя fragmentType переменную, которая определяет тип мирового фрагмента.

На основе переменной fragmentType устанавливаются другие переменные (singleIntersection, planes, geometry ил renderOp).

В общем говоря, RaySceneQueries возвращает только WFT_SINGLE_INTERSECTION WorldFragments.

singleIntersection переменная является простым вектором Vector3, сообщающим о местоположении пересечения.

Другие типы мировых фрагментов выходят за рамки данного руководства.

Теперь давайте посмотрим на пример.

Допустим, мы хотим вывести список результатов после использования RaySceneQuery.

Следующий код сделает это.

(Предполагается, что объект fout является типом ofstream, уже созданным с помощью метода open).

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

Обратите внимание, что иногда это может работать странным образом.

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

Различные менеджеры сцены реализуют RaySceneQueries по-разному.

Имейте в виду, экспериментируя с ним, когда вы используете его вместе с новым SceneManager.

Теперь, если мы посмотрим на код нашей RaySceneQuery, то заметим что: мы не проходим по всем результатам!

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

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

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

С другой стороны, что мы хотим "подцепить" и перетаскивать объекты, которые уже были установлены.

Если сейчас вы щелкните на объект, который был установлен ранее, программа игнорирует его и "робот" расположится за ним.

Что же исправим это сейчас.

Перейдем к функции mousePressed.

Во первых мы хотим быть уверены, что когда кликаем, мы получаем первый объект вдоль луча.

Чтобы сделать это, нам надо установить RaySceneQuery для сортировки по глубине.

Найдите этот код в функции mousePressed:

И замените его на этот:

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

Мы будем переписывать этот раздел, так что удалите этот код:

Нам нужно сделать так, чтобы была возможность выбирать объекты, которые уже находятся на экране.

Наша стратегия предполагает два варианта развития событий.

Первое, если пользователь нажимает на объект, тогда создаем mCurrentObject эквивалентным его родительному SceneNode.

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

Первое изменение состоит в том, что вместо ветвления if мы будем использовать цикл for:

Проверим, если первое пересечение - MovableObject, если таким образом мы назначим mCurrentObject родителем SceneNode.

Ну вот поймали все-таки.

Вначале, найдите вот эту часть кода в frameRenderingQueued:

Затем замените его этим:

Тут все должно быть понятно.

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

Сделаем то же самое с функцией mouseMoved. Найдите следующую секцию кода в функции mouseMoved:

И замените ее на это код:

Скомпилируйте приложение и опробуйте его.

Заметных изменений с момента последней компиляции быть не должно, но теперь мы делаем это правильно и будущие изменения в TerrainSceneManager не сломают код.

Найдите в mousePressed следующий код:

Найдите этот код в функции onLeftPressed:

И добавьте эту строчку кода после него:

Скомпилируйте и запустите приложение.

Вы увидите, что запрос не возвращает billboardset, что вы щелкнули.

И так далее.

УПРАЖНЕНИЯ

Простые УПРАЖНЕНИЯ

УПРАЖНЕНИЯ СРЕДНЕГО УРОВНЯ

СЛОЖНЫЕ УПРАЖНЕНИЯ

УПРАЖНЕНИЯ ДЛЯ ДАЛЬНЕЙШЕГО ОБУЧЕНИЯ

1. Добавьте способ выделения нескольких объектов, например с помощью зажатия клавиши Ctrl и выделения кликом мыши. Подсветите выделенные объекты.

При перемещении объектов перемещайте их группой все разом.

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

Реализуйте это в своей программе.

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

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

Переводчики: Taredd, oYASo, proVIDec, neutron

H0tHead, senorit, wuffur

Настройки

Готово:

57.47% КП = 1.0

Непереведённые фрагменты будут пропущены.