29 ноября 2011 в 02:15

Введение в Qt Quick3D tutorial

Qt*
Этот пост участвует в конкурсе „Умные телефоны за умные посты“
Не так давно фреймворк 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). Вот вкратце, что мы сделаем:
  1. Откроем модели холодильника и НЛО.
  2. Поместим НЛО в холодильник.
  3. Поцепим на холодильник логотип Хабра.
  4. Экспортируем из 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}
            }
        }
    }
}

и еще раз результат:


Все исходники проекта можно взять тут.

Полезные материалы по теме

  1. Представление Qt Quick3D и ссылки на загрузку под разные платформы
  2. Клёвый туториал по созданию модели машинки
  3. Официальная документация по Qt Quick3D
+57
8013
111
tangro 404,1

Комментарии (58)

+13
limonchik, #
А симпатично вышло, с холодильничком. И правда, как подумаю, сколько такое писать на чистом С++ — это ужас. Всё, вот прямо завтра сажусь изучать Qt Quick.
+2
rokemoon, #
Действительно впечатляет, что-то Qt Quick 3D прошел не много мимо меня. Надо бы заняться этим вопросом.
@tangro спасибо за статью.
+2
tangro, #
Всегда рад оказаться полезным.
+1
wholeman, #
Если на Qt3D, на основе которого сделан Qt Quick3D, то больше в 2-3 раза.
+1
skynoname, #
Зачем на чистом С++, когда можно подцепить тот же OpenGL? Строчек кода может быть и выйдет немного больше (потратится на всякие декларации и инициализацию переменных), а так все абсолютно идиентично
0
tangro, #
Мне кажется даже с прицепленным OpenGL будет в несколько раз больше кода. А это время, что, как мы знаем, нынче самый ценный ресурс.
0
TheHorse, #
И не близко. В n раз больше будет, сами обработки контекста и окна займут кучу времени и кода.
+2
QmlPortal, #
Спасибо, весьма интересно! А не пробовали запускать на мобильных устройствах? Как там, интересно, с производительностью у QtQuick 3D?
НЛО прилетело и опубликовало эту надпись здесь
0
tangro, #
Извините, реального устройства у меня, к сожалению, пока нет.
+2
bukt, #
>>сколько строк будет в нём, если написать его на С++\Java\.NET\Ваш_язык
Если написать библиотеку, размером с QtQuick3D — то три :)
А а деле: синтаксис мне очень понравился. Этакий программирующий JSON
+1
skynoname, #
Да, ты прав, а еще есть прикольный язык Java 3D, очень давно думал по нему написать статью, но кармы не хватает ( Если вдруг произойдет чудо, то обязательно отпишу! Вообще, 3D-технологии — это то, что хоть как-то заставляет меня сидеть днями и программировать ))
+1
tangro, #
Плюсанул Вам карму. По идее теперь Вы можете писать в блог Java.
0
Mezomish, #
>А а деле: синтаксис мне очень понравился. Этакий программирующий JSON

