Этот пост участвует в конкурсе „Умные телефоны за умные посты“
Не так давно фреймворк Qt Quick обзавелся дополнением Qt Quick3D, позволяющим полноценно работать с 3D объектами (поддерживается импорт из 3D Max и Blender), совершать над ними различные трансформации, анимации, применять эффекты, ну и вообще по полной использовать возможности лежащего в основе OpenGL. Работает всё это под Symbian, MeeGo, Windows\Linux\MacOs (ну и вообще везде, где Qt есть). В этом топике мы попробуем технологию «на зуб». Писать что-то сложное и серьёзное не хочется, поэтому мы сделаем хабрахолодильник, из которого по клику будет вылетать НЛО.
Сразу результат:
Качаем и устанавливаем Qt Creator, Qt library, Qt Quick3D и Blender. Еще нам понадобятся 3D-модели, которые мы будем использовать. Я взял вот этот холодильник и эту летающую тарелку (всё бесплатное). Ну и логотип Хабра бессовестно стащил.
Итак, запускаем Qt Creator и создаём новый проект: Файл->Новый файл или проект->Проект Qt Quick->Интерфейс пользователя на Qt Quick.

Вводим имя проекта, расположения папки, все остальные опции оставляем по-умолчанию.

Новосозданный проект выглядит как-то так:

Оставим пока его в покое.
Открываем Blender и готовим в нём модели, которые мы будем использовать. Я не буду детально описывать работу в Blender (всё-таки, это не по нему урок, да и не спец я в Blender). Вот вкратце, что мы сделаем:
Возвращаемся в Qt Creator к нашему проекту. При его создании в файл HabraHolod.qml был записан такой себе «Hello world», но он никакого отношения к Qt Quick3D не имеет, а потому мы его удалим. Начнём писать свой qml-код с нуля. В первой итерации он будет вот таким:
Давайте пройдемся по коду. В первой строке мы импортируем пакет Qt3D, который, если Вы всё установили верно, должен найтись и взяться за работу по отображению 3D-объектов. Если же Вы, как и я, что-то на этапе установки запороли (у меня была установлена другая версия библиотек Qt), то вот очень толковая статья, описывающая что и куда нужно положить руками, чтобы всё работало и еще одна, объясняющая как скомпилировать Qt Quick3D вручную.
Далее мы создаём элемент Viewport — это контейнер, в который можно добавлять 3D-объекты и задавать некоторые параметры их отображения (освещение, позицию камеры и т.д.). Дальше мы импортируем 3 наших 3ds-файла (каждый — в отдельный мэш) и создаём 3 элемента Item3D (обратите внимание на их связку с мэшами по свойству id).
Если Вы нажмёте Ctrl+R (запуск), то даже сможете увидеть результат:

Что-то странное, да? :) На самом деле всё работает верно. Всё дело в том, что пока ни окно нашего приложения, ни параметры 3D-сцены не настроены и поэтому мы смотрим на наши объекты в малюсенькое окошко с непонятной позиции. Если Вы вручную сделаете окно побольше и воспользуетесь скролингом, то увидите нашу сцену примерно такой:

Начало неплохое — в 10 строк кода у нас уже есть приложение, кое-как отображающее группу 3D-объектов (а ну-ка, сколько строк будет в нём, если написать его на С++\Java\.NET\Ваш_язык?).
Итак, объекты наши, конечно, отображаются, но как-то не так, не там и пока не двигаются. Будем это дело понемногу улучшать. Во-первых, добавим в код корневой элемент Rectangle (для этого нам придётся импортировать модуль QtQuick 1.0), который позволит нам задать размер окошка:
Для Rectangle мы задали начальные размеры и цвет, а для Viewport — сказали, что он должен быть растянут на весь размер родителя. Результат:

Окошко верного размера, но смотрим мы на объект всё еще откуда-то снизу. Поправим положение камеры. Для этого задейстуем свойство camera у нашего Viewport:
Полный код на данном этапе
Свойство eye определяет, где находится камера, а center — точку, на которую она смотрит. Текущий результат:

