summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2019-03-21 12:43:03 +0200
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2019-03-26 11:31:55 +0000
commitcc2c27f625c2c7f6c0f4a0951833ddd9a7bb9d76 (patch)
tree63464206015ca489caa332ffb3784fae8ee78726
parentff0dba0dbfde3b9f34db4d89a7f94a531bbe547d (diff)
Revert "Remove legacy viewer"
This reverts commit 4193fc4f00c2e687ca82de589603c7fa133bff50. Also made the legacy viewer button show up by default. The viewer doesn't currently show materials correctly, that will be fixed in a separate patch. Task-number: QT3DS-3198 Change-Id: I079eb31d9240abde496d91edc9f5611c3fbbe6a7 Reviewed-by: Jere Tuliniemi <jere.tuliniemi@qt.io> Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
-rw-r--r--doc/src/03-studio/0-menu.qdoc4
-rw-r--r--qt3dstudio.pro45
-rw-r--r--src/Authoring/Client/Code/Core/Utility/StudioPreferences.cpp20
-rw-r--r--src/Authoring/Client/Code/Core/Utility/StudioPreferences.h3
-rw-r--r--src/Authoring/Studio/MainFrm.cpp11
-rw-r--r--src/Authoring/Studio/MainFrm.ui16
-rw-r--r--src/Authoring/Studio/PreviewHelper.h2
-rw-r--r--src/Authoring/Studio/UI/StudioAppPrefsPage.cpp9
-rw-r--r--src/Authoring/Studio/UI/StudioAppPrefsPage.ui31
-rw-r--r--src/Viewer/Qt3DViewer/Qt3DViewer.pro48
-rw-r--r--src/Viewer/Qt3DViewer/Viewer.qrc14
-rw-r--r--src/Viewer/Qt3DViewer/android/AndroidManifest.xml77
-rw-r--r--src/Viewer/Qt3DViewer/main.cpp367
-rw-r--r--src/Viewer/Qt3DViewer/qml/StyledButton.qml52
-rw-r--r--src/Viewer/Qt3DViewer/qml/StyledMenu.qml95
-rw-r--r--src/Viewer/Qt3DViewer/qml/StyledMenuButton.qml71
-rw-r--r--src/Viewer/Qt3DViewer/qml/StyledMenuItem.qml159
-rw-r--r--src/Viewer/Qt3DViewer/qml/StyledMenuSeparator.qml47
-rw-r--r--src/Viewer/Qt3DViewer/qml/main.qml594
-rw-r--r--src/Viewer/Qt3DViewer/remotedeploymentreceiver.cpp202
-rw-r--r--src/Viewer/Qt3DViewer/remotedeploymentreceiver.h79
-rw-r--r--src/Viewer/Qt3DViewer/resources/images/3D-studio-viewer.icobin0 -> 112324 bytes
-rw-r--r--src/Viewer/Qt3DViewer/resources/images/3D-studio-viewer.svg38
-rw-r--r--src/Viewer/Qt3DViewer/resources/images/arrow.pngbin0 -> 523 bytes
-rw-r--r--src/Viewer/Qt3DViewer/resources/images/arrow@2x.pngbin0 -> 600 bytes
-rw-r--r--src/Viewer/Qt3DViewer/resources/images/check.pngbin0 -> 502 bytes
-rw-r--r--src/Viewer/Qt3DViewer/resources/images/check@2x.pngbin0 -> 638 bytes
-rw-r--r--src/Viewer/Qt3DViewer/resources/images/viewer.icnsbin0 -> 161003 bytes
-rw-r--r--src/Viewer/Qt3DViewer/viewer.cpp401
-rw-r--r--src/Viewer/Qt3DViewer/viewer.h123
-rw-r--r--src/Viewer/Viewer.pro7
-rw-r--r--src/Viewer/qmlviewer/Qt3DSRenderer.cpp344
-rw-r--r--src/Viewer/qmlviewer/Qt3DSRenderer.h92
-rw-r--r--src/Viewer/qmlviewer/Qt3DSView.cpp307
-rw-r--r--src/Viewer/qmlviewer/Qt3DSView.h111
-rw-r--r--src/Viewer/qmlviewer/Qt3DSViewPlugin.cpp81
-rw-r--r--src/Viewer/qmlviewer/Qt3DSViewPlugin.h60
-rw-r--r--src/Viewer/qmlviewer/q3dspresentationitem.cpp102
-rw-r--r--src/Viewer/qmlviewer/q3dspresentationitem.h64
-rw-r--r--src/Viewer/qmlviewer/qmldir3
-rw-r--r--src/Viewer/qmlviewer/qmlviewer.pro34
41 files changed, 3699 insertions, 14 deletions
diff --git a/doc/src/03-studio/0-menu.qdoc b/doc/src/03-studio/0-menu.qdoc
index d949f52f..7ff48cd9 100644
--- a/doc/src/03-studio/0-menu.qdoc
+++ b/doc/src/03-studio/0-menu.qdoc
@@ -110,6 +110,10 @@
\li
\uicontrol{Timeline Snapping Grid} - When using snapping in the
timeline, controls which ticks to snap to.
+ \li
+ \uicontrol{Legacy Viewer} - Enables the legacy viewer preview button in the toolbar. Legacy
+ viewer uses Runtime from Qt 3D Studio 1.1, whereas the default viewer uses Runtime from
+ Qt 3D Studio 2.0. Requires a restart.
\endlist
\li
\uicontrol{Editing View}
diff --git a/qt3dstudio.pro b/qt3dstudio.pro
index 7360423e..b5b3fe5e 100644
--- a/qt3dstudio.pro
+++ b/qt3dstudio.pro
@@ -28,6 +28,32 @@ load(qt_parts)
deploytool = windeployqt
exesuffix = .exe
+ # windeployqt will get confused when deploying viewer 1.0, because some of studio
+ # libraries it links detect as Qt libraries due to their naming scheme.
+ # Since viewer and studio have mostly identical Qt dependencies and both are deployed
+ # to the same directory, we can just omit deployment of viewer in windows and
+ # deploy the missing qml bits separately.
+
+ # Viewer 1.0 needs the studio qml plugin
+ # The assumption is that we are deploying release build in case both are built
+ release {
+ QML_FILE_R = QtStudio3D/declarative_qtstudio3d.dll
+ QMAKE_EXTRA_TARGETS += deployReleaseQml
+ deployTarget.depends += deployReleaseQml
+ deployReleaseQml.depends = mkStudioQmlDir
+ deployReleaseQml.commands = \
+ $$QMAKE_COPY $$shell_quote($$shell_path($$OUT_PWD/qml/$$QML_FILE_R)) \
+ $$shell_quote($$shell_path(\$(DEPLOY_DIR)/$$QML_FILE_R))
+ } else {
+ QML_FILE_D = QtStudio3D/declarative_qtstudio3dd.dll
+ QMAKE_EXTRA_TARGETS += deployDebugQml
+ deployTarget.depends += deployDebugQml
+ deployDebugQml.depends += mkStudioQmlDir
+ deployDebugQml.commands = \
+ $$QMAKE_COPY $$shell_quote($$shell_path($$OUT_PWD/qml/$$QML_FILE_D)) \
+ $$shell_quote($$shell_path(\$(DEPLOY_DIR)/$$QML_FILE_D))
+ }
+
# copy QtStudio3D.dll
release {
QTSTUDIO3D_LIB = Qt5Studio3D.dll
@@ -40,6 +66,18 @@ load(qt_parts)
$$QMAKE_COPY $$shell_quote($$shell_path( \
$$OUT_PWD/bin/$$QTSTUDIO3D_LIB)) \
$$shell_quote($$shell_path($$[QT_INSTALL_BINS]/$$QTSTUDIO3D_LIB))
+
+ QMLDIR_FILE = QtStudio3D/qmldir
+ QMAKE_EXTRA_TARGETS += deployQmldir
+ deployTarget.depends += deployQmldir
+ deployQmldir.depends += mkStudioQmlDir
+ deployQmldir.commands = \
+ $$QMAKE_COPY $$shell_quote($$shell_path($$OUT_PWD/qml/$$QMLDIR_FILE)) \
+ $$shell_quote($$shell_path(\$(DEPLOY_DIR)/$$QMLDIR_FILE))
+
+ QMAKE_EXTRA_TARGETS += mkStudioQmlDir
+ mkStudioQmlDir.commands = \
+ $$sprintf($$QMAKE_MKDIR_CMD, $$shell_quote($$shell_path(\$(DEPLOY_DIR)/QtStudio3D)))
}
qtPrepareTool(DEPLOY_TOOL, $$deploytool)
@@ -49,6 +87,13 @@ load(qt_parts)
$$DEPLOY_TOOL $$shell_quote(\$(DEPLOY_DIR)/Qt3DStudio$${exesuffix}) \
-qmldir=$$shell_quote($$PWD/src/Authoring/Studio/Palettes) $$EXTRA_DEPLOY_OPTIONS
+ macos {
+ # Viewer 1.0
+ deployTarget.commands += && \
+ $$DEPLOY_TOOL $$shell_quote(\$(DEPLOY_DIR)/Qt3DViewer$${exesuffix}) \
+ $$EXTRA_DEPLOY_OPTIONS
+ }
+
greaterThan(QT_MAJOR_VERSION, 5)|greaterThan(QT_MINOR_VERSION, 10) {
# Viewer 2.0
win32 {
diff --git a/src/Authoring/Client/Code/Core/Utility/StudioPreferences.cpp b/src/Authoring/Client/Code/Core/Utility/StudioPreferences.cpp
index 2096bb1d..d9ed32c4 100644
--- a/src/Authoring/Client/Code/Core/Utility/StudioPreferences.cpp
+++ b/src/Authoring/Client/Code/Core/Utility/StudioPreferences.cpp
@@ -205,6 +205,26 @@ void CStudioPreferences::savePreferences()
//==============================================================================
/**
+ * Returns the state of the legacy viewer
+ * @return true if the legacy viewer is active
+ */
+bool CStudioPreferences::IsLegacyViewerActive()
+{
+ return m_preferences->GetValue("LegacyViewerActive", true);
+}
+
+//==============================================================================
+/**
+ * Sets the state of the legacy viewer
+ * @param inActiveFlag true if the legacy viewer is active
+ */
+void CStudioPreferences::SetLegacyViewerActive(bool inActive)
+{
+ m_preferences->SetValue("LegacyViewerActive", inActive);
+}
+
+//==============================================================================
+/**
* Returns the state of the timeline snapping grid
* @return true if the snapping grid is active
*/
diff --git a/src/Authoring/Client/Code/Core/Utility/StudioPreferences.h b/src/Authoring/Client/Code/Core/Utility/StudioPreferences.h
index 4ef185ed..99e2d02f 100644
--- a/src/Authoring/Client/Code/Core/Utility/StudioPreferences.h
+++ b/src/Authoring/Client/Code/Core/Utility/StudioPreferences.h
@@ -53,6 +53,9 @@ public:
static bool IsTimelineSnappingGridActive();
static void SetTimelineSnappingGridActive(bool inActive);
+ static bool IsLegacyViewerActive();
+ static void SetLegacyViewerActive(bool inActive);
+
static ESnapGridResolution GetTimelineSnappingGridResolution();
static void SetTimelineSnappingGridResolution(ESnapGridResolution inResolution);
diff --git a/src/Authoring/Studio/MainFrm.cpp b/src/Authoring/Studio/MainFrm.cpp
index 6cd7b681..da2a5fca 100644
--- a/src/Authoring/Studio/MainFrm.cpp
+++ b/src/Authoring/Studio/MainFrm.cpp
@@ -210,6 +210,16 @@ CMainFrame::CMainFrame()
connect(m_ui->actionRemote_Preview, &QAction::triggered,
this, &CMainFrame::OnPlaybackPreviewRemote);
+ // Only show runtime1 preview if we have appropriate viewer and it's enabled
+ if (CStudioPreferences::IsLegacyViewerActive()
+ && QFileInfo(CPreviewHelper::getViewerFilePath(QStringLiteral("Qt3DViewer"))).exists()) {
+ connect(m_ui->actionPreviewRuntime1, &QAction::triggered,
+ this, &CMainFrame::OnPlaybackPreviewRuntime1);
+ m_ui->actionPreviewRuntime1->setVisible(true);
+ } else {
+ m_ui->actionPreviewRuntime1->setVisible(false);
+ }
+
// Tool mode toolbar
connect(m_ui->actionPosition_Tool, &QAction::triggered, this,
std::bind(&CMainFrame::onTransformToolChanged, this, STUDIO_TOOLMODE_MOVE));
@@ -839,6 +849,7 @@ void CMainFrame::EditPreferences(short inPageIndex)
CStudioPreferences::SetBigTimeAdvanceAmount(CStudioPreferences::DEFAULT_BIG_TIME_ADVANCE);
CStudioPreferences::SetTimelineSnappingGridActive(true);
CStudioPreferences::SetTimelineSnappingGridResolution(SNAPGRID_SECONDS);
+ CStudioPreferences::SetLegacyViewerActive(true);
CStudioPreferences::SetEditViewFillMode(true);
CStudioPreferences::SetPreferredStartupView(
CStudioPreferences::PREFERREDSTARTUP_DEFAULTINDEX);
diff --git a/src/Authoring/Studio/MainFrm.ui b/src/Authoring/Studio/MainFrm.ui
index edce9819..772ff946 100644
--- a/src/Authoring/Studio/MainFrm.ui
+++ b/src/Authoring/Studio/MainFrm.ui
@@ -271,6 +271,7 @@ Project palette using Import functionality.</string>
<addaction name="actionPreview"/>
<addaction name="actionRemote_Preview"/>
<addaction name="actionFilterVariants"/>
+ <addaction name="actionPreviewRuntime1"/>
</widget>
<action name="action_Reference_Manual">
<property name="text">
@@ -865,6 +866,21 @@ Project palette using Import functionality.</string>
<bool>false</bool>
</property>
</action>
+ <action name="actionPreviewRuntime1">
+ <property name="icon">
+ <iconset resource="images.qrc">
+ <normaloff>:/images/playback_tools_play.png</normaloff>:/images/playback_tools_play.png</iconset>
+ </property>
+ <property name="text">
+ <string>Preview with Legacy Qt 3D Viewer 1.1</string>
+ </property>
+ <property name="iconText">
+ <string>Preview with Legacy Qt 3D Viewer 1.1</string>
+ </property>
+ <property name="toolTip">
+ <string>Preview with Legacy Qt 3D Viewer 1.1</string>
+ </property>
+ </action>
<action name="actionImportAssets">
<property name="text">
<string>Import...</string>
diff --git a/src/Authoring/Studio/PreviewHelper.h b/src/Authoring/Studio/PreviewHelper.h
index 77043485..367ca1b8 100644
--- a/src/Authoring/Studio/PreviewHelper.h
+++ b/src/Authoring/Studio/PreviewHelper.h
@@ -72,9 +72,9 @@ public:
const QString &inDocumentFile,
EExecMode inMode, const QString &viewerExeName,
RemoteDeploymentSender *project = 0);
+ static QString getViewerFilePath(const QString &exeName);
protected:
- static QString getViewerFilePath(const QString &exeName);
static void cleanupProcess(QProcess *p, QString *docPath);
static void previewClosed();
diff --git a/src/Authoring/Studio/UI/StudioAppPrefsPage.cpp b/src/Authoring/Studio/UI/StudioAppPrefsPage.cpp
index 08ef1c82..8882a4b0 100644
--- a/src/Authoring/Studio/UI/StudioAppPrefsPage.cpp
+++ b/src/Authoring/Studio/UI/StudioAppPrefsPage.cpp
@@ -90,6 +90,7 @@ void CStudioAppPrefsPage::onInitDialog()
// Add tool tips for controls
m_ui->m_DefaultInterpolation->setToolTip(tr("Set default keyframe interpolation type"));
m_ui->m_checkTimelineAbsoluteSnapping->setToolTip(tr("Enable timeline snapping grid"));
+ m_ui->m_checkLegacyViewer->setToolTip(tr("Enable preview with legacy Qt 3D Viewer 1.1"));
m_ui->m_SnapRangeCombo->setToolTip(tr("Set resolution of timeline snapping grid"));
m_ui->m_buttonRestoreDefaults->setToolTip(tr("Click to restore default Studio settings"));
@@ -118,6 +119,8 @@ void CStudioAppPrefsPage::onInitDialog()
connect(m_ui->m_SnapRangeCombo, activated, this, [=](){ setModified(true); });
connect(m_ui->m_checkTimelineAbsoluteSnapping, &QCheckBox::clicked,
this, [=](){ setModified(true); enableOptions(); });
+ connect(m_ui->m_checkLegacyViewer, &QCheckBox::clicked,
+ this, [=](){ setModified(true); m_restartNeeded = true; });
connect(m_ui->m_EditViewStartupView, activated, this, [=](){ setModified(true); });
connect(m_ui->selectorWidth,
static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
@@ -161,6 +164,9 @@ void CStudioAppPrefsPage::loadSettings()
m_ui->m_checkTimelineAbsoluteSnapping->setChecked(
CStudioPreferences::IsTimelineSnappingGridActive());
+ // Legacy viewer
+ m_ui->m_checkLegacyViewer->setChecked(CStudioPreferences::IsLegacyViewerActive());
+
// Tool handles
m_ui->selectorWidth->setValue(CStudioPreferences::getSelectorLineWidth());
m_ui->selectorLength->setValue(CStudioPreferences::getSelectorLineLength());
@@ -203,6 +209,9 @@ void CStudioAppPrefsPage::saveSettings()
long theCurrentSelection = m_ui->m_SnapRangeCombo->currentIndex();
CStudioPreferences::SetTimelineSnappingGridResolution((ESnapGridResolution)theCurrentSelection);
+ // Legacy viewer
+ CStudioPreferences::SetLegacyViewerActive(m_ui->m_checkLegacyViewer->isChecked());
+
// Preferred Startup View
CStudioPreferences::SetPreferredStartupView(m_ui->m_EditViewStartupView->currentIndex());
diff --git a/src/Authoring/Studio/UI/StudioAppPrefsPage.ui b/src/Authoring/Studio/UI/StudioAppPrefsPage.ui
index 9d724e63..1af60cd3 100644
--- a/src/Authoring/Studio/UI/StudioAppPrefsPage.ui
+++ b/src/Authoring/Studio/UI/StudioAppPrefsPage.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>418</width>
- <height>543</height>
+ <width>347</width>
+ <height>511</height>
</rect>
</property>
<property name="windowTitle">
@@ -20,16 +20,6 @@
<string>General</string>
</property>
<layout class="QGridLayout" name="gridLayout">
- <item row="1" column="0">
- <widget class="QCheckBox" name="m_checkTimelineAbsoluteSnapping">
- <property name="text">
- <string>Timeline Snapping Grid</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="m_DefaultInterpolation"/>
- </item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
@@ -40,9 +30,26 @@
</property>
</widget>
</item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="m_DefaultInterpolation"/>
+ </item>
<item row="1" column="1">
<widget class="QComboBox" name="m_SnapRangeCombo"/>
</item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="m_checkTimelineAbsoluteSnapping">
+ <property name="text">
+ <string>Timeline Snapping Grid</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="m_checkLegacyViewer">
+ <property name="text">
+ <string>Legacy Viewer</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/Viewer/Qt3DViewer/Qt3DViewer.pro b/src/Viewer/Qt3DViewer/Qt3DViewer.pro
new file mode 100644
index 00000000..d8046f46
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/Qt3DViewer.pro
@@ -0,0 +1,48 @@
+include($$PWD/../../Runtime/commoninclude.pri)
+
+TEMPLATE = app
+TARGET = Qt3DViewer
+QT += qml quickcontrols2 studio3d-private
+
+INCLUDEPATH += $$PWD/../qmlviewer
+
+RESOURCES += Viewer.qrc
+RC_ICONS = resources/images/3D-studio-viewer.ico
+
+ICON = resources/images/viewer.icns
+
+SOURCES += \
+ $$PWD/../qmlviewer/Qt3DSView.cpp \
+ $$PWD/../qmlviewer/Qt3DSRenderer.cpp \
+ $$PWD/../qmlviewer/q3dspresentationitem.cpp \
+ main.cpp \
+ viewer.cpp \
+ remotedeploymentreceiver.cpp
+
+HEADERS += \
+ $$PWD/../qmlviewer/Qt3DSView.h \
+ $$PWD/../qmlviewer/Qt3DSRenderer.h \
+ $$PWD/../qmlviewer/q3dspresentationitem.h \
+ viewer.h \
+ remotedeploymentreceiver.h
+
+android: {
+SOURCES += \
+ $$PWD/../studio3d/q3dsviewersettings.cpp \
+ $$PWD/../studio3d/q3dspresentation.cpp \
+ $$PWD/../studio3d/q3dsdatainput.cpp
+
+HEADERS += \
+ $$PWD/../studio3d/q3dsviewersettings.h \
+ $$PWD/../studio3d/q3dspresentation.h \
+ $$PWD/../studio3d/q3dsdatainput.h
+}
+
+LIBS += \
+ -lqt3dsruntime$$qtPlatformTargetSuffix() \
+ -lqt3dsqmlstreamer$$qtPlatformTargetSuffix()
+
+macos:QMAKE_RPATHDIR += @executable_path/../../../../lib
+
+target.path = $$[QT_INSTALL_BINS]
+INSTALLS += target
diff --git a/src/Viewer/Qt3DViewer/Viewer.qrc b/src/Viewer/Qt3DViewer/Viewer.qrc
new file mode 100644
index 00000000..c808dca5
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/Viewer.qrc
@@ -0,0 +1,14 @@
+<RCC>
+ <qresource prefix="/">
+ <file alias="images/arrow.png">resources/images/arrow.png</file>
+ <file alias="images/arrow@2x.png">resources/images/arrow@2x.png</file>
+ <file alias="images/check.png">resources/images/check.png</file>
+ <file alias="images/check@2x.png">resources/images/check@2x.png</file>
+ <file>qml/main.qml</file>
+ <file>qml/StyledMenu.qml</file>
+ <file>qml/StyledMenuButton.qml</file>
+ <file>qml/StyledMenuItem.qml</file>
+ <file>qml/StyledMenuSeparator.qml</file>
+ <file>qml/StyledButton.qml</file>
+ </qresource>
+</RCC>
diff --git a/src/Viewer/Qt3DViewer/android/AndroidManifest.xml b/src/Viewer/Qt3DViewer/android/AndroidManifest.xml
new file mode 100644
index 00000000..3697a9a7
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/android/AndroidManifest.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<manifest package="org.qtproject.qt3dviewer" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.1" android:versionCode="1" android:installLocation="auto">
+ <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:icon="@drawable/icon">
+ <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+
+ <!-- Application arguments -->
+ <!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
+ <!-- Application arguments -->
+
+ <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
+ <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
+ <meta-data android:name="android.app.repository" android:value="default"/>
+ <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
+ <meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
+ <!-- Deploy Qt libs as part of package -->
+ <meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
+ <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
+ <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
+ <!-- Run with local libs -->
+ <meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
+ <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
+ <meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
+ <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
+ <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
+ <!-- Messages maps -->
+ <meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
+ <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
+ <meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
+ <!-- Messages maps -->
+
+ <!-- Splash screen -->
+ <!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
+ <!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
+ <!-- Splash screen -->
+
+ <!-- Background running -->
+ <!-- Warning: changing this value to true may cause unexpected crashes if the
+ application still try to draw after
+ "applicationStateChanged(Qt::ApplicationSuspended)"
+ signal is sent! -->
+ <meta-data android:name="android.app.background_running" android:value="false"/>
+ <!-- Background running -->
+
+ <!-- auto screen scale factor -->
+ <meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
+ <!-- auto screen scale factor -->
+
+ <!-- extract android style -->
+ <!-- available android:values :
+ * full - useful QWidget & Quick Controls 1 apps
+ * minimal - useful for Quick Controls 2 apps, it is much faster than "full"
+ * none - useful for apps that don't use any of the above Qt modules
+ -->
+ <meta-data android:name="android.app.extract_android_style" android:value="full"/>
+ <!-- extract android style -->
+ </activity>
+
+ <!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
+
+ </application>
+
+ <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/>
+ <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
+
+ <!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
+ Remove the comment if you do not require these default permissions. -->
+ <!-- %%INSERT_PERMISSIONS -->
+
+ <!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
+ Remove the comment if you do not require these default features. -->
+ <!-- %%INSERT_FEATURES -->
+
+</manifest>
diff --git a/src/Viewer/Qt3DViewer/main.cpp b/src/Viewer/Qt3DViewer/main.cpp
new file mode 100644
index 00000000..6b020a4f
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/main.cpp
@@ -0,0 +1,367 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 - 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "viewer.h"
+
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qtouchdevice.h>
+#include <QtGui/qscreen.h>
+#include <QtCore/qcommandlineparser.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qtimer.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtStudio3D/q3dssurfaceviewer.h>
+#include <QtStudio3D/private/q3dsimagesequencegenerator_p.h>
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcontext.h>
+
+static QSurfaceFormat findIdealGLVersion()
+{
+ QSurfaceFormat fmt;
+ fmt.setProfile(QSurfaceFormat::CoreProfile);
+
+ // Advanced: Try 4.3 core (so we get compute shaders for instance)
+ fmt.setVersion(4, 3);
+ QOpenGLContext ctx;
+ ctx.setFormat(fmt);
+ if (ctx.create() && ctx.format().version() >= qMakePair(4, 3))
+ return fmt;
+
+ // Basic: Stick with 3.3 for now to keep less fortunate,
+ // Mesa-based systems happy
+ fmt.setVersion(3, 3);
+ ctx.setFormat(fmt);
+ if (ctx.create())
+ return fmt;
+
+ // We tried...
+ return QSurfaceFormat::defaultFormat();
+}
+
+static QSurfaceFormat findIdealGLESVersion()
+{
+ QSurfaceFormat fmt;
+
+ // Advanced: Try 3.1 (so we get compute shaders for instance)
+ fmt.setVersion(3, 1);
+ QOpenGLContext ctx;
+ ctx.setFormat(fmt);
+ if (ctx.create())
+ return fmt;
+
+ // Basic: OpenGL ES 3.0 is a hard requirement at the moment since we can
+ // only generate 300 es shaders, uniform buffers are mandatory.
+ fmt.setVersion(3, 0);
+ ctx.setFormat(fmt);
+ if (ctx.create())
+ return fmt;
+
+ // We tried...
+ return QSurfaceFormat::defaultFormat();
+}
+
+int main(int argc, char *argv[])
+{
+#if defined(Q_OS_MACOS)
+ QSurfaceFormat openGLFormat;
+ openGLFormat.setRenderableType(QSurfaceFormat::OpenGL);
+ openGLFormat.setProfile(QSurfaceFormat::CoreProfile);
+ openGLFormat.setMajorVersion(4);
+ openGLFormat.setMinorVersion(1);
+ openGLFormat.setStencilBufferSize(8);
+ QSurfaceFormat::setDefaultFormat(openGLFormat);
+#endif
+
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QCoreApplication::setOrganizationName("The Qt Company");
+ QCoreApplication::setOrganizationDomain("qt.io");
+ QCoreApplication::setApplicationName("Qt 3D Viewer");
+
+ QGuiApplication a(argc, argv);
+
+#if !defined(Q_OS_MACOS)
+ QSurfaceFormat fmt;
+ if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL)
+ fmt = findIdealGLVersion();
+ else
+ fmt = findIdealGLESVersion();
+ fmt.setDepthBufferSize(24);
+ fmt.setStencilBufferSize(8);
+ QSurfaceFormat::setDefaultFormat(fmt);
+#endif
+
+ QCommandLineParser parser;
+ parser.addHelpOption();
+ parser.addPositionalArgument(
+ "file",
+ QCoreApplication::translate("main", "The presentation file to open."),
+ QCoreApplication::translate("main", "[file]"));
+
+ parser.addOption({"sequence",
+ QCoreApplication::translate("main",
+ "Generates an image sequence.\n"
+ "The file argument must be specified.\n"""
+ "Specifying any of the seq-* arguments\n"
+ "implies setting this option.")});
+ parser.addOption({"seq-start",
+ QCoreApplication::translate("main",
+ "Start time of the sequence in\n"
+ "milliseconds.\n"
+ "The default value is 0."),
+ QCoreApplication::translate("main", "ms"), QString::number(0)});
+ parser.addOption({"seq-end",
+ QCoreApplication::translate("main",
+ "End time of the sequence in\n"
+ "milliseconds.\n"
+ "The default value is 1000."),
+ QCoreApplication::translate("main", "ms"), QString::number(1000)});
+ parser.addOption({"seq-fps",
+ QCoreApplication::translate("main",
+ "Frames per second for the sequence.\n"
+ "The default value is 60."),
+ QCoreApplication::translate("main", "fps"), QString::number(60)});
+ parser.addOption({"seq-interval",
+ QCoreApplication::translate("main",
+ "Time interval between frames in\n"
+ "the sequence in milliseconds. The seq-fps argument is ignored"
+ "if this argument is used."),
+ QCoreApplication::translate("main", "ms"), QString::number(0)});
+ parser.addOption({"seq-width",
+ QCoreApplication::translate("main",
+ "Width of the image sequence.\n"
+ "The default value is 1920."),
+ QCoreApplication::translate("main", "pixels"), QString::number(1920)});
+ parser.addOption({"seq-height",
+ QCoreApplication::translate("main",
+ "Height of the image sequence.\n"
+ "The default value is 1080."),
+ QCoreApplication::translate("main", "pixels"), QString::number(1080)});
+ parser.addOption({"seq-outpath",
+ QCoreApplication::translate("main",
+ "Output path of the image sequence.\n"
+ "The default value is the current directory."),
+ QCoreApplication::translate("main", "path"), QStringLiteral(".")});
+ parser.addOption({"seq-outfile",
+ QCoreApplication::translate("main",
+ "Output filename base for the image\n"
+ "sequence.\n"
+ "The default value is derived from the presentation filename."),
+ QCoreApplication::translate("main", "file"), QStringLiteral("")});
+ parser.addOption({"connect",
+ QCoreApplication::translate("main",
+ "If this parameter is specified, the viewer\n"
+ "is started in connection mode.\n"
+ "The default value is 36000."),
+ QCoreApplication::translate("main", "port"), QString::number(36000)});
+ parser.addOption({"fullscreen",
+ QCoreApplication::translate("main",
+ "Starts the viewer in fullscreen mode.\n")});
+ parser.addOption({"maximized",
+ QCoreApplication::translate("main",
+ "Starts the viewer in maximized mode.")});
+ parser.addOption({"windowgeometry",
+ QCoreApplication::translate("main",
+ "Specifies the initial\n"
+ "window geometry using the X11-syntax.\n"
+ "For example: 1000x800+50+50"),
+ QCoreApplication::translate("main", "geometry"), QStringLiteral("")});
+ parser.addOption({"mattecolor",
+ QCoreApplication::translate("main",
+ "Specifies custom matte color\n"
+ "using #000000 syntax.\n"
+ "For example, white matte: #ffffff"),
+ QCoreApplication::translate("main", "color"), QStringLiteral("#333333")});
+ parser.addOption({"showstats",
+ QCoreApplication::translate("main",
+ "Show render statistics on screen.")});
+ parser.addOption({"scalemode",
+ QCoreApplication::translate("main",
+ "Specifies scaling mode.\n"
+ "The default value is 'center'."),
+ QCoreApplication::translate("main", "center|fit|fill"),
+ QStringLiteral("center")});
+ parser.process(a);
+
+ const QStringList files = parser.positionalArguments();
+ if (files.count() > 1) {
+ qWarning() << "Only one presentation file can be given.";
+ parser.showHelp(-1);
+ }
+
+ bool generateSequence = parser.isSet("sequence") || parser.isSet("seq-start")
+ || parser.isSet("seq-end") || parser.isSet("seq-fps")
+ || parser.isSet("seq-interval") || parser.isSet("seq-width")
+ || parser.isSet("seq-height") || parser.isSet("seq-outpath")
+ || parser.isSet("seq-outfile");
+
+#ifndef Q_OS_ANDROID
+ Q3DSImageSequenceGenerator *generator = nullptr;
+#endif
+ Viewer viewer(generateSequence);
+
+ // Figure out control size multiplier for devices using touch screens to ensure all controls
+ // have minimum usable size.
+ qreal sizeMultiplier = 1.0;
+ const auto touchDevices = QTouchDevice::devices();
+ if (touchDevices.size() > 0) {
+ // Find out the actual screen logical pixel size. Typically touch devices we care about
+ // only have a single screen, so we just check primary screen.
+ const auto screens = QGuiApplication::screens();
+ if (screens.size() > 0) {
+ QScreen *screen = screens.at(0);
+ qreal dpi = screen->physicalDotsPerInch() / screen->devicePixelRatio();
+ sizeMultiplier = dpi / 40.0; // divider chosen empirically
+ }
+ }
+
+ QQmlApplicationEngine engine;
+ // Set import paths so that standalone installation works
+ QString extraImportPath1(QStringLiteral("%1/qml"));
+ engine.addImportPath(extraImportPath1.arg(QGuiApplication::applicationDirPath()));
+#ifdef Q_OS_MACOS
+ QString extraImportPath2(QStringLiteral("%1/../../../../qml"));
+ engine.addImportPath(extraImportPath2.arg(QGuiApplication::applicationDirPath()));
+#endif
+
+ QQmlContext *ctx = engine.rootContext();
+ ctx->setContextProperty(QStringLiteral("_menuBackgroundColor"), QColor("#404244"));
+ ctx->setContextProperty(QStringLiteral("_menuSelectionColor"), QColor("#46a2da"));
+ ctx->setContextProperty(QStringLiteral("_menuBorderColor"), QColor("#727476"));
+ ctx->setContextProperty(QStringLiteral("_dialogBorderColor"), QColor("#404244"));
+ ctx->setContextProperty(QStringLiteral("_dialogBackgroundColor"), QColor("#2e2f30"));
+ ctx->setContextProperty(QStringLiteral("_dialogFieldColor"), QColor("#404244"));
+ ctx->setContextProperty(QStringLiteral("_dialogFieldBorderColor"), QColor("#262829"));
+ ctx->setContextProperty(QStringLiteral("_textColor"), QColor("#ffffff"));
+ ctx->setContextProperty(QStringLiteral("_disabledColor"), QColor("#727476"));
+ ctx->setContextProperty(QStringLiteral("_fontSize"), int(12 * sizeMultiplier));
+ ctx->setContextProperty(QStringLiteral("_controlBaseHeight"), int(24 * sizeMultiplier));
+ ctx->setContextProperty(QStringLiteral("_controlBaseWidth"), int(80 * sizeMultiplier));
+ ctx->setContextProperty(QStringLiteral("_controlPadding"), int(12 * sizeMultiplier));
+ ctx->setContextProperty(QStringLiteral("_viewerHelper"), &viewer);
+ qmlRegisterUncreatableType<Viewer>(
+ "Qt3DStudioViewer", 1, 0, "ViewerHelper",
+ QCoreApplication::translate("main",
+ "Creation of ViewerHelper not allowed from QML"));
+ engine.load(QUrl(QLatin1String("qrc:/qml/main.qml")));
+ Q_ASSERT(engine.rootObjects().size() > 0);
+ QWindow *appWindow = qobject_cast<QWindow *>(engine.rootObjects().at(0));
+ Q_ASSERT(appWindow);
+ viewer.setQmlRootObject(appWindow);
+
+ if (parser.isSet(QStringLiteral("windowgeometry"))) {
+ int width = 1280;
+ int height = 768;
+ int x = 50;
+ int y = 50;
+ QString geometryStr = parser.value(QStringLiteral("windowgeometry"));
+ const QStringList splitPlus = geometryStr.split(QLatin1Char('+'));
+ if (splitPlus.size() > 0) {
+ const QStringList splitX = splitPlus[0].split(QLatin1Char('x'));
+ if (splitX.size() >= 2) {
+ width = splitX[0].toInt();
+ height = splitX[1].toInt();
+ }
+ if (splitPlus.size() >= 3) {
+ x = splitPlus[1].toInt();
+ y = splitPlus[2].toInt();
+ }
+ }
+ appWindow->setGeometry(x, y, width, height);
+ }
+ if (parser.isSet(QStringLiteral("fullscreen")))
+ appWindow->setVisibility(QWindow::FullScreen);
+ else if (parser.isSet(QStringLiteral("maximized")))
+ appWindow->setVisibility(QWindow::Maximized);
+
+ if (parser.isSet(QStringLiteral("mattecolor"))) {
+ QColor matteColor(parser.value("mattecolor"));
+ if (matteColor != Qt::black) {
+ appWindow->setProperty("showMatteColor", QVariant::fromValue<QColor>(matteColor));
+ appWindow->setProperty("matteColor", QVariant::fromValue<QColor>(matteColor));
+ }
+ }
+ if (parser.isSet(QStringLiteral("showstats")))
+ appWindow->setProperty("showRenderStats", true);
+ if (parser.isSet(QStringLiteral("scalemode"))) {
+ QString scaleStr(parser.value("scalemode"));
+ if (scaleStr == QStringLiteral("fit"))
+ appWindow->setProperty("scaleMode", Q3DSViewerSettings::ScaleModeFit);
+ else if (scaleStr == QStringLiteral("fill"))
+ appWindow->setProperty("scaleMode", Q3DSViewerSettings::ScaleModeFill);
+ else
+ appWindow->setProperty("scaleMode", Q3DSViewerSettings::ScaleModeCenter);
+ }
+
+#ifndef Q_OS_ANDROID
+ if (generateSequence) {
+ if (files.count() != 1) {
+ qWarning() << "Presentation file is required for generating an image sequence.";
+ parser.showHelp(-1);
+ }
+ generator = new Q3DSImageSequenceGenerator;
+ QObject::connect(generator, &Q3DSImageSequenceGenerator::progress,
+ &viewer, &Viewer::generatorProgress);
+ QObject::connect(generator, &Q3DSImageSequenceGenerator::finished,
+ &viewer, &Viewer::generatorFinished);
+ viewer.setGeneratorDetails(files.first());
+ generator->generateImageSequence(
+ files.first(),
+ parser.value("seq-start").toDouble(),
+ parser.value("seq-end").toDouble(),
+ parser.value("seq-fps").toDouble(),
+ parser.value("seq-interval").toDouble(),
+ parser.value("seq-width").toInt(),
+ parser.value("seq-height").toInt(),
+ parser.value("seq-outpath"),
+ parser.value("seq-outfile"));
+ } else
+#endif
+ if (!files.isEmpty()) {
+ // Load the presentation after window has been exposed to give QtQuick time to construct
+ // the application window properly
+ QTimer *exposeTimer = new QTimer(appWindow);
+ QObject::connect(exposeTimer, &QTimer::timeout, [&](){
+ if (appWindow->isExposed()) {
+ exposeTimer->stop();
+ exposeTimer->deleteLater();
+ viewer.loadFile(files.first());
+ }
+ });
+ exposeTimer->start(0);
+ } else {
+ viewer.setContentView(Viewer::ConnectView);
+ if (parser.isSet(QStringLiteral("connect")))
+ viewer.setConnectPort(parser.value(QStringLiteral("connect")).toInt());
+ viewer.connectRemote();
+ }
+
+ return a.exec();
+}
diff --git a/src/Viewer/Qt3DViewer/qml/StyledButton.qml b/src/Viewer/Qt3DViewer/qml/StyledButton.qml
new file mode 100644
index 00000000..400179de
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/qml/StyledButton.qml
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.7
+import QtQuick.Controls 2.2
+
+Button {
+ id: control
+ implicitWidth: _controlBaseWidth
+ implicitHeight: _controlBaseHeight
+
+ contentItem: Text {
+ width: _controlBaseWidth
+ text: control.text
+ height: _controlBaseHeight
+ font.pixelSize: _fontSize
+ color: _textColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ background: Rectangle {
+ color: control.down ? _menuSelectionColor : _dialogFieldColor
+ border.color: _dialogFieldBorderColor
+ radius: 2
+ }
+}
diff --git a/src/Viewer/Qt3DViewer/qml/StyledMenu.qml b/src/Viewer/Qt3DViewer/qml/StyledMenu.qml
new file mode 100644
index 00000000..6275dbc6
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/qml/StyledMenu.qml
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.7
+import QtQuick.Controls 2.2
+
+Menu {
+ id: control
+
+ property alias hovered: menuArea.containsMouse
+
+ width: contentItem.width + leftPadding + rightPadding
+ height: contentItem.height + topPadding + bottomPadding
+ padding: 1 // For background border
+ x: 0
+ y: parent.height
+ closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnEscape
+
+ contentItem: MouseArea {
+ id: menuArea
+ hoverEnabled: true
+ height: list.height
+ width: list.width
+ ListView {
+ id: list
+ boundsBehavior: Flickable.StopAtBounds
+ clip: true
+ model: control.contentModel
+ currentIndex: control.currentIndex
+ highlightRangeMode: ListView.ApplyRange
+ highlightMoveDuration: 0
+ Component.onCompleted: {
+ var maxItemWidth = 0;
+ var maxShortcutWidth = 0;
+ var totalHeight = 0
+ var extraWidth = 0
+ var i;
+ for (i = control.contentData.length - 1; i >= 0; --i) {
+ if (control.contentData[i].itemWidth !== undefined) {
+ maxItemWidth = Math.max(maxItemWidth, control.contentData[i].itemWidth);
+ maxShortcutWidth = Math.max(maxShortcutWidth,
+ control.contentData[i].shortcutWidth);
+ }
+ totalHeight += control.contentData[i].height
+ }
+ maxItemWidth += _controlPadding // minimum item spacer
+ for (i = control.contentData.length - 1; i >= 0; --i) {
+ if (control.contentData[i].itemSpacerWidth !== undefined) {
+ control.contentData[i].itemSpacerWidth
+ = maxItemWidth - control.contentData[i].itemWidth;
+ control.contentData[i].shortcutSpacerWidth
+ = maxShortcutWidth - control.contentData[i].shortcutWidth;
+ }
+ }
+ width = maxItemWidth + maxShortcutWidth + extraWidth
+ + control.contentData[0].leftPadding + control.contentData[0].rightPadding
+ + control.contentData[0].arrowWidth + control.contentData[0].checkMarkWidth;
+ height = totalHeight
+ }
+ }
+ }
+
+ background: Rectangle {
+ width: control.width
+ height: control.height
+ color: _menuBackgroundColor
+ border.color: _menuBorderColor
+ }
+}
diff --git a/src/Viewer/Qt3DViewer/qml/StyledMenuButton.qml b/src/Viewer/Qt3DViewer/qml/StyledMenuButton.qml
new file mode 100644
index 00000000..b5290c3d
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/qml/StyledMenuButton.qml
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.7
+import QtQuick.Controls 2.2
+
+Button {
+ id: control
+
+ property Menu menu: null
+ property ApplicationWindow window: null
+
+ onPressed: {
+ if (menu.visible)
+ menu.close();
+ else
+ menu.open();
+ }
+
+ onHoveredChanged: {
+ if (hovered && window.menuOpen) {
+ window.closeMenus();
+ menu.open();
+ }
+ }
+
+ hoverEnabled: true
+ width: contentItem.contentWidth + leftPadding + rightPadding
+ leftPadding: _controlPadding
+ rightPadding: _controlPadding
+ height: _controlBaseHeight
+ contentItem: Text {
+ text: control.text
+ font.pixelSize: _fontSize
+ opacity: enabled ? 1.0 : 0.3
+ color: _textColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ background: Rectangle {
+ opacity: enabled ? 1 : 0.3
+ color: control.down || control.hovered
+ ? _menuSelectionColor : _menuBackgroundColor
+ }
+}
diff --git a/src/Viewer/Qt3DViewer/qml/StyledMenuItem.qml b/src/Viewer/Qt3DViewer/qml/StyledMenuItem.qml
new file mode 100644
index 00000000..79049a90
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/qml/StyledMenuItem.qml
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.7
+import QtQuick.Controls 2.2
+
+MenuItem {
+ id: control
+
+ property alias shortcut: shortcut.sequence
+ property string shortcutText
+ property alias itemSpacerWidth: itemSpacer.width
+ property alias shortcutSpacerWidth: shortcutSpacer.width
+ property alias itemWidth: itemLabel.width
+ property alias shortcutWidth: shortcutLabel.width
+ property bool showArrow: false
+ property int arrowWidth: arrow.width
+ property int checkMarkWidth: checkMark.width
+ property bool showCheckMark: false
+ property Menu arrowMenu: null
+
+ hoverEnabled: true
+ width: contentItem.width + leftPadding + rightPadding
+ height: contentItem.height + topPadding + bottomPadding
+ padding: 0
+ leftPadding: 0
+ rightPadding: 0
+
+ MouseArea {
+ anchors.fill: parent
+ onPressed: {
+ if (showArrow) {
+ if (!arrowMenu.visible) {
+ arrowMenuDelay.stop();
+ arrowMenu.open();
+ }
+ } else {
+ mouse.accepted = false;
+ }
+ }
+ }
+
+ onHoveredChanged: {
+ if (showArrow)
+ arrowMenuDelay.start();
+ }
+
+ Timer {
+ id: arrowMenuDelay
+ interval: 500
+ repeat: false
+ onTriggered: {
+ if (arrowMenu.visible) {
+ if (!control.hovered && !arrowMenu.hovered)
+ arrowMenu.close();
+ } else {
+ if (control.hovered)
+ arrowMenu.open();
+ }
+ }
+ }
+
+ Shortcut {
+ id: shortcut
+ context: Qt.ApplicationShortcut
+ onActivated: control.triggered()
+ }
+
+ contentItem: Row {
+ width: checkMark.width + itemLabel.width + itemSpacer.width
+ + shortcutLabel.width + shortcutSpacer.width + arrow.width
+ height: _controlBaseHeight
+ Item {
+ id: checkMark
+ width: 16
+ height: _controlBaseHeight
+ Image {
+ anchors.fill: parent
+ visible: control.showCheckMark
+ fillMode: Image.Pad
+ source: "qrc:/images/check.png"
+ }
+ }
+ Label {
+ id: itemLabel
+ text: control.text
+ font.pixelSize: _fontSize
+ horizontalAlignment: Text.AlignLeft
+ color: control.enabled ? _textColor : _disabledColor
+ verticalAlignment: Text.AlignVCenter
+ clip: true
+ width: contentWidth
+ height: _controlBaseHeight
+ }
+ Item {
+ id: itemSpacer
+ width: _controlPadding
+ height: _controlBaseHeight
+ }
+ Label {
+ id: shortcutLabel
+ text: shortcut.nativeText === "" ? control.shortcutText : shortcut.nativeText
+ font.pixelSize: _fontSize
+ horizontalAlignment: Text.AlignLeft
+ color: control.enabled ? _textColor : _disabledColor
+ verticalAlignment: Text.AlignVCenter
+ clip: true
+ width: contentWidth
+ height: _controlBaseHeight
+ }
+ Item {
+ id: shortcutSpacer
+ width: 0
+ height: _controlBaseHeight
+ }
+ Item {
+ id: arrow
+ width: 16
+ height: _controlBaseHeight
+ Image {
+ anchors.fill: parent
+ visible: control.showArrow
+ fillMode: Image.Pad
+ source: "qrc:/images/arrow.png"
+ }
+ }
+ }
+ background: Rectangle {
+ width: control.width
+ height: control.height
+ color: control.hovered ? _menuSelectionColor : _menuBackgroundColor
+ }
+}
diff --git a/src/Viewer/Qt3DViewer/qml/StyledMenuSeparator.qml b/src/Viewer/Qt3DViewer/qml/StyledMenuSeparator.qml
new file mode 100644
index 00000000..344d6678
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/qml/StyledMenuSeparator.qml
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.7
+import QtQuick.Controls 2.2
+
+MenuSeparator {
+ id: control
+ padding: 0
+ topPadding: 0
+ bottomPadding: 0
+ leftPadding: 0
+ rightPadding: 0
+ width: parent.width
+ height: 1
+ contentItem: Rectangle {
+ width: control.width - control.leftPadding - control.rightPadding
+ height: 1
+ color: _menuBorderColor
+ }
+}
diff --git a/src/Viewer/Qt3DViewer/qml/main.qml b/src/Viewer/Qt3DViewer/qml/main.qml
new file mode 100644
index 00000000..8d9a2015
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/qml/main.qml
@@ -0,0 +1,594 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.7
+import QtQuick.Controls 2.2
+import QtQuick.Dialogs 1.2
+import Qt3DStudioViewer 1.0
+import QtStudio3D 1.0
+import QtQuick.Window 2.2
+
+ApplicationWindow {
+ id: window
+ width: 1280
+ height: 768
+ visible: true
+ title: qsTr("Qt 3D Studio Viewer")
+
+ property bool menuOpen: fileMenu.visible || viewMenu.visible
+ property Item loadedContent: contentLoader ? contentLoader.item : null
+ property string error
+ property int previousVisibility
+
+ property color showMatteColor: Qt.rgba(0.2, 0.2, 0.2, 1)
+ property color hideMatteColor: Qt.rgba(0, 0, 0, 1)
+ property color matteColor: hideMatteColor
+ property bool showRenderStats: false
+ property int scaleMode: ViewerSettings.ScaleModeCenter
+
+ function closeMenus() {
+ fileMenu.close();
+ scaleMenu.close();
+ viewMenu.close();
+ }
+
+ Component.onCompleted: {
+ _viewerHelper.restoreWindowState(window);
+ previousVisibility = visibility;
+ }
+
+ onClosing: {
+ _viewerHelper.storeWindowState(window);
+ }
+
+ Timer {
+ id: infoTimer
+ repeat: false
+ interval: 5000
+
+ onTriggered: {
+ infoOverlay.visible = false;
+ }
+ }
+
+ Rectangle {
+ id: infoOverlay
+ visible: false
+ color: "black"
+ border.color: _dialogBorderColor
+ x: parent.width * 0.2
+ y: parent.height * 0.4
+ width: parent.width * 0.6
+ height: parent.height * 0.2
+ z: 20
+ Label {
+ id: infoLabel
+ anchors.fill: parent
+ color: _textColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: window.width / 40
+ }
+ }
+
+ Connections {
+ target: _viewerHelper
+ onShowInfoOverlay: {
+ // Show a brief info overlay
+ infoLabel.text = infoStr;
+ infoOverlay.visible = true;
+ infoTimer.restart();
+ }
+ }
+
+ MouseArea {
+ property int swipeStart: 0
+
+ anchors.fill: parent
+ z: 10
+ enabled: !ipEntry.visible
+
+ onPressed: {
+ if (window.visibility === Window.FullScreen)
+ swipeStart = mouse.y;
+ _viewerHelper.handleMousePress(mouse.x, mouse.y, mouse.button, mouse.buttons,
+ mouse.modifiers);
+ mouse.accepted = true;
+ }
+ onReleased: {
+ _viewerHelper.handleMouseRelease(mouse.x, mouse.y, mouse.button, mouse.buttons,
+ mouse.modifiers);
+ mouse.accepted = true;
+ }
+ onPositionChanged: {
+ // Swipe down to exit fullscreen mode
+ if (window.visibility === Window.FullScreen && mouse.y > swipeStart + (height / 8)) {
+ window.visibility = window.previousVisibility;
+ } else {
+ _viewerHelper.handleMouseMove(mouse.x, mouse.y, mouse.button, mouse.buttons,
+ mouse.modifiers);
+ }
+ }
+ }
+
+ DropArea {
+ anchors.fill: parent
+ onEntered: {
+ if (drag.hasUrls) {
+ var filename = _viewerHelper.convertUrlListToFilename(drag.urls);
+ if (filename === "")
+ drag.accepted = false;
+ }
+ }
+ onDropped: {
+ if (drop.hasUrls) {
+ var filename = _viewerHelper.convertUrlListToFilename(drop.urls);
+ if (filename === "")
+ drag.accepted = false;
+ else
+ _viewerHelper.loadFile(filename);
+ }
+ }
+ }
+
+ Loader {
+ id: contentLoader
+ anchors.fill: parent
+ sourceComponent: {
+ switch (_viewerHelper.contentView) {
+ case ViewerHelper.StudioView:
+ return studioContent;
+ case ViewerHelper.ConnectView:
+ return connectContent;
+ case ViewerHelper.SequenceView:
+ return sequenceContent;
+ default:
+ return emptyContent;
+ }
+ }
+ Timer {
+ id: asyncContentChanger
+ repeat: false
+ interval: 0
+ property int view: ViewerHelper.DefaultView
+ function changeView(newView) {
+ view = newView;
+ start();
+ }
+
+ onTriggered: {
+ _viewerHelper.contentView = view;
+ }
+ }
+ }
+
+ Component {
+ id: emptyContent
+ Rectangle {
+ color: "black"
+ Label {
+ anchors.fill: parent
+ text: window.error
+ color: _textColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: width / 80
+ }
+ }
+ }
+
+ Component {
+ id: studioContent
+ Studio3D {
+ id: studio3D
+
+ property alias hiderVisible: hider.visible
+
+ focus: true
+ ViewerSettings {
+ matteColor: window.matteColor
+ showRenderStats: window.showRenderStats
+ scaleMode: window.scaleMode
+ }
+
+ // Hider item keeps the Studio3D hidden until it starts running and we reset the
+ // animation time to the start
+ Rectangle {
+ id: hider
+ color: "black"
+ anchors.fill: parent
+ }
+
+ Timer {
+ id: revealTimer
+ repeat: false
+ interval: 0
+ onTriggered: {
+ hider.visible = false;
+ }
+ }
+
+ onRunningChanged: {
+ if (running) {
+ // Successfully opened a presentation, update the open folder
+ _viewerHelper.openFolder = presentation.source.toString();
+ // Force the animation to start from the beginning, as the first frame render
+ // can take some time as shaders are compiled on-demand
+ // Localization note: "Scene" needs to be the same as the default "Scene"
+ // element name generated in the Studio application.
+ presentation.goToTime(qsTr("Scene"), 0);
+ revealTimer.start();
+ }
+ }
+ onErrorChanged: {
+ if (error.length > 0) {
+ window.error = error;
+ asyncContentChanger.changeView(ViewerHelper.DefaultView);
+ }
+ }
+ }
+ }
+
+ Component {
+ id: connectContent
+ Rectangle {
+ color: "black"
+ Label {
+ anchors.fill: parent
+ text: _viewerHelper.connectText
+ color: _textColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: width / 40
+ }
+ }
+ }
+
+ Component {
+ id: sequenceContent
+ Rectangle {
+ property alias mainText: mainLabel.text
+ property alias detailsText: detailsLabel.text
+ color: "black"
+ Item {
+ anchors.fill: parent
+ Label {
+ id: mainLabel
+ color: _textColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignBottom
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottomMargin: _controlPadding
+ height: parent.height / 2
+ font.pixelSize: width / 40
+ text: qsTr("Image sequence generation initializing...")
+ }
+ Label {
+ id: detailsLabel
+ color: _textColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignTop
+ anchors.top: mainLabel.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.topMargin: _controlPadding
+ height: parent.height / 2
+ font.pixelSize: width / 50
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: ipEntry
+ visible: false
+ color: _dialogBackgroundColor
+ border.color: _dialogBorderColor
+ y: (parent.height - height) / 2
+ x: (parent.width - width) / 2
+ z: 100
+ width: connectionEntry.width + (2 * _controlPadding)
+ height: connectionEntry.height + (2 * _controlPadding)
+
+ onVisibleChanged: {
+ if (visible) {
+ connectText.forceActiveFocus();
+ connectText.selectAll();
+ }
+ }
+
+ Grid {
+ id: connectionEntry
+ spacing: _controlPadding
+ columns: 2
+ y: _controlPadding
+ x: _controlPadding
+ Label {
+ id: ipEntryLabel
+ width: _controlBaseWidth
+ height: _controlBaseHeight
+ text: qsTr("Enter IP port:")
+ color: _textColor
+ font.pixelSize: _fontSize
+ verticalAlignment: Text.AlignVCenter
+ padding: _controlPadding / 2
+ }
+ TextField {
+ id: connectText
+ width: _controlBaseWidth
+ height: _controlBaseHeight
+ font.pixelSize: _fontSize
+ color: _textColor
+ selectByMouse: true
+ padding: _controlPadding / 6
+ enabled: ipEntry.visible
+ text: _viewerHelper.connectPort
+ validator: IntValidator {
+ bottom: 1
+ top: 65535
+ }
+
+ onAccepted: {
+ if (ipEntry.visible) {
+ _viewerHelper.contentView = ViewerHelper.ConnectView;
+ _viewerHelper.connectPort = Number(text);
+ _viewerHelper.connectRemote();
+ ipEntry.visible = false;
+ infoOverlay.visible = false;
+ }
+ }
+
+ background: Rectangle {
+ id: textBackground
+ color: _dialogFieldColor
+ border.width: 1
+ border.color: _dialogFieldBorderColor
+ radius: 2
+ }
+ }
+ StyledButton {
+ id: connectButton
+ width: _controlBaseWidth
+ text: qsTr("Connect")
+ onClicked: {
+ _viewerHelper.contentView = ViewerHelper.ConnectView;
+ _viewerHelper.connectPort = Number(connectText.text);
+ _viewerHelper.connectRemote();
+ ipEntry.visible = false;
+ infoOverlay.visible = false;
+ }
+ }
+ StyledButton {
+ id: cancelButton
+ width: _controlBaseWidth
+ text: qsTr("Cancel")
+ onClicked: {
+ ipEntry.visible = false;
+ }
+ }
+ }
+ }
+
+ Component {
+ id: fileDialogComponent
+ FileDialog {
+ id: fileDialog
+ title: qsTr("Choose Presentation or Project")
+ folder: _viewerHelper.openFolder
+ nameFilters: [qsTr("All supported formats (*.uip *.uia)"),
+ qsTr("Studio UI Presentation (*.uip)"),
+ qsTr("Application file (*.uia)")]
+ onAccepted: {
+ _viewerHelper.contentView = ViewerHelper.StudioView;
+ contentLoader.item.presentation.setSource(fileUrls[0]);
+ }
+ }
+ }
+
+ header: Rectangle {
+ height: _controlBaseHeight
+ color: _menuBackgroundColor
+ visible: window.visibility !== Window.FullScreen
+
+ Row {
+ anchors.fill: parent
+ StyledMenuButton {
+ id: fileButton
+ text: qsTr("File")
+ menu: fileMenu
+ window: window
+
+ StyledMenu {
+ id: fileMenu
+ StyledMenuItem {
+ text: qsTr("Open...")
+ shortcut: StandardKey.Open
+ enabled: _viewerHelper.contentView !== ViewerHelper.SequenceView
+ onTriggered: {
+ if (enabled) {
+ fileDialogLoader.sourceComponent = fileDialogComponent;
+ fileDialogLoader.item.open();
+ }
+ }
+ Loader {
+ id: fileDialogLoader
+ sourceComponent: Item {}
+ }
+ }
+ StyledMenuItem {
+ text: _viewerHelper.connected ? qsTr("Disconnect") : qsTr("Connect...")
+ shortcut: "F9"
+ enabled: _viewerHelper.contentView !== ViewerHelper.SequenceView
+ onTriggered: {
+ if (enabled) {
+ if (_viewerHelper.connected)
+ _viewerHelper.disconnectRemote();
+ else
+ ipEntry.visible = !ipEntry.visible;
+ }
+ }
+ }
+ StyledMenuItem {
+ text: qsTr("Reload")
+ enabled: _viewerHelper.contentView === ViewerHelper.StudioView
+ shortcut: "F5"
+ onTriggered: {
+ if (enabled) {
+ contentLoader.item.hiderVisible = true;
+ contentLoader.item.reset();
+ }
+ }
+ }
+ StyledMenuSeparator {}
+ StyledMenuItem {
+ text: qsTr("Quit")
+ shortcut: "Ctrl+Q"
+ onTriggered: {
+ window.close();
+ }
+ }
+ }
+ }
+ StyledMenuButton {
+ id: viewButton
+ text: qsTr("View")
+ menu: viewMenu
+ window: window
+
+ StyledMenu {
+ id: viewMenu
+ StyledMenuItem {
+ text: qsTr("Show Matte")
+ shortcut: "Ctrl+D"
+ enabled: _viewerHelper.contentView === ViewerHelper.StudioView
+ showCheckMark: window.matteColor !== window.hideMatteColor
+ onTriggered: {
+ if (enabled) {
+ if (window.matteColor === window.hideMatteColor)
+ window.matteColor = window.showMatteColor;
+ else
+ window.matteColor = window.hideMatteColor;
+ }
+ }
+ }
+ StyledMenuItem {
+ id: scaleMenuItem
+ text: qsTr("Scale Mode")
+ showArrow: true
+ arrowMenu: scaleMenu
+ shortcut: "Ctrl+Shift+S"
+ enabled: _viewerHelper.contentView === ViewerHelper.StudioView
+ onTriggered: {
+ if (enabled) {
+ scaleMenu.close();
+ if (window.scaleMode === ViewerSettings.ScaleModeCenter)
+ window.scaleMode = ViewerSettings.ScaleModeFit;
+ else if (window.scaleMode === ViewerSettings.ScaleModeFit)
+ window.scaleMode = ViewerSettings.ScaleModeFill;
+ else if (window.scaleMode === ViewerSettings.ScaleModeFill)
+ window.scaleMode = ViewerSettings.ScaleModeCenter;
+ }
+ }
+
+ StyledMenu {
+ id: scaleMenu
+ x: parent.width
+ y: 0
+
+ StyledMenuItem {
+ id: scaleCenter
+ text: qsTr("Center")
+ enabled: _viewerHelper.contentView === ViewerHelper.StudioView
+ showCheckMark: window.scaleMode === ViewerSettings.ScaleModeCenter
+ onTriggered: {
+ if (enabled)
+ window.scaleMode = ViewerSettings.ScaleModeCenter;
+ }
+ }
+ StyledMenuItem {
+ id: scaleFit
+ text: qsTr("Scale to Fit")
+ enabled: _viewerHelper.contentView === ViewerHelper.StudioView
+ showCheckMark: window.scaleMode === ViewerSettings.ScaleModeFit
+ onTriggered: {
+ if (enabled)
+ window.scaleMode = ViewerSettings.ScaleModeFit;
+ }
+ }
+ StyledMenuItem {
+ id: scaleFill
+ text: qsTr("Scale to Fill")
+ enabled: _viewerHelper.contentView === ViewerHelper.StudioView
+ showCheckMark: window.scaleMode === ViewerSettings.ScaleModeFill
+ onTriggered: {
+ if (enabled)
+ window.scaleMode = ViewerSettings.ScaleModeFill;
+ }
+ }
+ }
+ }
+ StyledMenuItem {
+ text: qsTr("Show Render Statistics")
+ shortcut: "F7"
+ enabled: _viewerHelper.contentView === ViewerHelper.StudioView
+ showCheckMark: window.showRenderStats
+ onTriggered: {
+ if (enabled)
+ window.showRenderStats = !window.showRenderStats;
+ }
+ }
+ StyledMenuSeparator {}
+ StyledMenuItem {
+ text: qsTr("Full Screen")
+ shortcut: "F11"
+ Shortcut {
+ sequence: "ESC"
+ context: Qt.ApplicationShortcut
+ enabled: window.visibility === Window.FullScreen
+ onActivated: {
+ window.visibility = window.previousVisibility;
+ }
+ }
+
+ onTriggered: {
+ if (window.visibility !== Window.FullScreen) {
+ window.previousVisibility = window.visibility
+ window.visibility = Window.FullScreen;
+ } else {
+ window.visibility = window.previousVisibility;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Viewer/Qt3DViewer/remotedeploymentreceiver.cpp b/src/Viewer/Qt3DViewer/remotedeploymentreceiver.cpp
new file mode 100644
index 00000000..cdfa5b05
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/remotedeploymentreceiver.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "remotedeploymentreceiver.h"
+#include "viewer.h"
+
+#include <QtNetwork>
+
+RemoteDeploymentReceiver::RemoteDeploymentReceiver(int serverPort, QObject *parent)
+ : QObject(parent)
+ , m_tcpServer(0)
+ , m_connection(0)
+ , m_temporaryDir(0)
+ , m_serverPort(serverPort)
+ , m_projectDeployed(false)
+{
+ m_incoming.setVersion(QDataStream::Qt_5_8);
+}
+
+RemoteDeploymentReceiver::~RemoteDeploymentReceiver()
+{
+ delete m_temporaryDir;
+ m_temporaryDir = 0;
+}
+
+void RemoteDeploymentReceiver::setPort(int value)
+{
+ m_serverPort = value;
+}
+
+QString RemoteDeploymentReceiver::startServer()
+{
+ if (m_tcpServer)
+ return QString();
+
+ m_tcpServer = new QTcpServer(this);
+ if (!m_tcpServer->listen(QHostAddress::Any, m_serverPort)) {
+ QString error = tr("Can't start the remote connection: '%1'")
+ .arg(m_tcpServer->errorString());
+ delete m_tcpServer;
+ m_tcpServer = 0;
+ return error;
+ }
+
+ QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
+ // use the first non-localhost IPv4 address
+ for (int i = 0; i < ipAddressesList.size(); ++i) {
+ if (ipAddressesList.at(i) != QHostAddress::LocalHost
+ && ipAddressesList.at(i).toIPv4Address()) {
+ m_hostAddress = ipAddressesList.at(i);
+ break;
+ }
+ }
+
+ // if we did not find one, use IPv4 localhost
+ if (m_hostAddress.isNull())
+ m_hostAddress = QHostAddress(QHostAddress::LocalHost);
+
+ m_serverPort = m_tcpServer->serverPort();
+ connect(m_tcpServer, SIGNAL(newConnection()),
+ this, SLOT(acceptRemoteConnection()));
+ return QString();
+}
+
+void RemoteDeploymentReceiver::disconnectRemote()
+{
+ if (m_connection)
+ m_connection->disconnectFromHost();
+}
+
+void RemoteDeploymentReceiver::acceptRemoteConnection()
+{
+ Q_ASSERT(m_tcpServer);
+ Q_ASSERT(!m_connection);
+ m_connection = m_tcpServer->nextPendingConnection();
+ Q_ASSERT(m_connection);
+
+ m_incoming.setDevice(m_connection);
+
+ connect(m_connection, &QTcpSocket::disconnected,
+ this, &RemoteDeploymentReceiver::acceptRemoteDisconnection);
+
+ connect(m_connection, &QTcpSocket::readyRead,
+ this, &RemoteDeploymentReceiver::readProject);
+
+ Q_EMIT(remoteConnected());
+}
+
+void RemoteDeploymentReceiver::acceptRemoteDisconnection()
+{
+ Q_ASSERT(m_tcpServer);
+ Q_ASSERT(m_connection);
+ m_connection->deleteLater();
+ m_connection = 0;
+
+ m_incoming.setDevice(0);
+
+ Q_EMIT(remoteDisconnected());
+}
+
+void RemoteDeploymentReceiver::readProject()
+{
+ m_projectDeployed = false;
+ Q_EMIT(projectChanging());
+
+ m_incoming.startTransaction();
+
+ int totalBytes = 0;
+ m_incoming >> totalBytes;
+
+ Viewer *viewer = qobject_cast<Viewer *>(parent());
+ if (viewer && totalBytes != 0) {
+ viewer->updateProgress(
+ 100 * ((double)m_connection->bytesAvailable() / (double)totalBytes));
+ }
+
+ if (m_connection->bytesAvailable() < totalBytes) {
+ m_incoming.rollbackTransaction();
+ return;
+ }
+
+ int numberOfFiles = 0;
+ QString projectFile;
+ m_incoming >> numberOfFiles;
+ m_incoming >> projectFile;
+
+ QVector<QPair<QString, QByteArray> > files;
+ for (int i = 0; i < numberOfFiles; ++i) {
+ QString fileName;
+ QByteArray fileContents;
+ m_incoming >> fileName;
+ m_incoming >> fileContents;
+ files.append(qMakePair(fileName, fileContents));
+ }
+
+ if (!m_incoming.commitTransaction()) {
+ m_incoming.abortTransaction();
+ qWarning() << "Error transferring remote project in one payload";
+ return;
+ }
+
+ QFileInfo currentProject(m_projectFile);
+ if (projectFile != currentProject.fileName()) {
+ delete m_temporaryDir;
+ m_temporaryDir = 0;
+ }
+
+ if (!m_temporaryDir)
+ m_temporaryDir = new QTemporaryDir;
+
+ Q_ASSERT(m_temporaryDir->isValid());
+
+ for (const auto &file : qAsConst(files)) {
+ QString filePath = m_temporaryDir->path() + QDir::separator() + file.first;
+ QFile tmpFile(filePath);
+ QDir tmpFileDir = QFileInfo(tmpFile).absoluteDir();
+ if (!tmpFileDir.exists())
+ tmpFileDir.mkpath(".");
+ if (!tmpFile.open(QIODevice::WriteOnly)) {
+ delete m_temporaryDir;
+ m_temporaryDir = 0;
+ qWarning() << "Error opening temporary file for remote project:"
+ << filePath;
+ return;
+ }
+
+ if (file.first == projectFile)
+ m_projectFile = filePath;
+
+ tmpFile.write(file.second);
+ tmpFile.close();
+ }
+
+ m_projectDeployed = true;
+ Q_EMIT(projectChanged());
+}
diff --git a/src/Viewer/Qt3DViewer/remotedeploymentreceiver.h b/src/Viewer/Qt3DViewer/remotedeploymentreceiver.h
new file mode 100644
index 00000000..49f7de3f
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/remotedeploymentreceiver.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef REMOTEDEPLOYMENTRECEIVER_H
+#define REMOTEDEPLOYMENTRECEIVER_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qtemporarydir.h>
+#include <QtNetwork/qtcpserver.h>
+
+class RemoteDeploymentReceiver : public QObject
+{
+ Q_OBJECT
+public:
+ explicit RemoteDeploymentReceiver(int serverPort, QObject *parent);
+ ~RemoteDeploymentReceiver();
+
+ QString startServer();
+ void disconnectRemote();
+
+ QHostAddress hostAddress() const { return m_hostAddress; }
+ int serverPort() const { return m_serverPort; }
+ void setServerPort(int port) { m_serverPort = port; }
+ bool isConnected() const { return m_connection; }
+ bool isProjectDeployed() const { return m_connection && m_projectDeployed; }
+ QString fileName() const { return m_projectFile; }
+
+Q_SIGNALS:
+ void projectChanged();
+ void projectChanging();
+ void remoteConnected();
+ void remoteDisconnected();
+
+private Q_SLOTS:
+ void acceptRemoteConnection();
+ void acceptRemoteDisconnection();
+ void readProject();
+ void setPort(int value);
+
+private:
+ QTcpServer *m_tcpServer = nullptr;
+ QTcpSocket *m_connection = nullptr;
+ QHostAddress m_hostAddress;
+ QDataStream m_incoming;
+ QTemporaryDir *m_temporaryDir = nullptr;
+ QString m_projectFile;
+ bool m_projectDeployed;
+ int m_serverPort;
+};
+
+#endif // REMOTEDEPLOYMENTRECEIVER_H
diff --git a/src/Viewer/Qt3DViewer/resources/images/3D-studio-viewer.ico b/src/Viewer/Qt3DViewer/resources/images/3D-studio-viewer.ico
new file mode 100644
index 00000000..c15cd68a
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/resources/images/3D-studio-viewer.ico
Binary files differ
diff --git a/src/Viewer/Qt3DViewer/resources/images/3D-studio-viewer.svg b/src/Viewer/Qt3DViewer/resources/images/3D-studio-viewer.svg
new file mode 100644
index 00000000..69a14f48
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/resources/images/3D-studio-viewer.svg
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="512px" height="512px" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
+ <title>icon_512x512</title>
+ <desc>Created with Sketch.</desc>
+ <defs>
+ <polygon id="path-1" points="64 0 0 48 0 112 64 160 128 112 128 48"></polygon>
+ <path d="M45.6534039,84.7089559 L45.6534039,97.4054197 L15.0588235,128 L0,112.941176 L30.1176471,82.8235294 L43.7426612,82.8235294 C35.3039365,74.0299707 30.1176471,62.091235 30.1176471,48.9411765 C30.1176471,21.9117111 52.0293581,0 79.0588235,0 C106.088289,0 128,21.9117111 128,48.9411765 C128,75.9706419 106.088289,97.8823529 79.0588235,97.8823529 C66.1449913,97.8823529 54.3993526,92.8807218 45.6534039,84.7089559 Z M79.0588235,82.8235294 C97.7715303,82.8235294 112.941176,67.6538833 112.941176,48.9411765 C112.941176,30.2284697 97.7715303,15.0588235 79.0588235,15.0588235 C60.3461167,15.0588235 45.1764706,30.2284697 45.1764706,48.9411765 C45.1764706,67.6538833 60.3461167,82.8235294 79.0588235,82.8235294 Z" id="path-2"></path>
+ </defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="icon_512x512">
+ <g id="Group-3" transform="translate(56.000000, 88.000000)">
+ <g id="Page-1-Copy">
+ <polygon id="Fill-1" fill="#41CD52" points="0 288.0032 328 288.0032 384 232.0032 384 0.0032 56 0.0032 0 56.0032"></polygon>
+ <g id="Group-6" transform="translate(72.000000, 48.000000)" fill="#FFFFFF">
+ <path d="M39.3792,138.3936 C44.8752,147.8176 55.4752,152.6656 71.0512,152.6656 C86.6272,152.6656 97.0992,147.9536 102.5952,138.6496 C107.9552,129.3456 110.7072,113.5216 110.7072,91.4016 C110.7072,69.1536 107.9632,53.0576 102.3312,42.9776 C96.7072,32.9056 86.3632,27.9216 71.0512,27.9216 C55.8752,27.9216 45.3952,32.8896 39.6432,42.9776 C34.0112,53.0656 31.1312,69.0256 31.1312,91.1456 C31.1312,113.1296 33.8832,128.9616 39.3792,138.3936 L39.3792,138.3936 Z M70.9232,176.2256 C45.6592,176.2256 27.9952,169.4096 18.0432,155.8016 C8.0992,142.2016 3.1232,120.5936 3.1232,91.2736 C3.1232,61.8336 8.2272,39.9696 18.3072,25.7056 C28.3872,11.4416 46.0512,4.2416 70.9232,4.2416 C95.9152,4.2416 113.4592,11.3056 123.4032,25.5696 C133.4832,39.7056 138.4512,61.5616 138.4512,91.1536 C138.4512,110.6496 136.3632,126.3536 132.3072,138.2656 C128.1152,150.2976 121.4432,159.4656 112.0192,165.7536 L132.4352,198.6016 L107.4352,210.2336 L85.8432,174.7776 C82.6992,175.8016 77.7232,176.1376 70.9232,176.1376 L70.9232,176.2256 Z" id="Fill-2"></path>
+ <path d="M198.66,71.5184 L198.66,125.0464 C198.66,134.9984 199.444,141.5344 200.884,144.8064 C202.316,148.0784 206.116,149.6544 212.004,149.6544 L231.9,148.7984 L233.076,170.0064 C222.212,172.1024 213.972,173.1424 208.212,173.1424 C194.468,173.1424 185.044,170.0064 179.94,163.7184 C174.836,157.4384 172.22,145.5344 172.22,127.9984 L172.22,71.5824 L153.764,71.5824 L153.764,48.9424 L172.348,48.9424 L172.348,13.7344 L198.66,13.7344 L198.66,48.9424 L232.164,48.9424 L232.164,71.4544 L198.66,71.5184 Z" id="Fill-4"></path>
+ </g>
+ </g>
+ <g id="Group" transform="translate(280.000000, 176.000000)">
+ <g id="Path-29">
+ <use fill="#D8D8D8" fill-rule="evenodd" xlink:href="#path-1"></use>
+ <path stroke="#FFFFFF" stroke-width="7" d="M64,-4.375 L131.5,46.25 L131.5,113.75 L64,164.375 L-3.5,113.75 L-3.5,46.25 L64,-4.375 Z"></path>
+ </g>
+ <polygon id="Path-27" fill="#81CFFF" points="0 48 64 96 128 48 64 0"></polygon>
+ <polygon id="Path-28-Copy" fill="#1F83C1" transform="translate(96.000000, 104.000000) scale(-1, 1) translate(-96.000000, -104.000000) " points="64 48 64 112 128 160 128 96"></polygon>
+ <polygon id="Path-28" fill="#2696DA" points="0 48 0 112 64 160 64 96"></polygon>
+ </g>
+ <g id="Group-2" transform="translate(200.000000, 208.000000)">
+ <g id="Combined-Shape">
+ <use fill="#707070" fill-rule="evenodd" xlink:href="#path-2"></use>
+ <path stroke="#FFFFFF" stroke-width="7" d="M49.1534039,92.0264501 L49.1534039,98.8551671 L15.0588235,132.949747 L-4.94974747,112.941176 L28.6678996,79.3235294 L36.3089911,79.3235294 C30.0728186,70.5670745 26.6176471,60.0229376 26.6176471,48.9411765 C26.6176471,19.9787144 50.0963615,-3.5 79.0588235,-3.5 C108.021286,-3.5 131.5,19.9787144 131.5,48.9411765 C131.5,77.9036385 108.021286,101.382353 79.0588235,101.382353 C68.1809425,101.382353 57.8185023,98.0537262 49.1534039,92.0264501 Z M79.0588235,79.3235294 C95.8385337,79.3235294 109.441176,65.7208867 109.441176,48.9411765 C109.441176,32.1614663 95.8385337,18.5588235 79.0588235,18.5588235 C62.2791133,18.5588235 48.6764706,32.1614663 48.6764706,48.9411765 C48.6764706,65.7208867 62.2791133,79.3235294 79.0588235,79.3235294 Z"></path>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/src/Viewer/Qt3DViewer/resources/images/arrow.png b/src/Viewer/Qt3DViewer/resources/images/arrow.png
new file mode 100644
index 00000000..40ebda88
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/resources/images/arrow.png
Binary files differ
diff --git a/src/Viewer/Qt3DViewer/resources/images/arrow@2x.png b/src/Viewer/Qt3DViewer/resources/images/arrow@2x.png
new file mode 100644
index 00000000..1a21ee06
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/resources/images/arrow@2x.png
Binary files differ
diff --git a/src/Viewer/Qt3DViewer/resources/images/check.png b/src/Viewer/Qt3DViewer/resources/images/check.png
new file mode 100644
index 00000000..5c1ef70a
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/resources/images/check.png
Binary files differ
diff --git a/src/Viewer/Qt3DViewer/resources/images/check@2x.png b/src/Viewer/Qt3DViewer/resources/images/check@2x.png
new file mode 100644
index 00000000..ed730bfe
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/resources/images/check@2x.png
Binary files differ
diff --git a/src/Viewer/Qt3DViewer/resources/images/viewer.icns b/src/Viewer/Qt3DViewer/resources/images/viewer.icns
new file mode 100644
index 00000000..b718d75c
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/resources/images/viewer.icns
Binary files differ
diff --git a/src/Viewer/Qt3DViewer/viewer.cpp b/src/Viewer/Qt3DViewer/viewer.cpp
new file mode 100644
index 00000000..4d6bcea3
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/viewer.cpp
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qsettings.h>
+#include <QtCore/qfileinfo.h>
+#include <QtStudio3D/private/q3dsviewersettings_p.h>
+#include <QtStudio3D/private/q3dspresentation_p.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qscreen.h>
+#include <QtQuick/qquickwindow.h>
+
+#include "viewer.h"
+#include "q3dspresentationitem.h"
+
+Viewer::Viewer(bool generatorMode, QObject *parent)
+ : QObject(parent)
+ , m_generatorMode(generatorMode)
+{
+ if (m_generatorMode)
+ setContentView(SequenceView);
+
+ m_connectTextResetTimer.setInterval(5000);
+ m_connectTextResetTimer.setSingleShot(true);
+ connect(&m_connectTextResetTimer, &QTimer::timeout, this, &Viewer::resetConnectionInfoText);
+}
+
+Viewer::~Viewer()
+{
+}
+
+void Viewer::connectRemote()
+{
+ if (m_remoteDeploymentReceiver) {
+ delete m_remoteDeploymentReceiver;
+ m_remoteDeploymentReceiver = 0;
+ Q_EMIT connectedChanged();
+ }
+
+ m_remoteDeploymentReceiver = new RemoteDeploymentReceiver(m_connectPort, this);
+ QString error = m_remoteDeploymentReceiver->startServer();
+ if (!error.isEmpty()) {
+ delete m_remoteDeploymentReceiver;
+ m_remoteDeploymentReceiver = nullptr;
+ setContentView(DefaultView);
+ m_qmlRootObject->setProperty("error", QVariant(error));
+ return;
+ }
+
+ resetConnectionInfoText();
+
+ connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::remoteConnected,
+ this, &Viewer::remoteConnected);
+
+ connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::remoteDisconnected,
+ this, &Viewer::remoteDisconnected);
+
+ connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::projectChanging,
+ this, &Viewer::remoteProjectChanging);
+
+ connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::projectChanged,
+ this, &Viewer::loadRemoteDeploymentReceiver);
+
+ Q_EMIT connectedChanged();
+}
+
+void Viewer::disconnectRemote()
+{
+ m_remoteDeploymentReceiver->disconnectRemote();
+}
+
+// Used to load files via command line and when using remote deployment
+void Viewer::loadFile(const QString &filename)
+{
+ QString targetFilename = filename;
+ // Try to find the application (*.uia) file for loading instead of the presentation (*.uip)
+ // in case we are connected to remote sender.
+ if (isConnected() && targetFilename.endsWith(QStringLiteral(".uip"))) {
+ targetFilename.chop(4);
+ targetFilename.append(QStringLiteral(".uia"));
+ QFileInfo targetfileInfo(targetFilename);
+ // uia not found, revert to given uip
+ if (!targetfileInfo.exists())
+ targetFilename = filename;
+ }
+
+ QFileInfo fileInfo(targetFilename);
+ if (!fileInfo.exists()) {
+ setContentView(DefaultView);
+ m_qmlRootObject->setProperty(
+ "error", QVariant(tr("Tried to load nonexistent file %1").arg(targetFilename)));
+ return;
+ }
+
+ QUrl sourceUrl = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
+
+ setContentView(StudioView);
+
+ if (qmlStudio())
+ qmlStudio()->presentation()->setSource(sourceUrl);
+}
+
+QString Viewer::convertUrlListToFilename(const QList<QUrl> &list)
+{
+ for (const QUrl &url : list) {
+ QString str = url.toLocalFile();
+ if (!str.isEmpty()) {
+ if (QFileInfo(str).suffix() == QStringLiteral("uip")
+ || QFileInfo(str).suffix() == QStringLiteral("uia")) {
+ return str;
+ }
+ }
+ }
+ return QString();
+}
+
+void Viewer::restoreWindowState(QWindow *window)
+{
+ QSettings settings;
+ QRect geo = settings.value(QStringLiteral("WindowGeometry"), QRect()).toRect();
+ int visibility = settings.value(QStringLiteral("WindowVisibility"),
+ QWindow::Windowed).toInt();
+
+ // Do not restore geometry if resulting geometry means the center of the window
+ // would be offscreen on the virtual desktop
+ QRect vgeo = window->screen()->availableVirtualGeometry();
+ QPoint center(geo.x() + geo.width() / 2, geo.y() + geo.height() / 2);
+ bool offscreen = center.x() > vgeo.width() || center.x() < 0
+ || center.y() > vgeo.height() || center.y() < 0;
+
+ if (!offscreen && !geo.isNull()) {
+ // The first geometry set at startup may adjust the geometry according to pixel
+ // ratio if mouse cursor is on different screen than where window goes and the
+ // two screens have different pixel ratios. Setting geometry twice seems to
+ // work around this.
+ geo.adjust(0, 0, -1, 0);
+ window->setGeometry(geo);
+ geo.adjust(0, 0, 1, 0);
+ window->setGeometry(geo);
+ }
+
+ window->setVisibility(QWindow::Visibility(visibility));
+}
+
+void Viewer::storeWindowState(QWindow *window)
+{
+ QSettings settings;
+ settings.setValue(QStringLiteral("WindowGeometry"), window->geometry());
+ settings.setValue(QStringLiteral("WindowState"), window->visibility());
+}
+
+// Since we need mouse events for handling the swipe gesture in viewer, we need to generate
+// a fake event for the presentation.
+void Viewer::handleMousePress(int x, int y, int button, int buttons, int modifiers)
+{
+ if (qmlStudio()) {
+ QMouseEvent fakeEvent(QEvent::MouseButtonPress,
+ QPointF(x, y) * qmlStudio()->window()->devicePixelRatio(),
+ Qt::MouseButton(button),
+ Qt::MouseButtons(buttons),
+ Qt::KeyboardModifiers(modifiers));
+ qmlStudio()->presentation()->mousePressEvent(&fakeEvent);
+ }
+}
+
+void Viewer::handleMouseRelease(int x, int y, int button, int buttons, int modifiers)
+{
+ if (qmlStudio()) {
+ QMouseEvent fakeEvent(QEvent::MouseButtonRelease,
+ QPointF(x, y) * qmlStudio()->window()->devicePixelRatio(),
+ Qt::MouseButton(button),
+ Qt::MouseButtons(buttons),
+ Qt::KeyboardModifiers(modifiers));
+ qmlStudio()->presentation()->mouseReleaseEvent(&fakeEvent);
+ }
+}
+
+void Viewer::handleMouseMove(int x, int y, int button, int buttons, int modifiers)
+{
+ if (qmlStudio()) {
+ QMouseEvent fakeEvent(QEvent::MouseMove,
+ QPointF(x, y) * qmlStudio()->window()->devicePixelRatio(),
+ Qt::MouseButton(button),
+ Qt::MouseButtons(buttons),
+ Qt::KeyboardModifiers(modifiers));
+ qmlStudio()->presentation()->mouseMoveEvent(&fakeEvent);
+ }
+}
+
+void Viewer::setContentView(Viewer::ContentView view)
+{
+ if (view != m_contentView) {
+ m_qmlStudio = nullptr;
+ m_contentView = view;
+ Q_EMIT contentViewChanged();
+ }
+}
+
+Viewer::ContentView Viewer::contentView() const
+{
+ return m_contentView;
+}
+
+void Viewer::setOpenFolder(const QUrl &folder)
+{
+ QString localFolder = folder.toLocalFile();
+ QFileInfo fi(localFolder);
+ QString newDir;
+ if (fi.isDir())
+ newDir = fi.absoluteFilePath();
+ else
+ newDir = fi.absolutePath();
+ if (newDir != m_openFileDir) {
+ m_openFileDir = newDir;
+ QSettings settings;
+ settings.setValue(QStringLiteral("DirectoryOfLastOpen"), m_openFileDir);
+ Q_EMIT openFolderChanged();
+ }
+}
+
+QUrl Viewer::openFolder()
+{
+ if (m_openFileDir.size() == 0) {
+ QSettings settings;
+ m_openFileDir = settings.value(QStringLiteral("DirectoryOfLastOpen"),
+ QString("")).toString();
+#ifdef Q_OS_ANDROID
+ if (m_openFileDir.isEmpty())
+ m_openFileDir = QStringLiteral("/sdcard/qt3dviewer"); // Add default folder for Android
+#endif
+ }
+ return QUrl::fromLocalFile(m_openFileDir);
+}
+
+void Viewer::setConnectPort(int port)
+{
+ if (m_connectPort != port) {
+ QSettings settings;
+ m_connectPort = port;
+ settings.setValue(QStringLiteral("ConnectPort"), m_connectPort);
+ Q_EMIT connectPortChanged();
+ }
+}
+
+int Viewer::connectPort()
+{
+ if (m_connectPort < 0) {
+ QSettings settings;
+ m_connectPort = settings.value(QStringLiteral("ConnectPort"), 36000).toInt();
+ }
+ return m_connectPort;
+}
+
+QString Viewer::connectText() const
+{
+ return m_connectText;
+}
+
+bool Viewer::isConnected() const
+{
+ return m_remoteDeploymentReceiver ? m_remoteDeploymentReceiver->isConnected() : false;
+}
+
+void Viewer::setQmlRootObject(QObject *obj)
+{
+ m_qmlRootObject = obj;
+}
+
+void Viewer::loadRemoteDeploymentReceiver()
+{
+ Q_ASSERT(m_remoteDeploymentReceiver);
+ const QString remote = m_remoteDeploymentReceiver->fileName();
+ QMetaObject::invokeMethod(this, "loadFile", Qt::QueuedConnection, Q_ARG(QString, remote));
+}
+
+void Viewer::remoteProjectChanging()
+{
+ if (m_contentView != ConnectView)
+ setContentView(ConnectView);
+ m_connectText = tr("Loading remote project...");
+ Q_EMIT connectTextChanged();
+}
+
+void Viewer::remoteConnected()
+{
+ m_connectText = tr("Remote Connected");
+ Q_EMIT connectTextChanged();
+ Q_EMIT connectedChanged();
+ if (m_contentView != ConnectView)
+ Q_EMIT showInfoOverlay(m_connectText);
+}
+
+void Viewer::remoteDisconnected()
+{
+ m_connectText = tr("Remote Disconnected");
+ Q_EMIT connectTextChanged();
+ Q_EMIT connectedChanged();
+ if (m_contentView != ConnectView) {
+ Q_EMIT showInfoOverlay(m_connectText);
+ } else {
+ // Start timer to reset connection info text
+ m_connectTextResetTimer.start();
+ }
+}
+
+void Viewer::resetConnectionInfoText()
+{
+ m_connectText.clear();
+ QTextStream stream(&m_connectText);
+ stream << tr("Use IP: %1 and Port: %2\n"
+ "in Qt 3D Studio Editor to connect to this viewer.\n\n"
+ "Use File/Open... to open a local presentation.")
+ .arg(m_remoteDeploymentReceiver->hostAddress().toString())
+ .arg(QString::number(m_connectPort));
+ Q_EMIT connectTextChanged();
+}
+
+Q3DSView *Viewer::qmlStudio()
+{
+ if (m_contentView == StudioView) {
+ if (!m_qmlStudio) {
+ QObject *loadedContent = m_qmlRootObject->property("loadedContent").value<QObject *>();
+ m_qmlStudio = static_cast<Q3DSView *>(loadedContent);
+ }
+ } else {
+ m_qmlStudio = nullptr;
+ }
+ return m_qmlStudio;
+}
+
+void Viewer::generatorProgress(int totalFrames, int frameCount)
+{
+ QString progressString;
+ if (frameCount >= totalFrames) {
+ progressString =
+ QCoreApplication::translate(
+ "main", "Image sequence generation done! (%2 frames generated)")
+ .arg(totalFrames);
+ } else {
+ progressString =
+ QCoreApplication::translate("main", "Image sequence generation progress: %1 / %2")
+ .arg(frameCount).arg(totalFrames);
+ }
+ QObject *loadedContent = m_qmlRootObject->property("loadedContent").value<QObject *>();
+ loadedContent->setProperty("mainText", progressString);
+}
+
+void Viewer::generatorFinished(bool success, const QString &details)
+{
+ QObject *loadedContent = m_qmlRootObject->property("loadedContent").value<QObject *>();
+ if (success) {
+ loadedContent->setProperty("detailsText", details);
+ } else {
+ QString mainString =
+ QCoreApplication::translate("main", "Image sequence generation failed:");
+ loadedContent->setProperty("mainText", mainString);
+ loadedContent->setProperty("detailsText", details);
+ }
+}
+
+void Viewer::updateProgress(int percent)
+{
+ // Don't wait for 100%, as it'll already start loading and text isn't updated anymore
+ if (percent >= 99)
+ m_connectText = tr("Loading remote project...");
+ else
+ m_connectText = QStringLiteral("Receiving (%1%)").arg(percent);
+ Q_EMIT connectTextChanged();
+}
+
+void Viewer::setGeneratorDetails(const QString &filename)
+{
+ QObject *loadedContent = m_qmlRootObject->property("loadedContent").value<QObject *>();
+ loadedContent->setProperty("detailsText", filename);
+}
diff --git a/src/Viewer/Qt3DViewer/viewer.h b/src/Viewer/Qt3DViewer/viewer.h
new file mode 100644
index 00000000..9931483e
--- /dev/null
+++ b/src/Viewer/Qt3DViewer/viewer.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VIEWER_H
+#define VIEWER_H
+
+#include "remotedeploymentreceiver.h"
+
+#include <QtCore/qobject.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qtimer.h>
+#include <QtGui/qwindow.h>
+#include "Qt3DSView.h"
+
+class Viewer : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(ContentView contentView READ contentView WRITE setContentView NOTIFY contentViewChanged)
+ Q_PROPERTY(QUrl openFolder READ openFolder WRITE setOpenFolder NOTIFY openFolderChanged)
+ Q_PROPERTY(int connectPort READ connectPort WRITE setConnectPort NOTIFY connectPortChanged)
+ Q_PROPERTY(QString connectText READ connectText NOTIFY connectTextChanged)
+ Q_PROPERTY(bool connected READ isConnected NOTIFY connectedChanged)
+
+public:
+ enum ContentView {
+ DefaultView,
+ StudioView,
+ ConnectView,
+ SequenceView
+ };
+
+ Q_ENUM(ContentView)
+
+ explicit Viewer(bool generatorMode, QObject *parent = nullptr);
+ ~Viewer();
+
+ Q_INVOKABLE void connectRemote();
+ Q_INVOKABLE void disconnectRemote();
+ Q_INVOKABLE void loadFile(const QString &filename);
+ Q_INVOKABLE QString convertUrlListToFilename(const QList<QUrl> &list);
+ Q_INVOKABLE void restoreWindowState(QWindow *window);
+ Q_INVOKABLE void storeWindowState(QWindow *window);
+
+ Q_INVOKABLE void handleMousePress(int x, int y, int button, int buttons, int modifiers);
+ Q_INVOKABLE void handleMouseRelease(int x, int y, int button, int buttons, int modifiers);
+ Q_INVOKABLE void handleMouseMove(int x, int y, int button, int buttons, int modifiers);
+
+ void setContentView(ContentView view);
+ ContentView contentView() const;
+ void setOpenFolder(const QUrl &folder);
+ QUrl openFolder(); // not const since it potentially updates from settings
+ void setConnectPort(int port);
+ int connectPort(); // not const since it potentially updates from settings
+ QString connectText() const;
+ bool isConnected() const;
+
+ void setQmlRootObject(QObject *obj);
+
+ void loadProject(const QByteArray &data);
+ void updateProgress(int percent);
+ void setGeneratorDetails(const QString &filename);
+
+public Q_SLOTS:
+ void generatorProgress(int totalFrames, int frameCount);
+ void generatorFinished(bool success, const QString &details);
+
+private Q_SLOTS:
+ void loadRemoteDeploymentReceiver();
+ void remoteProjectChanging();
+ void remoteConnected();
+ void remoteDisconnected();
+ void resetConnectionInfoText();
+
+Q_SIGNALS:
+ void contentViewChanged();
+ void openFolderChanged();
+ void connectPortChanged();
+ void connectTextChanged();
+ void connectedChanged();
+ void showInfoOverlay(const QString &infoStr);
+
+private:
+ Q3DSView *qmlStudio();
+
+ QString m_openFileDir;
+ RemoteDeploymentReceiver *m_remoteDeploymentReceiver = nullptr;
+ bool m_generatorMode = false;
+ ContentView m_contentView = DefaultView;
+ QObject *m_qmlRootObject = nullptr;
+ int m_connectPort = -1;
+ QString m_connectText;
+ Q3DSView *m_qmlStudio = nullptr;
+ QTimer m_connectTextResetTimer;
+};
+
+#endif // VIEWER_H
diff --git a/src/Viewer/Viewer.pro b/src/Viewer/Viewer.pro
index 62df68ee..07bdb7ad 100644
--- a/src/Viewer/Viewer.pro
+++ b/src/Viewer/Viewer.pro
@@ -1,4 +1,9 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS += \
- studio3d
+ studio3d \
+ qmlviewer
+
+!boot2qt:!integrity:!qnx {
+ SUBDIRS += Qt3DViewer
+}
diff --git a/src/Viewer/qmlviewer/Qt3DSRenderer.cpp b/src/Viewer/qmlviewer/Qt3DSRenderer.cpp
new file mode 100644
index 00000000..39d0c9d6
--- /dev/null
+++ b/src/Viewer/qmlviewer/Qt3DSRenderer.cpp
@@ -0,0 +1,344 @@
+/****************************************************************************
+**
+** Copyright (c) 2016 NVIDIA CORPORATION.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSRenderer.h"
+#include "Qt3DSViewerApp.h"
+#include "Qt3DSAudioPlayerImpl.h"
+
+#include <QtStudio3D/private/q3dscommandqueue_p.h>
+#include <QtStudio3D/private/q3dsviewersettings_p.h>
+#include <QtStudio3D/private/q3dspresentation_p.h>
+#include <QtStudio3D/private/studioutils_p.h>
+
+#include <QtCore/qdebug.h>
+#include <QtGui/qwindow.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtQuick/qquickwindow.h>
+
+using namespace Q3DSViewer;
+
+QT_BEGIN_NAMESPACE
+
+Q3DSRenderer::Q3DSRenderer(bool visibleFlag, qt3ds::Qt3DSAssetVisitor *assetVisitor)
+ : m_visibleFlag(visibleFlag)
+ , m_initElements(false)
+ , m_runtime(0)
+ , m_window(nullptr)
+ , m_initialized(false)
+ , m_initializationFailure(false)
+ , m_visitor(assetVisitor)
+ , m_settings(new Q3DSViewerSettings(this))
+ , m_presentation(new Q3DSPresentation(this))
+{
+}
+
+Q3DSRenderer::~Q3DSRenderer()
+{
+ releaseRuntime();
+}
+
+QOpenGLFramebufferObject *Q3DSRenderer::createFramebufferObject(const QSize &size)
+{
+ QOpenGLFramebufferObjectFormat theFormat;
+ theFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ QOpenGLFramebufferObject *frameBuffer =
+ new QOpenGLFramebufferObject(size, theFormat);
+ if (m_runtime && m_runtime->IsInitialised())
+ m_runtime->setOffscreenId(frameBuffer->handle());
+ return frameBuffer;
+}
+
+/** Pull pending commands from the plugin.
+ * Invoked automatically by the QML scene graph.
+ *
+ * This is the only place where it is valid for the Q3DSView plugin and render to communicate.
+ */
+void Q3DSRenderer::synchronize(QQuickFramebufferObject *inView)
+{
+ // Passing m_InitElements here is a bit of a hack to easily set the flag on the plugin.
+ static_cast<Q3DSView *>(inView)->getCommands(m_initElements, m_commands);
+
+ if (m_initializationFailure)
+ static_cast<Q3DSView *>(inView)->setError(m_error);
+
+ if (m_commands.m_sourceChanged) {
+ releaseRuntime();
+ // Need to update source here rather than processCommands, as source is needed for init
+ m_presentation->setSource(m_commands.m_source);
+ m_initialized = false;
+ m_initializationFailure = false;
+ m_error.clear();
+ static_cast<Q3DSView *>(inView)->setError(QString());
+ }
+
+ m_initElements = false;
+
+ // We need a handle to the window to be able to reset the GL state inside of Draw().
+ // See https://bugreports.qt.io/browse/QTBUG-47213
+ if (!m_window)
+ m_window = inView->window();
+}
+
+void Q3DSRenderer::releaseRuntime()
+{
+ m_settings->d_ptr->setViewerApp(nullptr);
+ m_presentation->d_ptr->setViewerApp(nullptr);
+
+ if (m_runtime) {
+ m_runtime->Release();
+ m_runtime = nullptr;
+ }
+}
+
+/** Invoked by the QML scene graph indicating that it's time to render.
+ * Calls `draw()` if the plugin is visible, or `processCommands()` otherwise.
+ *
+ * Note that this will still render if the plugin is opacity:0. To avoid this,
+ * add a line to your QML to hide the object when opacity is zero, like:
+ *
+ * visible: opacity != 0
+ */
+void Q3DSRenderer::render()
+{
+ // We may start in a non visible state but we still need
+ // to init the runtime otherwise the commands are never processed
+ if (!m_initialized && !m_initializationFailure) {
+ m_initialized = initializeRuntime(this->framebufferObject());
+ m_initializationFailure = !m_initialized;
+ if (m_initializationFailure)
+ m_commands.clear();
+ }
+
+ // Don't render if the plugin is hidden; however, if hidden, but sure
+ // to process pending commands so we can be shown again.
+ if (m_initialized) {
+ if (m_visibleFlag)
+ draw();
+ else
+ processCommands();
+ update(); // mark as dirty to ensure update again
+ }
+}
+
+/** Cause Qt3DS runtime to render content.
+ * Initializes GL and the runtime when called the first time.
+ */
+void Q3DSRenderer::draw()
+{
+ if (m_runtime && m_runtime->IsInitialised() && m_window) {
+ if (m_initialized)
+ m_runtime->RestoreState();
+ m_runtime->Render();
+ m_runtime->SaveState();
+
+ m_window->resetOpenGLState();
+ }
+}
+
+bool Q3DSRenderer::initializeRuntime(QOpenGLFramebufferObject *inFbo)
+{
+ m_runtime = &Q3DSViewerApp::Create(nullptr, new Qt3DSAudioPlayerImpl());
+ Q_ASSERT(m_runtime);
+
+ // Connect presentation ready signal before initializing the app
+ connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigPresentationReady,
+ this, &Q3DSRenderer::presentationReady);
+
+ int theWidth = inFbo->width();
+ int theHeight = inFbo->height();
+
+ const QString localSource = Q3DSUtils::urlToLocalFileOrQrc(m_presentation->source());
+
+ if (!m_runtime->InitializeApp(theWidth, theHeight, QOpenGLContext::currentContext()->format(),
+ inFbo->handle(), localSource, m_visitor)) {
+ m_error = m_runtime->error();
+ releaseRuntime();
+ return false;
+ }
+
+ m_runtime->RegisterScriptCallback(Q3DSViewer::ViewerCallbackType::Enum::CALLBACK_ON_INIT,
+ reinterpret_cast<qml_Function>(Q3DSRenderer::onInitHandler),
+ this);
+ m_runtime->RegisterScriptCallback(Q3DSViewer::ViewerCallbackType::Enum::CALLBACK_ON_UPDATE,
+ reinterpret_cast<qml_Function>(Q3DSRenderer::onUpdateHandler),
+ this);
+
+ m_settings->d_ptr->setViewerApp(m_runtime);
+ m_presentation->d_ptr->setViewerApp(m_runtime, false);
+
+ // Connect signals
+ connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigSlideEntered,
+ this, &Q3DSRenderer::enterSlide);
+ connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigSlideExited,
+ this, &Q3DSRenderer::exitSlide);
+
+ return true;
+}
+
+/** Accept user commands (e.g. setAttribute) needed to initialize the code.
+ *
+ * If we attempt to run Qt3DS methods like setAttribute() before the runtime
+ * has been initialized, they will be lost. This method is the correct place
+ * to accept user commands.
+ *
+ * Currently this method just sets a flag needed to pass a flag to the
+ * plugin next time syncronize() is called, which eventually gets the plugin
+ * to emit an `runningChanged` signal the next time `tick()` is called.
+ * As a result, code specified in an `onRunningChanged` handler may be run
+ * after one or more frames have already rendered.
+ */
+void Q3DSRenderer::onInitHandler(void *userData)
+{
+ Q3DSRenderer *theRenderer = static_cast<Q3DSRenderer *>(userData);
+ theRenderer->setInitElements(true);
+}
+
+/** Accept the latest pending user commands (e.g. setAttribute).
+ *
+ * This method just calls `ProcessCommands` to avoid unnecessary
+ * pointer dereferencing and accessor methods (or public member variables).
+ */
+void Q3DSRenderer::onUpdateHandler(void *userData)
+{
+ Q3DSRenderer *theRenderer = static_cast<Q3DSRenderer *>(userData);
+ theRenderer->processCommands();
+}
+
+/** Apply commands queued up by the user (e.g. setAttribute).
+ *
+ * Note that these commands are executed even if the plugin is not visible,
+ * in part to allow changes to the visible flag to be noticed, but also
+ * to allow specialty code to continue to be queued up even when not rendering.
+ */
+void Q3DSRenderer::processCommands()
+{
+ if (!m_runtime) {
+ m_commands.clear();
+ return;
+ }
+
+ if (m_commands.m_visibleChanged)
+ m_visibleFlag = m_commands.m_visible;
+
+ if (QOpenGLFramebufferObject *inFbo = this->framebufferObject()) {
+ if (inFbo->isValid() && (inFbo->width() != m_runtime->GetWindowWidth()
+ || inFbo->height() != m_runtime->GetWindowHeight())) {
+ m_runtime->Resize(inFbo->width(), inFbo->height());
+ }
+ }
+
+ if (m_commands.m_scaleModeChanged)
+ m_settings->setScaleMode(m_commands.m_scaleMode);
+ if (m_commands.m_shadeModeChanged)
+ m_settings->setShadeMode(m_commands.m_shadeMode);
+ if (m_commands.m_matteColorChanged)
+ m_settings->setMatteColor(m_commands.m_matteColor);
+ if (m_commands.m_showRenderStatsChanged)
+ m_settings->setShowRenderStats(m_commands.m_showRenderStats);
+
+ if (m_commands.m_globalAnimationTimeChanged)
+ m_presentation->setGlobalAnimationTime(m_commands.m_globalAnimationTime);
+
+ // Send scene graph changes over to Q3DS
+ for (int i = 0; i < m_commands.size(); i++) {
+ const ElementCommand &cmd = m_commands.commandAt(i);
+ switch (cmd.m_commandType) {
+ case CommandType_SetAttribute:
+ m_presentation->setAttribute(cmd.m_elementPath, cmd.m_stringValue, cmd.m_variantValue);
+ break;
+ case CommandType_SetPresentationActive:
+ m_presentation->setPresentationActive(cmd.m_elementPath, cmd.m_boolValue);
+ break;
+ case CommandType_GoToTime:
+ m_presentation->goToTime(cmd.m_elementPath, cmd.m_floatValue);
+ break;
+ case CommandType_GoToSlide:
+ m_presentation->goToSlide(cmd.m_elementPath, cmd.m_intValues[0]);
+ break;
+ case CommandType_GoToSlideByName:
+ m_presentation->goToSlide(cmd.m_elementPath, cmd.m_stringValue);
+ break;
+ case CommandType_GoToSlideRelative:
+ m_presentation->goToSlide(cmd.m_elementPath, bool(cmd.m_intValues[0]),
+ bool(cmd.m_intValues[1]));
+ break;
+ case CommandType_FireEvent:
+ m_presentation->fireEvent(cmd.m_elementPath, cmd.m_stringValue);
+ break;
+ case CommandType_MousePress:
+ m_runtime->HandleMousePress(cmd.m_intValues[0],
+ cmd.m_intValues[1], cmd.m_intValues[2], true);
+ break;
+ case CommandType_MouseRelease:
+ m_runtime->HandleMousePress(cmd.m_intValues[0],
+ cmd.m_intValues[1], cmd.m_intValues[2], false);
+ break;
+ case CommandType_MouseMove:
+ m_runtime->HandleMouseMove(cmd.m_intValues[0], cmd.m_intValues[1], true);
+ break;
+ case CommandType_MouseWheel:
+ m_runtime->HandleMouseWheel(cmd.m_intValues[0], cmd.m_intValues[1],
+ bool(cmd.m_intValues[2]), cmd.m_intValues[3]);
+ break;
+ case CommandType_KeyPress:
+ m_runtime->HandleKeyInput(Q3DStudio::EKeyCode(cmd.m_intValues[0]), true);
+ break;
+ case CommandType_KeyRelease:
+ m_runtime->HandleKeyInput(Q3DStudio::EKeyCode(cmd.m_intValues[0]), false);
+ break;
+ case CommandType_SetDataInputValue:
+ m_runtime->SetDataInputValue(cmd.m_stringValue, cmd.m_variantValue);
+ break;
+ case CommandType_RequestSlideInfo: {
+ int current = 0;
+ int previous = 0;
+ QString currentName;
+ QString previousName;
+ const QByteArray path(cmd.m_elementPath.toUtf8());
+ m_runtime->GetSlideInfo(path, current, previous, currentName, previousName);
+ QVariantList *requestData = new QVariantList();
+ requestData->append(QVariant(current));
+ requestData->append(QVariant(previous));
+ requestData->append(QVariant(currentName));
+ requestData->append(QVariant(previousName));
+
+ Q_EMIT requestResponse(cmd.m_elementPath, cmd.m_commandType, requestData);
+
+ break;
+ }
+ default:
+ qWarning() << __FUNCTION__ << "Unrecognized CommandType in command list!";
+ }
+ }
+
+ m_commands.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/Viewer/qmlviewer/Qt3DSRenderer.h b/src/Viewer/qmlviewer/Qt3DSRenderer.h
new file mode 100644
index 00000000..0037d9a6
--- /dev/null
+++ b/src/Viewer/qmlviewer/Qt3DSRenderer.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (c) 2016 NVIDIA CORPORATION.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DS_RENDERER_H
+#define Q3DS_RENDERER_H
+
+#include <QtGui/qopenglframebufferobject.h>
+#include <QtQuick/qquickframebufferobject.h>
+
+#include "Qt3DSViewerApp.h"
+#include "Qt3DSView.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSViewerSettings;
+class Q3DSPresentation;
+
+class Q3DSRenderer : public QObject,
+ public QQuickFramebufferObject::Renderer
+{
+ Q_OBJECT
+
+public:
+ Q3DSRenderer(bool visibleFlag, qt3ds::Qt3DSAssetVisitor *assetVisitor);
+ ~Q3DSRenderer();
+
+ QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override;
+
+ void setInitElements(bool inFlag) { m_initElements = inFlag; }
+ void processCommands();
+
+Q_SIGNALS:
+ void enterSlide(const QString &elementPath, unsigned int slide, const QString &slideName);
+ void exitSlide(const QString &elementPath, unsigned int slide, const QString &slideName);
+ void requestResponse(const QString &elementPath, CommandType commandType, void *requestData);
+ void presentationReady();
+
+protected:
+ static void onInitHandler(void *userData);
+ static void onUpdateHandler(void *userData);
+ bool initializeRuntime(QOpenGLFramebufferObject *inFbo);
+ void draw();
+ void render() override;
+ void synchronize(QQuickFramebufferObject *inView) override;
+ void releaseRuntime();
+
+protected:
+ bool m_visibleFlag; // Is the plugin visible? Prevents rendering hidden content.
+ CommandQueue m_commands; // A list of commands retrieved by the plugin to be applied.
+ bool m_initElements; // Flag set when the runtime is first ready to render.
+ Q3DSViewer::Q3DSViewerApp *m_runtime; // The Qt3DS viewer that renders all content.
+ QQuickWindow *m_window; // Window associated with the plugin; needed to reset OpenGL state.
+
+ bool m_initialized; // Has the runtime and OpenGL state been initialized?
+ bool m_initializationFailure; // Initialization failed, no point in trying to init again
+ qt3ds::Qt3DSAssetVisitor *m_visitor;
+
+ Q3DSViewerSettings *m_settings;
+ Q3DSPresentation *m_presentation;
+ QString m_error;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DS_RENDERER_H
diff --git a/src/Viewer/qmlviewer/Qt3DSView.cpp b/src/Viewer/qmlviewer/Qt3DSView.cpp
new file mode 100644
index 00000000..08103d5e
--- /dev/null
+++ b/src/Viewer/qmlviewer/Qt3DSView.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (c) 2016 NVIDIA CORPORATION.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSView.h"
+#include "Qt3DSRenderer.h"
+#include "q3dspresentationitem.h"
+
+#include <QtStudio3D/private/q3dsviewersettings_p.h>
+#include <QtStudio3D/private/q3dspresentation_p.h>
+#include <QtStudio3D/private/q3dssceneelement_p.h>
+#include <QtStudio3D/private/viewerqmlstreamproxy_p.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qfileinfo.h>
+#include <QtQuick/qquickwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+Q3DSView::Q3DSView()
+ : m_viewerSettings(nullptr)
+ , m_presentation(nullptr)
+ , m_emitRunningChange(false)
+ , m_isRunning(false)
+ , m_ignoreMouseEvents(false)
+ , m_ignoreWheelEvents(false)
+ , m_ignoreKeyboardEvents(false)
+ , m_pixelRatio(1.0)
+{
+ setMirrorVertically(true);
+ connect(this, &Q3DSView::windowChanged, this, &Q3DSView::handleWindowChanged);
+ connect(this, &Q3DSView::visibleChanged, this, &Q3DSView::handleVisibleChanged);
+
+ setIgnoreEvents(false, false, false);
+}
+
+Q3DSView::~Q3DSView()
+{
+}
+
+Q3DSPresentationItem *Q3DSView::presentation() const
+{
+ return m_presentation;
+}
+
+Q3DSViewerSettings *Q3DSView::viewerSettings() const
+{
+ return m_viewerSettings;
+}
+
+QString Q3DSView::error() const
+{
+ return m_error;
+}
+
+void Q3DSView::setError(const QString &error)
+{
+ if (error != m_error) {
+ m_error = error;
+ Q_EMIT errorChanged(m_error);
+ }
+}
+
+void Q3DSView::setIgnoreEvents(bool mouse, bool wheel, bool keyboard)
+{
+ // TODO: It might be beneficial to have these as properties so they could be acceessed from QML
+ m_ignoreMouseEvents = mouse;
+ m_ignoreWheelEvents = wheel;
+ m_ignoreKeyboardEvents = keyboard;
+
+ if (mouse)
+ setAcceptedMouseButtons(Qt::NoButton);
+ else
+ setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton);
+ setAcceptHoverEvents(!mouse);
+}
+
+void Q3DSView::componentComplete()
+{
+ const auto childObjs = children();
+ for (QObject *child : childObjs) {
+ auto settings = qobject_cast<Q3DSViewerSettings *>(child);
+ if (settings) {
+ if (m_viewerSettings)
+ qWarning("Duplicate ViewerSettings defined for Studio3D.");
+ else
+ m_viewerSettings = settings;
+ }
+ auto presentation = qobject_cast<Q3DSPresentationItem *>(child);
+ if (presentation) {
+ if (m_presentation)
+ qWarning("Duplicate Presentation defined for Studio3D.");
+ else
+ m_presentation = presentation;
+ }
+ }
+
+ if (!m_viewerSettings)
+ m_viewerSettings = new Q3DSViewerSettings(this);
+ if (!m_presentation)
+ m_presentation = new Q3DSPresentationItem(this);
+
+ m_viewerSettings->d_ptr->setCommandQueue(&m_pendingCommands);
+ m_presentation->d_ptr->setCommandQueue(&m_pendingCommands);
+
+ // Ensure qml stream proxy gets created on main thread
+ m_presentation->d_ptr->streamProxy();
+
+ QQuickFramebufferObject::componentComplete();
+}
+
+void Q3DSView::handleWindowChanged(QQuickWindow *window)
+{
+ if (!window)
+ return;
+
+ window->setClearBeforeRendering(false);
+ m_pixelRatio = window->devicePixelRatio();
+
+ // Call tick every frame of the GUI thread to notify QML about new frame via frameUpdate signal
+ connect(window, &QQuickWindow::afterAnimating, this, &Q3DSView::tick);
+ // Call update after the frame is handled to queue another frame
+ connect(window, &QQuickWindow::afterSynchronizing, this, &Q3DSView::update);
+}
+
+// Queue up a command to inform the renderer of a newly-changed visible/hidden status.
+void Q3DSView::handleVisibleChanged()
+{
+ m_pendingCommands.m_visibleChanged = true;
+ m_pendingCommands.m_visible = isVisible();
+}
+
+void Q3DSView::reset()
+{
+ // Fake a source change to trigger a reloading of the presentation
+ m_pendingCommands.m_sourceChanged = true;
+ m_pendingCommands.m_source = m_presentation->source();
+}
+
+void Q3DSView::requestResponseHandler(const QString &elementPath, CommandType commandType,
+ void *requestData)
+{
+ switch (commandType) {
+ case CommandType_RequestSlideInfo: {
+ Q3DSSceneElement *handler = qobject_cast<Q3DSSceneElement *>(
+ m_presentation->registeredElement(elementPath));
+ if (handler)
+ handler->d_ptr->requestResponseHandler(commandType, requestData);
+ else
+ qWarning() << __FUNCTION__ << "RequestSlideInfo response got for unregistered scene.";
+ break;
+ }
+ default:
+ qWarning() << __FUNCTION__ << "Unknown command type.";
+ break;
+ }
+}
+
+// Create the Q3DSRenderer. Invoked automatically by the QML scene graph.
+QQuickFramebufferObject::Renderer *Q3DSView::createRenderer() const
+{
+ // It is "illegal" to create a connection between the renderer
+ // and the plugin, and vice-versa. The only valid time the two
+ // may communicate is during Q3DSRenderer::synchronize().
+ Q3DSRenderer *renderer = new Q3DSRenderer(isVisible(), m_presentation->d_ptr->streamProxy());
+
+ connect(renderer, &Q3DSRenderer::enterSlide,
+ m_presentation->d_ptr, &Q3DSPresentationPrivate::handleSlideEntered);
+ connect(renderer, &Q3DSRenderer::exitSlide,
+ m_presentation, &Q3DSPresentation::slideExited);
+ connect(renderer, &Q3DSRenderer::requestResponse,
+ this, &Q3DSView::requestResponseHandler);
+ connect(renderer, &Q3DSRenderer::presentationReady,
+ this, &Q3DSView::presentationReady);
+ return renderer;
+}
+
+bool Q3DSView::isRunning() const
+{
+ return m_isRunning;
+}
+
+/** Emit QML `runningChanged` and `frameUpdate` and signals.
+ * This method is called every frame, and emits the `frameUpdate` signal every frame,
+ * regardless of plugin visibility. This allows a hidden Qt3DSView to still process
+ * information every frame, even though the Renderer is not rendering.
+ *
+ * To prevent expensive onFrameUpdate handlers from being processed when hidden,
+ * add an early return to the top like:
+ *
+ * onFrameUpdate: {
+ * if (!visible) return;
+ * ...
+ * }
+ */
+void Q3DSView::tick()
+{
+ if (m_emitRunningChange) {
+ m_isRunning = true;
+ Q_EMIT runningChanged(true);
+ m_emitRunningChange = false;
+ }
+
+ // Don't call onFrameUpdate until after onInitialize has been called
+ if (m_isRunning) {
+ // Give QML an opportunity to change Qt3DS values every frame
+ Q_EMIT frameUpdate();
+ }
+}
+
+// Copies the list of commands previously queued up. Called by Q3DSRenderer::synchronize().
+void Q3DSView::getCommands(bool emitInitialize, CommandQueue &renderQueue)
+{
+ if (emitInitialize)
+ m_emitRunningChange = true;
+
+ renderQueue.copyCommands(m_pendingCommands);
+ m_pendingCommands.clear();
+}
+
+void Q3DSView::mousePressEvent(QMouseEvent *event)
+{
+ if (!m_ignoreMouseEvents) {
+ if (m_pixelRatio != 1.0) {
+ QMouseEvent scaledEvent(event->type(), event->pos() * m_pixelRatio,
+ event->button(), event->buttons(), event->modifiers());
+ m_presentation->mousePressEvent(&scaledEvent);
+ } else {
+ m_presentation->mousePressEvent(event);
+ }
+ }
+}
+
+void Q3DSView::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (!m_ignoreMouseEvents) {
+ if (m_pixelRatio != 1.0) {
+ QMouseEvent scaledEvent(event->type(), event->pos() * m_pixelRatio,
+ event->button(), event->buttons(), event->modifiers());
+ m_presentation->mouseReleaseEvent(&scaledEvent);
+ } else {
+ m_presentation->mouseReleaseEvent(event);
+ }
+ }
+}
+
+void Q3DSView::mouseMoveEvent(QMouseEvent *event)
+{
+ if (!m_ignoreMouseEvents) {
+ if (m_pixelRatio != 1.0) {
+ QMouseEvent scaledEvent(event->type(), event->pos() * m_pixelRatio,
+ event->button(), event->buttons(), event->modifiers());
+ m_presentation->mouseMoveEvent(&scaledEvent);
+ } else {
+ m_presentation->mouseMoveEvent(event);
+ }
+ }
+}
+
+void Q3DSView::wheelEvent(QWheelEvent *event)
+{
+ if (!m_ignoreWheelEvents)
+ m_presentation->wheelEvent(event);
+}
+
+void Q3DSView::keyPressEvent(QKeyEvent *event)
+{
+ if (m_ignoreKeyboardEvents)
+ return;
+ m_presentation->keyPressEvent(event);
+}
+
+void Q3DSView::keyReleaseEvent(QKeyEvent *event)
+{
+ if (m_ignoreKeyboardEvents)
+ return;
+ if (!event->isAutoRepeat())
+ m_presentation->keyReleaseEvent(event);
+}
+
+QT_END_NAMESPACE
diff --git a/src/Viewer/qmlviewer/Qt3DSView.h b/src/Viewer/qmlviewer/Qt3DSView.h
new file mode 100644
index 00000000..ab104d4c
--- /dev/null
+++ b/src/Viewer/qmlviewer/Qt3DSView.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (c) 2016 NVIDIA CORPORATION.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DS_VIEW_H
+#define Q3DS_VIEW_H
+
+
+#include <QtStudio3D/private/q3dscommandqueue_p.h>
+#include <QtGui/qopenglframebufferobject.h>
+#include <QtQuick/qquickframebufferobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSRenderer;
+class Q3DSViewerSettings;
+class Q3DSPresentationItem;
+
+class Q3DSView : public QQuickFramebufferObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged)
+ Q_PROPERTY(Q3DSPresentationItem *presentation READ presentation CONSTANT)
+ Q_PROPERTY(Q3DSViewerSettings *viewerSettings READ viewerSettings CONSTANT)
+ Q_PROPERTY(QString error READ error NOTIFY errorChanged)
+
+public:
+ Q3DSView();
+ virtual ~Q3DSView();
+
+ QQuickFramebufferObject::Renderer *createRenderer() const override;
+
+ bool isRunning() const;
+ Q3DSPresentationItem *presentation() const;
+ Q3DSViewerSettings *viewerSettings() const;
+ QString error() const;
+ void setError(const QString &error);
+
+ void getCommands(bool emitInitialize, CommandQueue &renderQueue);
+
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void wheelEvent(QWheelEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+
+ void setIgnoreEvents(bool mouse, bool wheel, bool keyboard);
+
+ void componentComplete() override;
+
+Q_SIGNALS:
+ void frameUpdate();
+ void runningChanged(bool initialized);
+ void errorChanged(const QString &error);
+ Q_REVISION(1) void presentationReady();
+
+public Q_SLOTS:
+ void reset();
+
+protected Q_SLOTS:
+ void handleWindowChanged(QQuickWindow *window);
+ void handleVisibleChanged();
+ void tick();
+ void requestResponseHandler(const QString &elementPath, CommandType commandType,
+ void *requestData);
+
+protected:
+ Q3DSViewerSettings *m_viewerSettings;
+ Q3DSPresentationItem *m_presentation;
+
+ bool m_emitRunningChange;
+ bool m_isRunning;
+ bool m_ignoreMouseEvents;
+ bool m_ignoreWheelEvents;
+ bool m_ignoreKeyboardEvents;
+
+ CommandQueue m_pendingCommands;
+ qreal m_pixelRatio;
+ QString m_error;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DS_VIEW_H
diff --git a/src/Viewer/qmlviewer/Qt3DSViewPlugin.cpp b/src/Viewer/qmlviewer/Qt3DSViewPlugin.cpp
new file mode 100644
index 00000000..389e1cc4
--- /dev/null
+++ b/src/Viewer/qmlviewer/Qt3DSViewPlugin.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (c) 2016 NVIDIA CORPORATION.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSViewPlugin.h"
+
+#include <QtQml/qqml.h>
+
+#include <QtStudio3D/private/q3dsviewersettings_p.h>
+
+#include "Qt3DSView.h"
+#include "q3dspresentationitem.h"
+#include "q3dsqmlstream.h"
+#include "q3dsqmlsubpresentationsettings.h"
+#include "q3dssceneelement.h"
+#include "q3dsdatainput.h"
+
+QT_BEGIN_NAMESPACE
+
+//TODO: remove if works with X
+/*
+#if defined _LINUX || defined linux
+extern "C" {
+bool InitializeGL();
+}
+#endif
+*/
+
+void Q3DSViewPlugin::registerTypes(const char *uri)
+{
+ Q_ASSERT(uri == QLatin1String("QtStudio3D"));
+//TODO: remove if works with X
+/*
+#if defined _LINUX || defined linux
+ // To initialize GL functions for Qt3DS GL bindings
+ InitializeGL();
+#endif
+*/
+ // @uri Studio3D
+ // Version 1.0
+ qmlRegisterType<Q3DSView>(uri, 1, 0, "Studio3D");
+ qmlRegisterType<Q3DSViewerSettings>(uri, 1, 0, "ViewerSettings");
+ qmlRegisterType<Q3DSPresentationItem>(uri, 1, 0, "Presentation");
+ qmlRegisterType<Q3DSSceneElement>(uri, 1, 0, "SceneElement");
+ qmlRegisterType<Q3DSElement>(uri, 1, 0, "Element");
+ qmlRegisterType<Q3DSQmlStream>(uri, 1, 0, "QmlStream");
+ qmlRegisterType<Q3DSSubPresentationSettings>(uri, 1, 0, "SubPresentationSettings");
+
+ // Version 1.1
+ qmlRegisterType<Q3DSView, 1>(uri, 1, 1, "Studio3D");
+ qmlRegisterType<Q3DSDataInput>(uri, 1, 1, "DataInput");
+ qmlRegisterType<Q3DSPresentationItem, 1>(uri, 1, 1, "Presentation");
+}
+
+QT_END_NAMESPACE
diff --git a/src/Viewer/qmlviewer/Qt3DSViewPlugin.h b/src/Viewer/qmlviewer/Qt3DSViewPlugin.h
new file mode 100644
index 00000000..1d1a6fff
--- /dev/null
+++ b/src/Viewer/qmlviewer/Qt3DSViewPlugin.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (c) 2016 NVIDIA CORPORATION.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSVIEW_PLUGIN_H
+#define Q3DSVIEW_PLUGIN_H
+
+#include <QtQml/qqmlextensionplugin.h>
+
+static void initResources()
+{
+#ifdef QT_STATIC
+ Q_INIT_RESOURCE(qmake_QtStudio3D);
+#endif
+}
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSViewPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+
+public:
+ Q3DSViewPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent)
+ {
+ initResources();
+ }
+ void registerTypes(const char *uri) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DSVIEW_PLUGIN_H
diff --git a/src/Viewer/qmlviewer/q3dspresentationitem.cpp b/src/Viewer/qmlviewer/q3dspresentationitem.cpp
new file mode 100644
index 00000000..6c4c8e36
--- /dev/null
+++ b/src/Viewer/qmlviewer/q3dspresentationitem.cpp
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dspresentationitem.h"
+
+#include <QtStudio3D/q3dssceneelement.h>
+#include <QtStudio3D/q3dsdatainput.h>
+#include <QtStudio3D/private/q3dspresentation_p.h>
+#include <QtStudio3D/private/viewerqmlstreamproxy_p.h>
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+Q3DSPresentationItem::Q3DSPresentationItem(QObject *parent)
+ : Q3DSPresentation(parent)
+ , m_subPresentationSettings(nullptr)
+{
+}
+
+Q3DSPresentationItem::~Q3DSPresentationItem()
+{
+}
+
+Q3DSSubPresentationSettings *Q3DSPresentationItem::subPresentationSettings() const
+{
+ return m_subPresentationSettings;
+}
+
+QQmlListProperty<QObject> Q3DSPresentationItem::qmlChildren()
+{
+ return QQmlListProperty<QObject>(this, nullptr, &appendQmlChildren, nullptr, nullptr, nullptr);
+}
+
+void Q3DSPresentationItem::appendQmlChildren(QQmlListProperty<QObject> *list, QObject *obj)
+{
+ auto item = qobject_cast<Q3DSPresentationItem *>(list->object);
+ if (item) {
+ auto scene = qobject_cast<Q3DSSceneElement *>(obj);
+ if (scene) {
+ if (item->registeredElement(scene->elementPath()))
+ qWarning() << __FUNCTION__ << "A duplicate SceneElement defined for Presentation.";
+ else
+ item->registerElement(scene);
+ } else {
+ auto studioElement = qobject_cast<Q3DSElement *>(obj);
+ if (studioElement) {
+ if (item->registeredElement(studioElement->elementPath()))
+ qWarning() << __FUNCTION__ << "A duplicate Element defined for Presentation.";
+ else
+ item->registerElement(studioElement);
+ } else {
+ auto subPresSettings = qobject_cast<Q3DSSubPresentationSettings *>(obj);
+ if (subPresSettings) {
+ if (item->m_subPresentationSettings) {
+ qWarning() << __FUNCTION__
+ << "Duplicate SubPresentationSettings defined for Presentation.";
+ } else {
+ item->m_subPresentationSettings = subPresSettings;
+ item->d_ptr->streamProxy()->setSettings(subPresSettings);
+ }
+ } else {
+ auto dataInput = qobject_cast<Q3DSDataInput *>(obj);
+ if (item->registeredDataInput(dataInput->name())) {
+ qWarning() << __FUNCTION__
+ << "Duplicate DataInput defined for Presentation.";
+ } else {
+ item->registerDataInput(dataInput);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/Viewer/qmlviewer/q3dspresentationitem.h b/src/Viewer/qmlviewer/q3dspresentationitem.h
new file mode 100644
index 00000000..63270594
--- /dev/null
+++ b/src/Viewer/qmlviewer/q3dspresentationitem.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSPRESENTATIONITEM_H
+#define Q3DSPRESENTATIONITEM_H
+
+#include "q3dsqmlsubpresentationsettings.h"
+
+#include <QtStudio3D/q3dspresentation.h>
+#include <QtQml/qqmllist.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSPresentationItem : public Q3DSPresentation
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlListProperty<QObject> qmlChildren READ qmlChildren DESIGNABLE false)
+ Q_PROPERTY(Q3DSSubPresentationSettings *subPresentationSettings READ subPresentationSettings CONSTANT)
+ Q_CLASSINFO("DefaultProperty", "qmlChildren")
+
+public:
+ explicit Q3DSPresentationItem(QObject *parent = nullptr);
+ ~Q3DSPresentationItem();
+
+ Q3DSSubPresentationSettings *subPresentationSettings() const;
+
+ QQmlListProperty<QObject> qmlChildren();
+
+public Q_SLOTS:
+ static void appendQmlChildren(QQmlListProperty<QObject> *list, QObject *obj);
+
+private:
+ Q3DSSubPresentationSettings *m_subPresentationSettings;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DSPRESENTATIONITEM_H
diff --git a/src/Viewer/qmlviewer/qmldir b/src/Viewer/qmlviewer/qmldir
new file mode 100644
index 00000000..381f7e42
--- /dev/null
+++ b/src/Viewer/qmlviewer/qmldir
@@ -0,0 +1,3 @@
+module QtStudio3D
+plugin declarative_qtstudio3d
+classname Q3DSViewPlugin
diff --git a/src/Viewer/qmlviewer/qmlviewer.pro b/src/Viewer/qmlviewer/qmlviewer.pro
new file mode 100644
index 00000000..dca7feab
--- /dev/null
+++ b/src/Viewer/qmlviewer/qmlviewer.pro
@@ -0,0 +1,34 @@
+include($$PWD/../../Runtime/commoninclude.pri)
+
+QT += qml quick opengl studio3d-private
+CONFIG += plugin
+
+qtHaveModule(multimedia) {
+DEFINES += PLATFORM_HAS_QT_MULTIMEDIA_LIB
+QT += multimedia
+}
+
+TARGET = qtstudio3d
+TARGETPATH = QtStudio3D
+IMPORT_VERSION = 1.0
+
+SOURCES += \
+ Qt3DSViewPlugin.cpp \
+ Qt3DSView.cpp \
+ Qt3DSRenderer.cpp \
+ q3dspresentationitem.cpp
+
+HEADERS += \
+ Qt3DSViewPlugin.h \
+ Qt3DSView.h \
+ Qt3DSRenderer.h \
+ q3dspresentationitem.h
+
+LIBS += \
+ -lqt3dsruntime$$qtPlatformTargetSuffix() \
+ -lqt3dsqmlstreamer$$qtPlatformTargetSuffix()
+
+OTHER_FILES += \
+ qmldir
+
+load(qml_plugin)