diff options
Diffstat (limited to 'src/viewer/Qt3DSViewerApp.cpp')
-rw-r--r-- | src/viewer/Qt3DSViewerApp.cpp | 1001 |
1 files changed, 1001 insertions, 0 deletions
diff --git a/src/viewer/Qt3DSViewerApp.cpp b/src/viewer/Qt3DSViewerApp.cpp new file mode 100644 index 0000000..3199124 --- /dev/null +++ b/src/viewer/Qt3DSViewerApp.cpp @@ -0,0 +1,1001 @@ +/**************************************************************************** +** +** 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 <algorithm> + +#include "EnginePrefix.h" +#include "Qt3DSRuntimeView.h" +#include "Qt3DSViewerApp.h" +#include "Qt3DSTegraInputEngine.h" +#include "Qt3DSInputFrame.h" // keyboard mapping +#include "foundation/Qt3DSTime.h" +#include "Qt3DSFNDTimer.h" +#include "Qt3DSAudioPlayer.h" +#include "Qt3DSImportMesh.h" + +#include <QList> +#include <QFileInfo> +#include <QDebug> + +#ifdef _MACOSX +#include <mach-o/dyld.h> +#endif + +namespace qt3ds { +void Qt3DSAssert(const char *exp, const char *file, int line, bool *ignore) +{ + Q_UNUSED(ignore) + qCritical() << "Assertion thrown: " << file << " " << line << " " << exp; +} +} + +#ifndef EASTL_DEBUG_BREAK +void EASTL_DEBUG_BREAK() +{ + qFatal("EASTL_DEBUG_BREAK: Assertion blown"); +} + +#endif + +#ifdef _WIN32 +#include <Xinput.h> +#include <ShellAPI.h> +#include "Qt3DSInputFrame.h" + +void HandleController(Q3DStudio::IRuntimeView &inView) +{ + static const class XInputLibrary + { + private: + typedef DWORD(__stdcall *GetStateFunction)(DWORD, XINPUT_STATE *); + + public: + XInputLibrary() + : m_module(NULL) + , GetState(NULL) + { + m_module = LoadLibraryA("XINPUT9_1_0.DLL"); + if (m_module != NULL) { + GetState = reinterpret_cast<GetStateFunction>(GetProcAddress(m_module, + "XInputGetState")); + } + } + + ~XInputLibrary() + { + if (m_module != NULL) + FreeLibrary(m_module); + } + + GetStateFunction GetState; + + private: + HMODULE m_module; + } library; + + static const DWORD MAX_USERS = 4; + static const Q3DStudio::FLOAT STICK_RANGE = 32767; + static const Q3DStudio::FLOAT TRIGGER_RANGE = 255; + + static const struct ButtonEntry + { + WORD XBoxButton; + Q3DStudio::INT32 button; + } buttonMap[] = { + { XINPUT_GAMEPAD_START, Q3DStudio::BUTTON_START }, + { XINPUT_GAMEPAD_BACK, Q3DStudio::BUTTON_SELECT }, + { XINPUT_GAMEPAD_LEFT_THUMB, Q3DStudio::BUTTON_THUMBL }, + { XINPUT_GAMEPAD_RIGHT_THUMB, Q3DStudio::BUTTON_THUMBR }, + { XINPUT_GAMEPAD_LEFT_SHOULDER, Q3DStudio::BUTTON_L1 }, + { XINPUT_GAMEPAD_RIGHT_SHOULDER, Q3DStudio::BUTTON_R1 }, + { XINPUT_GAMEPAD_A, Q3DStudio::BUTTON_A }, + { XINPUT_GAMEPAD_B, Q3DStudio::BUTTON_B }, + { XINPUT_GAMEPAD_X, Q3DStudio::BUTTON_X }, + { XINPUT_GAMEPAD_Y, Q3DStudio::BUTTON_Y } }; + + static DWORD userLastPacketNumber[MAX_USERS] = { 0 }; + static WORD userLastButtons[MAX_USERS] = { 0 }; + + Q3DStudio::CInputEngine *input = inView.GetInputEngine(); + if (input != NULL && library.GetState) { + // for each controller + for (DWORD userIndex = 0; userIndex < MAX_USERS; ++userIndex) { + DWORD &lastPacketNumber = userLastPacketNumber[userIndex]; + WORD &lastButtons = userLastButtons[userIndex]; + + // get the state + XINPUT_STATE state = { 0 }; + if (library.GetState(userIndex, &state) == ERROR_SUCCESS + && state.dwPacketNumber != lastPacketNumber) { + using namespace Q3DStudio; + const XINPUT_GAMEPAD &gamepad = state.Gamepad; + const WORD &buttons = gamepad.wButtons; + + // handle button changes + DWORD buttonChanges = buttons ^ lastButtons; + for (size_t buttonMapIndex = 0; + buttonMapIndex < sizeof(buttonMap) / sizeof(buttonMap[0]); ++buttonMapIndex) { + const ButtonEntry &buttonEntry = buttonMap[buttonMapIndex]; + if (buttonEntry.XBoxButton & buttonChanges) { + input->HandleButton(buttonEntry.button, + buttons & buttonEntry.XBoxButton); + } + } + + // handle gamepad + if (buttons & XINPUT_GAMEPAD_DPAD_LEFT) + input->HandleAxis(AXIS_HAT_X, -1); + else if (buttons & XINPUT_GAMEPAD_DPAD_RIGHT) + input->HandleAxis(AXIS_HAT_X, 1); + else + input->HandleAxis(AXIS_HAT_X, 0); + if (buttons & XINPUT_GAMEPAD_DPAD_UP) + input->HandleAxis(AXIS_HAT_Y, -1); + else if (buttons & XINPUT_GAMEPAD_DPAD_DOWN) + input->HandleAxis(AXIS_HAT_Y, 1); + else + input->HandleAxis(AXIS_HAT_Y, 0); + + // handle sticks and triggers + input->HandleAxis(AXIS_X, gamepad.sThumbLX / STICK_RANGE); + input->HandleAxis(AXIS_Y, gamepad.sThumbLY / -STICK_RANGE); + input->HandleAxis(AXIS_Z, gamepad.sThumbRX / STICK_RANGE); + input->HandleAxis(AXIS_RZ, gamepad.sThumbRY / -STICK_RANGE); + input->HandleAxis(AXIS_LTRIGGER, gamepad.bLeftTrigger / TRIGGER_RANGE); + input->HandleAxis(AXIS_RTRIGGER, gamepad.bRightTrigger / TRIGGER_RANGE); + + // save the state + lastButtons = buttons; + lastPacketNumber = state.dwPacketNumber; + } + } + } +} +#endif + +using namespace Q3DStudio; + +struct WindowRect +{ + int x; + int y; + int width; + int height; +}; + +void PerfLogSetStringData(const char *) +{ +} // Dummy defination when TCP perf logging isnt used + +Q3DStudio::Qt3DSFNDTimer g_GlobalTimeProvider; + +qint64 GetTimeUST() +{ + // this needs to be nano seconds + Q3DStudio::ITimeProvider &theTimer = g_GlobalTimeProvider; + return theTimer.GetCurrentTimeMicroSeconds() * 1000; +} + +void initResource() { + // init runtime static resources + Q_INIT_RESOURCE(res); +} + +namespace Q3DSViewer { + +struct SWindowSystemImpl : public Q3DStudio::IWindowSystem +{ + QSize m_size; + int m_OffscreenID; + int m_DepthBitCount; + + QSize GetWindowDimensions() override { return m_size; } + void SetWindowDimensions(const QSize &inSize) override + { + m_size = inSize; + } + // For platforms that support it, we get the egl info for render plugins + // Feel free to return NULL. + SEGLInfo *GetEGLInfo() override { return nullptr; } + // on some systems we allow our default render target to be a offscreen buffer + // otherwise return 0; + int GetDefaultRenderTargetID() override { return m_OffscreenID; } + // returns the depth buffer bit count for the render window + int GetDepthBitCount() override { return m_DepthBitCount; } +}; + +class Q3DSViewerAppImpl +{ +public: + Q3DSViewerAppImpl(Q3DStudio::IAudioPlayer *inAudioPlayer) + : m_view(nullptr) + , m_appInitSuccessful(false) + , m_AudioPlayer(inAudioPlayer) + { +#ifndef EMBEDDED_LINUX + initResource(); +#endif + } + Q3DStudio::IRuntimeView *m_view; + bool m_appInitSuccessful; ///< true if m_view is initialized successful + + std::vector<int> m_mouseButtons; + Q3DStudio::IWindowSystem *m_WindowSystem; + + IAudioPlayer *m_AudioPlayer; + QVector<QMouseEvent *> m_pendingEvents; + + QString m_error; + qt3dsimp::MeshBuilder *m_meshBuilder = nullptr; + + void queueMouseEvent(int button, int pressed, int x, int y) + { + QMouseEvent *e = new QMouseEvent(pressed ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease, + QPointF(x, y), (Qt::MouseButton)button, + (Qt::MouseButtons)button, 0); + e->setTimestamp(static_cast<ulong>(GetTimeUST())); + m_pendingEvents.append(e); + } + + qt3dsimp::Mesh *buildMesh(const Q3DSViewer::MeshData &meshData, QString &error) + { + using namespace qt3dsimp; + using namespace qt3ds::render; + + // Do some basic validation of the meshData + if (meshData.m_vertexBuffer.size() == 0) { + error = QObject::tr("Vertex buffer empty"); + return nullptr; + } + if (meshData.m_attributeCount == 0) { + error = QObject::tr("No attributes defined"); + return nullptr; + } + + if (!m_meshBuilder) + m_meshBuilder = &MeshBuilder::CreateMeshBuilder(); + m_meshBuilder->Reset(); + m_meshBuilder->SetDrawParameters( + static_cast<NVRenderDrawMode::Enum>(meshData.m_primitiveType), + NVRenderWinding::CounterClockwise); + + // The expectation is that the vertex buffer included in meshData is already properly + // formatted and doesn't need further processing. + + // Validate attributes + QVector<NVRenderVertexBufferEntry> vBufEntries; + NVRenderComponentTypes::Enum indexBufferComponentType = NVRenderComponentTypes::Unknown; + int indexBufferTypeSize = 0; + for (int i = 0; i < meshData.m_attributeCount; ++i) { + const Q3DSViewer::MeshData::Attribute &att = meshData.m_attributes[i]; + auto componentType = static_cast<NVRenderComponentTypes::Enum>(att.componentType); + if (att.semantic == Q3DSViewer::MeshData::Attribute::IndexSemantic) { + indexBufferComponentType = componentType; + indexBufferTypeSize = att.typeSize(); + } else { + const char *name = nullptr; + switch (att.semantic) { + case MeshData::Attribute::PositionSemantic: + name = Mesh::GetPositionAttrName(); + break; + case MeshData::Attribute::NormalSemantic: + name = Mesh::GetNormalAttrName(); + break; + case MeshData::Attribute::TexCoordSemantic: + name = Mesh::GetUVAttrName(); + break; + case MeshData::Attribute::TangentSemantic: + name = Mesh::GetTexTanAttrName(); + break; + case MeshData::Attribute::BinormalSemantic: + name = Mesh::GetTexBinormalAttrName(); + break; + default: + error = QObject::tr("Warning: Invalid attribute semantic: %1") + .arg(att.semantic); + return nullptr; + } + vBufEntries << NVRenderVertexBufferEntry(name, componentType, + QT3DSU32(att.componentCount()), + QT3DSU32(att.offset)); + } + } + m_meshBuilder->SetVertexBuffer( + toConstDataRef(reinterpret_cast<const NVRenderVertexBufferEntry *>( + vBufEntries.constData()), QT3DSU32(vBufEntries.count())), + QT3DSU32(meshData.m_stride), + toConstDataRef(const_cast<QT3DSU8 *>(reinterpret_cast<const QT3DSU8 *>( + meshData.m_vertexBuffer.constData())), + QT3DSU32(meshData.m_vertexBuffer.size()))); + + int vertexCount = 0; + if (indexBufferComponentType != NVRenderComponentTypes::Unknown) { + m_meshBuilder->SetIndexBuffer( + toConstDataRef(const_cast<QT3DSU8 *>(reinterpret_cast<const QT3DSU8 *>( + meshData.m_indexBuffer.constData())), + QT3DSU32(meshData.m_indexBuffer.size())), + indexBufferComponentType); + vertexCount = meshData.m_indexBuffer.size() / indexBufferTypeSize; + } else { + vertexCount = meshData.m_vertexBuffer.size() / meshData.m_stride; + } + + m_meshBuilder->AddMeshSubset(Mesh::s_DefaultName, QT3DSU32(vertexCount)); + + return &m_meshBuilder->GetMesh(); + } +}; + +///< @brief contructor +Q3DSViewerApp::Q3DSViewerApp(void *glContext, Q3DStudio::IAudioPlayer *inAudioPlayer, + QElapsedTimer *startupTimer) + : m_Impl(*new Q3DSViewerAppImpl(inAudioPlayer)) + , m_startupTimer(startupTimer) +{ + Q_UNUSED(glContext) + m_Impl.m_WindowSystem = new SWindowSystemImpl(); +} + +///< @brief destructor +Q3DSViewerApp::~Q3DSViewerApp() +{ + qDeleteAll(m_Impl.m_pendingEvents); + + if (m_Impl.m_meshBuilder) + m_Impl.m_meshBuilder->Release(); + + delete m_Impl.m_AudioPlayer; + + if (m_Impl.m_view) { + disconnect(m_Impl.m_view->signalProxy(), nullptr); + + m_Impl.m_view->Cleanup(); + m_Impl.m_view->release(); + + if (GetMemoryManager().GetLineTracker()) + GetMemoryManager().GetLineTracker()->Report(); + + GetMemoryManager().Release(); + } + delete static_cast<SWindowSystemImpl *>(m_Impl.m_WindowSystem); +} + +void Q3DSViewerApp::setOffscreenId(int offscreenID) +{ + static_cast<SWindowSystemImpl *>(m_Impl.m_WindowSystem)->m_OffscreenID + = offscreenID; + if (m_Impl.m_view && m_Impl.m_view->GetTegraRenderEngine()) + m_Impl.m_view->GetTegraRenderEngine()->ensureRenderTarget(); +} + +bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFormat &format, + int offscreenID, const QString &source, + const QStringList &variantList, + bool delayedLoading, + qt3ds::Qt3DSAssetVisitor *assetVisitor) +{ + bool hasValidPresentationFile = !source.isEmpty(); + + QFileInfo info(source); + if (!info.exists()) { + m_Impl.m_error + = QObject::tr("Failed to initialize viewer, presentation doesn't exist"); + qCritical() << m_Impl.m_error; + return false; + } + + m_Impl.m_WindowSystem->SetWindowDimensions(QSize(winWidth, winHeight)); + + // create our internal application + if (hasValidPresentationFile && !m_Impl.m_view) { + static_cast<SWindowSystemImpl *>(m_Impl.m_WindowSystem)->m_OffscreenID = offscreenID; + static_cast<SWindowSystemImpl *>(m_Impl.m_WindowSystem)->m_DepthBitCount + = format.depthBufferSize(); + + // create internal app + m_Impl.m_view = &IRuntimeView::Create(g_GlobalTimeProvider, *m_Impl.m_WindowSystem, + m_Impl.m_AudioPlayer, m_startupTimer); + + if (assetVisitor) + m_Impl.m_view->setAssetVisitor(assetVisitor); + + m_Impl.m_appInitSuccessful = m_Impl.m_view->BeginLoad(source, variantList); + + if (m_Impl.m_appInitSuccessful == false) { + m_Impl.m_error = QObject::tr("Viewer launch failure! Failed to load: '%1'").arg(source); + m_Impl.m_error.append("\n"); + qCritical() << m_Impl.m_error; + return false; + } + + bool success = m_Impl.m_view->InitializeGraphics(format, delayedLoading); + if (!success) { + m_Impl.m_error = QObject::tr("Viewer launch failure! Failed to load: '%1'").arg(source); + m_Impl.m_error.append("\n"); + qCritical() << m_Impl.m_error; + return false; + } + + // Connect signals + connect(m_Impl.m_view->signalProxy(), + &QRuntimeViewSignalProxy::SigSlideEntered, this, &Q3DSViewerApp::SigSlideEntered); + connect(m_Impl.m_view->signalProxy(), + &QRuntimeViewSignalProxy::SigSlideExited, this, &Q3DSViewerApp::SigSlideExited); + connect(m_Impl.m_view->signalProxy(), + &QRuntimeViewSignalProxy::SigCustomSignal, this, &Q3DSViewerApp::SigCustomSignal); + connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigDataOutputValueUpdated, + this, &Q3DSViewerApp::SigDataOutputValueUpdated); + QMetaObject::Connection *presReadyconn = new QMetaObject::Connection(); + *presReadyconn = connect(m_Impl.m_view->signalProxy(), + &QRuntimeViewSignalProxy::SigPresentationReady, [&, presReadyconn]{ + // We receive presentation ready signal from runtime when animations and properties + // have been updated. + Q_EMIT SigPresentationReady(); + disconnect(*presReadyconn); + delete presReadyconn; + }); + connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigElementsCreated, this, + &Q3DSViewerApp::SigElementsCreated); + connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigMaterialsCreated, this, + &Q3DSViewerApp::SigMaterialsCreated); + + Resize(winWidth, winHeight); + + Q_EMIT SigPresentationLoaded(); + } + return true; +} + +bool Q3DSViewerApp::IsInitialised(void) +{ + return m_Impl.m_view != nullptr && m_Impl.m_appInitSuccessful; +} + +int Q3DSViewerApp::GetWindowHeight() +{ + return m_Impl.m_WindowSystem->GetWindowDimensions().width(); +} + +int Q3DSViewerApp::GetWindowWidth() +{ + return m_Impl.m_WindowSystem->GetWindowDimensions().height(); +} + +void Q3DSViewerApp::setupSearchPath(std::vector<std::string> &cmdLineArgs) +{ + // setup some additional search path + // index 0 contains the module directory +#ifdef _MACOSX + char buf[2056]; + uint32_t bufsize = 2056; + _NSGetExecutablePath(buf, &bufsize); + std::string theModuleDirectory = buf; +#else + std::string theModuleDirectory = cmdLineArgs[0]; +#endif + std::string::size_type pos = theModuleDirectory.rfind('\\'); + if (pos == std::string::npos) + pos = theModuleDirectory.rfind('/'); + + // Include the slash + theModuleDirectory = theModuleDirectory.substr(0, pos + 1); + + // TODO: Search path needs to be set up properly + //NvFAppendSearchPath(theModuleDirectory.c_str()); +} + +void Q3DSViewerApp::Render() +{ + if (m_Impl.m_view && m_Impl.m_view->GetTegraRenderEngine()) { + if (m_Impl.m_appInitSuccessful) { + for (QEvent *e : m_Impl.m_pendingEvents) { + m_Impl.m_view->HandleMessage(e); + delete e; + } + m_Impl.m_pendingEvents.clear(); +#ifdef WIN32 + HandleController(*m_Impl.m_view); +#endif + + m_Impl.m_view->Render(); + } + } +} + +void Q3DSViewerApp::SaveState() +{ + if (!m_Impl.m_view) + return; + + if (m_Impl.m_view->GetTegraRenderEngine()) { + ITegraRenderStateManager &manager = + m_Impl.m_view->GetTegraRenderEngine()->GetTegraRenderStateManager(); + + manager.SaveAllState(); + } +} + +void Q3DSViewerApp::RestoreState() +{ + if (!m_Impl.m_view) + return; + + if (m_Impl.m_view->GetTegraRenderEngine()) { + ITegraRenderStateManager &manager = + m_Impl.m_view->GetTegraRenderEngine()->GetTegraRenderStateManager(); + + manager.RestoreAllState(); + } +} + +bool Q3DSViewerApp::WasLastFrameDirty() +{ + if (m_Impl.m_view) + return m_Impl.m_view->WasLastFrameDirty(); + return false; +} + +QString Q3DSViewerApp::error() +{ + QString error = m_Impl.m_error; + m_Impl.m_error.clear(); + return error; +} + +void Q3DSViewerApp::setDelayedLoading(bool enable) +{ + if (!m_Impl.m_view) + return; + m_Impl.m_view->setDelayedLoading(enable); +} + +void Q3DSViewerApp::preloadSlide(const QString &slide) +{ + if (!m_Impl.m_view) + return; + m_Impl.m_view->preloadSlide(slide); +} + +void Q3DSViewerApp::unloadSlide(const QString &slide) +{ + if (!m_Impl.m_view) + return; + m_Impl.m_view->unloadSlide(slide); +} + +void Q3DSViewerApp::Resize(int width, int height) +{ + QSize oldSize = m_Impl.m_WindowSystem->GetWindowDimensions(); + QSize newSize(width, height); + m_Impl.m_WindowSystem->SetWindowDimensions(newSize); + + if (m_Impl.m_appInitSuccessful && m_Impl.m_view + && m_Impl.m_view->GetTegraRenderEngine()) { + QResizeEvent event = QResizeEvent(newSize, oldSize); + m_Impl.m_view->HandleMessage(&event); + } +} + +void Q3DSViewerApp::HandleKeyInput(Q3DStudio::EKeyCode inKeyCode, bool isPressed) +{ + if (!m_Impl.m_view || inKeyCode == Q3DStudio::KEY_NOKEY) + return; + + CInputEngine *input = m_Impl.m_view->GetInputEngine(); + if (input) + input->HandleKeyboard(inKeyCode, isPressed); +} + +void Q3DSViewerApp::HandleMouseMove(int x, int y, bool isPressed) +{ + if (!m_Impl.m_view) + return; + + CInputEngine *input = m_Impl.m_view->GetInputEngine(); + if (input) { + input->BeginPickInput(); + input->EndPickInput(); + input->SetPickInput(static_cast<Q3DStudio::FLOAT>(x), static_cast<Q3DStudio::FLOAT>(y), + isPressed); + } + +} + +void Q3DSViewerApp::HandleMousePress(int x, int y, int mouseButton, bool isPressed) +{ + if (!m_Impl.m_view) + return; + + bool hasButton + = std::find(m_Impl.m_mouseButtons.begin(), m_Impl.m_mouseButtons.end(), mouseButton) + != m_Impl.m_mouseButtons.end(); + + if ((mouseButton == 1 || mouseButton == 2)) { + // We keep track of the mouse presses because there are situations where the application + // will not give us a mouse + // up for each mouse down. This ameleorates the effects of those slightly misbehaving + // applications. + if (isPressed == hasButton) { + bool localIsPressed = !isPressed; + // Slightly recursive call in order to handle situations where we aren't getting + // the mouse up with the corresponding mouse down or vice versa. + HandleMousePress(x, y, mouseButton, localIsPressed); + HandleMousePress(x, y, mouseButton, isPressed); + } else { + if (isPressed) { + m_Impl.m_mouseButtons.push_back(mouseButton); + qCInfo(qt3ds::TRACE_INFO) + << "ViewerApp: Mouse down of frame " + << m_Impl.m_view->GetFrameCount(); + } else { + m_Impl.m_mouseButtons.erase(std::remove(m_Impl.m_mouseButtons.begin(), + m_Impl.m_mouseButtons.end(), mouseButton), + m_Impl.m_mouseButtons.end()); + } + + CInputEngine *input = m_Impl.m_view->GetInputEngine(); + + if (input) { + input->BeginPickInput(); + input->SetPickInput(static_cast<Q3DStudio::FLOAT>(x), + static_cast<Q3DStudio::FLOAT>(y), isPressed); + input->EndPickInput(); + + m_Impl.queueMouseEvent(mouseButton, isPressed ? 1 : 0, x, y); + } + } + } +} + +void Q3DSViewerApp::HandleMouseWheel(int x, int y, int orientation, int numSteps) +{ + if (!m_Impl.m_view) + return; + + CInputEngine *input = m_Impl.m_view->GetInputEngine(); + if (input) { + input->SetPickInput(static_cast<Q3DStudio::FLOAT>(x), static_cast<Q3DStudio::FLOAT>(y), 0); + input->SetScrollValue(orientation == 0 ? VSCROLLWHEEL : HSCROLLWHEEL, numSteps); + } +} + +void Q3DSViewerApp::GoToSlideByName(const char *elementPath, const char *slideName) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->GoToSlideByName(elementPath, slideName); +} + +void Q3DSViewerApp::GoToSlideByIndex(const char *elementPath, const int slideIndex) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->GoToSlideByIndex(elementPath, slideIndex); +} + +void Q3DSViewerApp::GoToSlideRelative(const char *elementPath, const bool next, const bool wrap) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->GoToSlideRelative(elementPath, next, wrap); +} + +bool Q3DSViewerApp::GetSlideInfo(const char *elementPath, int ¤tIndex, int &previousIndex, + QString ¤tName, QString &previousName) +{ + if (!m_Impl.m_view) + return false; + + return m_Impl.m_view->GetSlideInfo(elementPath, currentIndex, previousIndex, + currentName, previousName); +} + +void Q3DSViewerApp::SetPresentationActive(const char *presId, const bool active) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->SetPresentationAttribute(presId, nullptr, active ? "True" : "False"); +} + +void Q3DSViewerApp::GoToTime(const char *elementPath, const float time) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->GoToTime(elementPath, time); +} + +void Q3DSViewerApp::PlaySoundFile(const char *soundPath) +{ + if (!m_Impl.m_AudioPlayer || !soundPath) + return; + + m_Impl.m_AudioPlayer->PlaySoundFile(soundPath); +} + +void Q3DSViewerApp::SetAttribute(const char *elementPath, const char *attributeName, + const char *value) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->SetAttribute(elementPath, attributeName, value); +} + +bool Q3DSViewerApp::GetAttribute(const char *elementPath, const char *attributeName, void *value) +{ + if (!m_Impl.m_view) + return false; + + return m_Impl.m_view->GetAttribute(elementPath, attributeName, value); +} + +void Q3DSViewerApp::FireEvent(const char *elementPath, const char *evtName) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->FireEvent(elementPath, evtName); +} + +bool Q3DSViewerApp::PeekCustomAction(std::string &outElementPath, std::string &outActionName) +{ + if (!m_Impl.m_view) + return false; + + char *theElementPath = nullptr; + char *theActioName = nullptr; + bool retVal = m_Impl.m_view->PeekCustomAction(theElementPath, theActioName); + + if (theElementPath) + outElementPath = theElementPath; + if (theActioName) + outActionName = theActioName; + + return retVal; +} + +bool Q3DSViewerApp::RegisterScriptCallback(ViewerCallbackType::Enum inCallbackType, + const qml_Function inCallback, void *inUserData) +{ + if (!m_Impl.m_view) + return false; + + // convert to int + int callbackType = 0; + if (inCallbackType == Q3DSViewer::ViewerCallbackType::CALLBACK_ON_INIT) + callbackType = 1; + else if (inCallbackType == Q3DSViewer::ViewerCallbackType::CALLBACK_ON_UPDATE) + callbackType = 2; + else + return false; + + bool retVal = m_Impl.m_view->RegisterScriptCallback(callbackType, inCallback, inUserData); + + return retVal; +} + +void Q3DSViewerApp::SetScaleMode(ViewerScaleModes::Enum inScale) +{ + if (m_Impl.m_view && m_Impl.m_view->GetTegraRenderEngine()) { + m_Impl.m_view->GetTegraRenderEngine()->SetScaleMode( + static_cast<TegraRenderScaleModes::Enum>(inScale)); + } +} + +ViewerScaleModes::Enum Q3DSViewerApp::GetScaleMode() +{ + if (m_Impl.m_view && m_Impl.m_view->GetTegraRenderEngine()) { + return static_cast<ViewerScaleModes::Enum>( + m_Impl.m_view->GetTegraRenderEngine()->GetScaleMode()); + } + + return ViewerScaleModes::ExactSize; +} + +void Q3DSViewerApp::setMatteColor(const QColor &color) +{ + if (m_Impl.m_view && m_Impl.m_view->GetTegraRenderEngine()) { + m_Impl.m_view->GetTegraRenderEngine()->SetMatteColor( + qt3ds::QT3DSVec4(color.redF(), color.greenF(), + color.blueF(), color.alphaF())); + } +} + +void Q3DSViewerApp::setShowOnScreenStats(bool inShow) +{ + if (m_Impl.m_view && m_Impl.m_view->GetTegraRenderEngine()) + m_Impl.m_view->showOnScreenStats(inShow); +} + +void Q3DSViewerApp::SetShadeMode(ViewerShadeModes::Enum inShadeMode) +{ + if (m_Impl.m_view && m_Impl.m_view->GetTegraRenderEngine()) { + StaticAssert<ViewerShadeModes::Shaded == TegraRenderShadeModes::Shaded>::valid_expression(); + StaticAssert<ViewerShadeModes::ShadedWireframe + == TegraRenderShadeModes::ShadedWireframe>::valid_expression(); + StaticAssert<ViewerShadeModes::Wireframe + == TegraRenderShadeModes::Wireframe>::valid_expression(); + + m_Impl.m_view->GetTegraRenderEngine()->SetShadeMode( + static_cast<TegraRenderShadeModes::Enum>(inShadeMode)); + } +} + +void Q3DSViewerApp::SetGlobalAnimationTime(qint64 inMilliSecs) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->SetGlobalAnimationTime(inMilliSecs); +} + +void Q3DSViewerApp::SetDataInputValue( + const QString &name, const QVariant &value, qt3ds::runtime::DataInputValueRole valueRole) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->SetDataInputValue(name, value, int(valueRole)); +} + +void Q3DSViewerApp::setPresentationId(const QString &id) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->setPresentationId(id); +} + +QList<QString> Q3DSViewerApp::dataInputs() const +{ + if (!m_Impl.m_view) + return {}; + + return m_Impl.m_view->dataInputs(); +} + +QList<QString> Q3DSViewerApp::dataOutputs() const +{ + if (!m_Impl.m_view) + return {}; + + return m_Impl.m_view->dataOutputs(); +} + +float Q3DSViewerApp::dataInputMax(const QString &name) const +{ + if (!m_Impl.m_view) + return 0.0f; + + return m_Impl.m_view->dataInputMax(name); +} + +float Q3DSViewerApp::dataInputMin(const QString &name) const +{ + if (!m_Impl.m_view) + return 0.0f; + + return m_Impl.m_view->dataInputMin(name); +} + +QHash<QString, QString> Q3DSViewerApp::dataInputMetadata(const QString &name) const +{ + if (!m_Impl.m_view) + return {}; + + return m_Impl.m_view->dataInputMetadata(name); +} + +void Q3DSViewerApp::createElements(const QString &parentElementPath, const QString &slideName, + const QVector<QHash<QString, QVariant>> &properties) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->createElements(parentElementPath, slideName, properties); +} + +void Q3DSViewerApp::deleteElements(const QStringList &elementPaths) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->deleteElements(elementPaths); +} + +void Q3DSViewerApp::createMaterials(const QString &subPresId, + const QStringList &materialDefinitions) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->createMaterials(subPresId, materialDefinitions); +} + +void Q3DSViewerApp::deleteMaterials(const QStringList &materialNames) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->deleteMaterials(materialNames); +} + +void Q3DSViewerApp::createMeshes(const QHash<QString, Q3DSViewer::MeshData> &meshData) +{ + if (!m_Impl.m_view) + return; + + QString error; + QHashIterator<QString, Q3DSViewer::MeshData> dataIter(meshData); + while (dataIter.hasNext()) { + dataIter.next(); + // Mesh creation needs to be done one by one, as Mesh pointers are not valid + // after builder is reused + qt3dsimp::Mesh *mesh = m_Impl.buildMesh(dataIter.value(), error); + if (mesh) { + m_Impl.m_view->createMesh(dataIter.key(), mesh); + } else { + QString errorMsg = QObject::tr("Creating mesh '%1' failed: %2") + .arg(dataIter.key()).arg(error); + qWarning() << __FUNCTION__ << errorMsg; + SigMeshesCreated(meshData.keys(), error); + return; + } + } + m_Impl.m_meshBuilder->Reset(); + SigMeshesCreated(meshData.keys(), error); +} + +void Q3DSViewerApp::deleteMeshes(const QStringList &meshNames) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->deleteMeshes(meshNames); +} + +Q3DSViewerApp &Q3DSViewerApp::Create(void *glContext, Q3DStudio::IAudioPlayer *inAudioPlayer, + QElapsedTimer *startupTimer) +{ + return *Q3DStudio_virtual_new(Q3DSViewerApp) Q3DSViewerApp(glContext, inAudioPlayer, + startupTimer); +} + +void Q3DSViewerApp::Release() +{ + Q3DStudio_virtual_delete(this, Q3DSViewerApp); +} + +} // end namespace |