«Программирующий JSON» называется JavaScript ;)
0
bukt, #
ECMA-Script, тогда уж.
+1
Mezomish, #
Я исходил из аббревиатуры JSON.
+1
Zhbert, #
Давно хотел попробовать поиграть с OpenGL в Qt, но останавливало то, что там действительно уйма кода на С++ будет. Спасибо огромное за статью, теперь буду более внимательно смотреть в сторону QtQuick и QtQuick 3D.
+1
tass, #
Спасибо, клевая статья.
Кстати, а как у QtQuick3D сейчас с производительностью?
И еще интересует какой сейчас статус у QtQuick3D. Насколько он функционально полон и чист от багов. Был на секции по нему в этом году на Qt DevDays, но, к сожалению, понял для себя, что слишком мало знаю о 3д, чтобы полностью понимать о чем там говорили (да и уйти пришлось под конец). И как-то в итоге не уловил текущий его статус.
0
tangro, #
Вполне себе рабочая штука, есть сборки под все платформы, производительность нормальная, возможности описаны в статье (и чуть больше можно увидеть на этом видео). Пользуйтесь на здоровье.
+2
iOrange, #
В принципе, такого же результата можно добиться и с помощью WPF, однако синтаксис QtQuick мне намного приятнее. Спасибо вам за статью — будем экспериментировать )
0
HomoLuden, #
Вот если б во времена появления WPF была такая альтернатива, то я б все-таки выбрал Qt. В WPF 3D очень тяжелая (тут я имел в виду декларативный подход с XAML — не программирование через DirectX).
НЛО прилетело и опубликовало эту надпись здесь
0
tass, #
ААаааааа, я тоже хочу такого разбора темы моей конкурсной статьи :( Люди, ау, ну сделайте это…
НЛО прилетело и опубликовало эту надпись здесь
0
tass, #
Дак вы спрашивайте что непонятно. Я все буду рад объяснить более подробно.
А то пост написал и как в пустоту, успели даже AccuWeather обсудить, а по теме почти ничего нет.
+1
tangro, #
Ваш комментарий на отдельный пост тянет.
0
auric, #
Эта статья типо копия туториала, который был опубликован у троллей в labs, только на русском и про холодильник. Ну дословно же с тем, что на видео, но только про холодильник… печалька(
+3
tangro, #
Ну это как слово «хлеб» с 4 ошибками, когда «пиво» получается. В обеих статьях используется Qt Creator, Qt Quick3D, подготовка моделей в Блендере, QML — ну вот и всего общего. Модели свои, обработка своя, код свой, видео своё. Я прям не знаю, как можно статью по Quick3D написать не похоже на тот туториал. А на него я честно в конце ссылочку дал.
–2
auric, #
Извиняюсь, ссылку сначала не увидел. Но это не слово «хлеб» с 4мя ошибками в слове «пиво». Это скорее посмотрел туториал, и написал свой, такой же с блекджеком и… нет, просто с блекджеком.
Я прям не знаю, как можно статью по Quick3D написать не похоже на тот туториал.

Идей для туториалов может быть множество. В самом репозитории демок много. Совершенно не обязательно статья должна быть похожей. Здесь, к примеру, человек весьма интересно написал о проблемах.
0
tangro, #
Вы правы, что идей много и о проблемах можно писать, но я хотел написать именно вводную статью по технологии. Простую (проще и меньше указанного Вами демо), на русском языке, без углубления в детали (ну зачем новичку сразу рассказывать о косяках с нормалями). Что вышло, то вышло. Не спорю, что можно написать и о другом — но это уже будут другие статьи.
+2
Truf, #
AFAIK, Планируется сделать QtQuick 3D штатным модулем Qt 5.x, альфу которого обещают в конце года.
labs.qt.nokia.com/wp-content/uploads/2011/05/Qt5.pdf
page 5
0
tangro, #
Логичный шаг.
+1
tass, #
да, он будет входить туда
+1
auric, #
В репах QtQuick3D видел, что сейчас активно ведется работа по поддержке QML2 в нем.
–1
QtRoS, #
Сразу столько статей, сразу Qt Quick такой хороший, как телефон пообещали…
0
GooRoo, #
Он всегда был хороший. Я на нём писать начал, ещё когда он был бетой.
0
auric, #
дык, QtQuick реально хорош (или ты не согласен?), просто не всегда об этом писать хочется (или не всегда есть что), а тут дополнительная стимуляция.
0
Vass, #
Завидно? :)

А если по делу, чем конкретно вас не устраивает Qt Quick?
+1
tass, #
неправда, статьи не только по квику (хотя он мне и нравится). Что у меня (по внутренностям Харматтана), что у Васса (по НФЦ) темы совсем не связанные с Qt Quick
–2
QtRoS, #
Я, вообще-то, люблю Qt Quick и скорее всего подольше многих из вас его знаю, но это просто стыд, когда статьи идут только после обещанного девайса :( За неделю больше, чем за полтора года…

Неужели ради интереса изучения нового и желания поделиться с другими трудно статью написать?..
+1
tass, #
Ну во-первых, не очень хорошо так говорить про людей, которых не знаешь " скорее всего подольше многих из вас его знаю". А во-вторых, далеко не все пишут на хабр, чо. Я по большей части на свой уютный бложек перебрался. Знаю что часть народа сделали также.
+3
Vass, #
Ух ты, будем мериться у кого знания длиннее?
+1
Mezomish, #
Давайте ;) Нам про QML рассказывали парни из Nokia (специально приезжали демонстрировать) ещё когда он только готовился к выпуску и не был анонсирован публично ;)
+1
Vass, #
Так не честно ;) А кстати это, было примерно когда, я так понимаю, самое начало 2010 или конец 2009 года? А то уже начинает казаться, что QML с нами всегда.
+1
Mezomish, #
В 2009-м дело было.
+3
tangro, #
Во-первых, чем для конечного читателя статья на конкурс отличается от статьи «просто так»?
Во-вторых, у меня вот полсотни статей «просто так», и одна на конкурс. И что?
+1
QtRoS, #
Я даже не сказал «получше», а вы, товарищи, уже так реагируете!

Смысл был в том, что я давно с самого начала слежу за интересом к Qt Quick, и вот в такие вот моменты становится обидно, честно!
0
tass, #
Ну раз следите, значит должны знать что статей на самом деле много в сети. В том числе и на русском языке. Или вы ограничиваетесь только Хабром?
0
Mezomish, #
«Обидно, что статьи появляются только на конкурс! Уж лучше бы вообще тогда не появлялись!» — так, что ли, по-вашему?
0
QtRoS, #
Конечно нет, о чем Вы :) Пользы от них не меньше, просто сам факт обязательной материальной мотивации расстраивает меня
0
Mezomish, #
Ни один человек не будет делать что-либо без мотивации. Мотивация бывает разная. На Хабре их несколько: 1) карма+рейтинг, 2) значочки в профиле, 3) ППА (программа поощрения авторов), теперь вот ещё и конкурс. Для кого-то достаточно первых, для кого-то нет, поэтому нужны дополнительные.

Я так понимаю, вы аналогичным образом выступаете против статей, участвующих в ППА? Ну а фигли — ради денег ведь пишут!
0
GooRoo, #
У меня мотивация в основном была такая: «Хочешь в чём-то разобраться, попробуй объяснить это другим». Можно добавить в список )
0
Mezomish, #
Разумеется, мой список не полный. В него можно ещё добавить «стремление потешить ЧСВ», «желание потроллить приверженцев чего-либо», «прорекламировать продукт/услуги» и так далее…
0
GooRoo, #
О, как же это я забыл про ЧСВ и троллинг? :) Надо добавить тоже, да.
0
GooRoo, #
Спасибо за статью.

Из Maya сцены не импортирует, да? Придётся через колладу всё это проворачивать.
0
Gorthauer87, #
Не удержался
www.youtube.com/watch?v=cmb6pTj67Nk
–1
Nashev, #
Как-то оно подозрительно напоминает библиотеку FireMonkey, продвигаемую нынче с Delphi… Случаем, не ею авторы вдохновлялись?
+1
auric, #
QtQuick появился в задумке задолго до покупки Qt Nokia. QtScript, логическим продолжением коего он является, был в Qt версии с 3й. Анимации разрабатывались 2,5 года назад, как и StateMachine. А FireMonkey, что это вообще такое? Появилась она совсем недавно, на сколько я знаю.

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