А ведь уже неплохо! Если бы мы хотели просто посмотреть на 3D объект в Qt Quick3D — можно было бы на этом и закончить. Но нет! Наша цель — движение.
Итак, у нас есть отображающаяся модель холодильника, которая на самом деле 3 модели в одной сцене. Постараемся сделать так, чтобы по клику дверца открывалась и оттуда вылетало НЛО. Прежде всего — обработчик клика. Это просто, добавляем к Viewport элемент MouseArea, растянутый на весь размер родителя. По клику пропишем пока выход (просто чтобы проверить, что работает):
Полный код на данном этапе
Запускаем, кликаем — программа завершается. Работает.
Начнем с открывающейся дверки. Для того, чтобы её открыть и закрыть, нам нужно сделать следующие вещи:
1. Создать элемент Rotation3D, описывающий, как именно (вокруг каких осей) будет вращаться некий объект.
2. Создать элемент SequentialAnimation, суть которого будет в двух вызовах Rotation3D (один-для открытия дверки, второй — для закрытия) с разными направлениями поворота.
3. Привязать элемент Rotation3D к Item3D, соответствующему нашей дверке.
4. Вызвать из обработчика клика промежуточную функцию, которая запустит анимацию.
Полный код на данном этапе
Запускаем, кликаем. Бинго!

Дверца открывается и показывает внутри наше НЛО (пока вполне себе мирно висящее). Задача анимации вылета НЛО один-в-один аналогична открытию дверцы. Поэтому без лишних комментариев финальный код:
и еще раз результат:
Все исходники проекта можно взять тут.
Не так давно фреймворк Qt Quick обзавелся дополнением Qt Quick3D, позволяющим полноценно работать с 3D объектами (поддерживается импорт из 3D Max и Blender), совершать над ними различные трансформации, анимации, применять эффекты, ну и вообще по полной использовать возможности лежащего в основе OpenGL. Работает всё это под Symbian, MeeGo, Windows\Linux\MacOs (ну и вообще везде, где Qt есть). В этом топике мы попробуем технологию «на зуб». Писать что-то сложное и серьёзное не хочется, поэтому мы сделаем хабрахолодильник, из которого по клику будет вылетать НЛО.
Сразу результат:
Что нам понадобится
Качаем и устанавливаем Qt Creator, Qt library, Qt Quick3D и Blender. Еще нам понадобятся 3D-модели, которые мы будем использовать. Я взял вот этот холодильник и эту летающую тарелку (всё бесплатное). Ну и логотип Хабра бессовестно стащил.
Поехали
Итак, запускаем Qt Creator и создаём новый проект: Файл->Новый файл или проект->Проект Qt Quick->Интерфейс пользователя на Qt Quick.

Вводим имя проекта, расположения папки, все остальные опции оставляем по-умолчанию.

Новосозданный проект выглядит как-то так:

Оставим пока его в покое.
Подготовка моделей
Открываем Blender и готовим в нём модели, которые мы будем использовать. Я не буду детально описывать работу в Blender (всё-таки, это не по нему урок, да и не спец я в Blender). Вот вкратце, что мы сделаем:
- Откроем модели холодильника и НЛО.
- Поместим НЛО в холодильник.
- Поцепим на холодильник логотип Хабра.
- Экспортируем из Blender отдельно НЛО (которое сейчас в холодильнике), дверцу холодильника (она нужна нам отдельно, чтобы её можно было открывать) и его корпус без дверцы. И того у нас должно быть на выходе 4 файла: ufo.3ds, door.3ds, refr.3ds + файл текстуры. Скопируем все эти файлы в папку проекта.
Hello world
Возвращаемся в Qt Creator к нашему проекту. При его создании в файл HabraHolod.qml был записан такой себе «Hello world», но он никакого отношения к Qt Quick3D не имеет, а потому мы его удалим. Начнём писать свой qml-код с нуля. В первой итерации он будет вот таким:
import Qt3D 1.0
Viewport {
Mesh { id: refrigirator; source: "refr.3ds" }
Mesh { id: ufo; source: "ufo.3ds" }
Mesh { id: bottom_door; source: "door.3ds" }
Item3D { mesh: refrigirator }
Item3D { mesh: ufo;}
Item3D { mesh: bottom_door; }
}
Давайте пройдемся по коду. В первой строке мы импортируем пакет Qt3D, который, если Вы всё установили верно, должен найтись и взяться за работу по отображению 3D-объектов. Если же Вы, как и я, что-то на этапе установки запороли (у меня была установлена другая версия библиотек Qt), то вот очень толковая статья, описывающая что и куда нужно положить руками, чтобы всё работало и еще одна, объясняющая как скомпилировать Qt Quick3D вручную.
Далее мы создаём элемент Viewport — это контейнер, в который можно добавлять 3D-объекты и задавать некоторые параметры их отображения (освещение, позицию камеры и т.д.). Дальше мы импортируем 3 наших 3ds-файла (каждый — в отдельный мэш) и создаём 3 элемента Item3D (обратите внимание на их связку с мэшами по свойству id).
Если Вы нажмёте Ctrl+R (запуск), то даже сможете увидеть результат:

Что-то странное, да? :) На самом деле всё работает верно. Всё дело в том, что пока ни окно нашего приложения, ни параметры 3D-сцены не настроены и поэтому мы смотрим на наши объекты в малюсенькое окошко с непонятной позиции. Если Вы вручную сделаете окно побольше и воспользуетесь скролингом, то увидите нашу сцену примерно такой:

Начало неплохое — в 10 строк кода у нас уже есть приложение, кое-как отображающее группу 3D-объектов (а ну-ка, сколько строк будет в нём, если написать его на С++\Java\.NET\Ваш_язык?).
Идём дальше
Итак, объекты наши, конечно, отображаются, но как-то не так, не там и пока не двигаются. Будем это дело понемногу улучшать. Во-первых, добавим в код корневой элемент Rectangle (для этого нам придётся импортировать модуль QtQuick 1.0), который позволит нам задать размер окошка:
import QtQuick 1.0
import Qt3D 1.0
Rectangle {
color: "black"
width: 400
height: 600
Viewport {
anchors.fill: parent
Mesh { id: refrigirator; source: "refr.3ds" }
Mesh { id: ufo; source: "ufo.3ds" }
Mesh { id: bottom_door; source: "door.3ds" }
Item3D { mesh: refrigirator }
Item3D { mesh: ufo;}
Item3D { mesh: bottom_door; }
}
}
Для Rectangle мы задали начальные размеры и цвет, а для Viewport — сказали, что он должен быть растянут на весь размер родителя. Результат:

Окошко верного размера, но смотрим мы на объект всё еще откуда-то снизу. Поправим положение камеры. Для этого задейстуем свойство camera у нашего Viewport:
camera: Camera {
id: viewCamera
eye: Qt.vector3d(15,10,40)
center: Qt.vector3d(-2,10,0)
}
Полный код на данном этапе
Свойство eye определяет, где находится камера, а center — точку, на которую она смотрит. Текущий результат:

А ведь уже неплохо! Если бы мы хотели просто посмотреть на 3D объект в Qt Quick3D — можно было бы на этом и закончить. Но нет! Наша цель — движение.
Обработчик клика
Итак, у нас есть отображающаяся модель холодильника, которая на самом деле 3 модели в одной сцене. Постараемся сделать так, чтобы по клику дверца открывалась и оттуда вылетало НЛО. Прежде всего — обработчик клика. Это просто, добавляем к Viewport элемент MouseArea, растянутый на весь размер родителя. По клику пропишем пока выход (просто чтобы проверить, что работает):
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
Полный код на данном этапе
Запускаем, кликаем — программа завершается. Работает.
Трансформации и анимации
Начнем с открывающейся дверки. Для того, чтобы её открыть и закрыть, нам нужно сделать следующие вещи:
1. Создать элемент Rotation3D, описывающий, как именно (вокруг каких осей) будет вращаться некий объект.
Rotation3D {
id: doorOpen
angle: 0
axis: Qt.vector3d(0, 1, 0)
origin: Qt.vector3d(-3, 0, 0)
}
2. Создать элемент SequentialAnimation, суть которого будет в двух вызовах Rotation3D (один-для открытия дверки, второй — для закрытия) с разными направлениями поворота.
SequentialAnimation { id: doorOpenAndClose;
NumberAnimation { target: doorOpen; property: "angle"; from: 0; to : -80.0; duration: 800; easing.type: Easing.OutBounce}
NumberAnimation { target: doorOpen; property: "angle"; from: -80; to : 0.0; duration: 1200; easing.type: Easing.OutCubic}
}
3. Привязать элемент Rotation3D к Item3D, соответствующему нашей дверке.
Item3D { mesh: bottom_door; transform: [doorOpen] }
4. Вызвать из обработчика клика промежуточную функцию, которая запустит анимацию.
MouseArea {
anchors.fill: parent
onClicked: {
fullScene.openDoor();
}
}
...
Item3D {
id: fullScene
function openDoor() {
doorOpenAndClose.loops = 1;
doorOpenAndClose.start();
}
...
}
Полный код на данном этапе
Запускаем, кликаем. Бинго!

Дверца открывается и показывает внутри наше НЛО (пока вполне себе мирно висящее). Задача анимации вылета НЛО один-в-один аналогична открытию дверцы. Поэтому без лишних комментариев финальный код:
import QtQuick 1.0
import Qt3D 1.0
Rectangle {
color: "black"
width: 400
height: 600
Viewport {
anchors.fill: parent
MouseArea {
anchors.fill: parent
onClicked: {
fullScene.openDoor();
}
}
camera: Camera {
id: viewCamera
eye: Qt.vector3d(15,10,40)
center: Qt.vector3d(-2,10,0)
}
Item3D {
id: fullScene
function openDoor(){
doorOpenAndClose.loops = 1;
doorOpenAndClose.start();
ufoFlyOutAndTeleportBack.loops = 1;
ufoFlyOutAndTeleportBack.start();
}
Mesh { id: refrigirator; source: "refr.3ds" }
Mesh { id: ufo; source: "ufo.3ds" }
Mesh { id: bottom_door; source: "door.3ds" }
Item3D { mesh: refrigirator }
Item3D { mesh: ufo; transform: [ufoFlyOut]}
Item3D { mesh: bottom_door; transform: [doorOpen] }
// ------------------ Transform + Animations ------------------
Rotation3D {
id: doorOpen
angle: 0
axis: Qt.vector3d(0, 1, 0)
origin: Qt.vector3d(-3, 0, 0)
}
Rotation3D {
id: ufoFlyOut
angle: 0
axis: Qt.vector3d(0, 3, -1)
origin: Qt.vector3d(10, 0, 0)
}
SequentialAnimation { id: doorOpenAndClose;
NumberAnimation { target: doorOpen; property: "angle"; from: 0; to : -80.0; duration: 800; easing.type: Easing.OutBounce}
NumberAnimation { target: doorOpen; property: "angle"; from: -80; to : 0.0; duration: 1200; easing.type: Easing.OutCubic}
}
SequentialAnimation { id: ufoFlyOutAndTeleportBack;
NumberAnimation { target: ufoFlyOut; property: "angle"; from: 0; to : 100.0; duration: 1700; easing.type: Easing.OutCurve}
NumberAnimation { target: ufoFlyOut; property: "angle"; from: 100; to : 0.0; duration: 0; easing.type: Easing.OutCubic}
}
}
}
}
и еще раз результат:
Все исходники проекта можно взять тут.
Полезные материалы по теме
- Представление Qt Quick3D и ссылки на загрузку под разные платформы
- Клёвый туториал по созданию модели машинки
- Официальная документация по Qt Quick3D